1+ #include < algorithm>
2+ #include < iostream>
3+ #include < optional>
4+
5+ template <typename T>
6+ struct Point {
7+ T x;
8+ T y;
9+ };
10+
11+ /* *
12+ * @brief Get the Cross Product of segment (a, b) and (a, c)
13+ *
14+ * Example:
15+ * W(3, 4)
16+ * V(5, 2)
17+ * (0,0)
18+ *
19+ * V x W = VxWy - WxVy
20+ * = | 5 3 |
21+ * | 2 4 |
22+ * = (5 * 4) - (2 * 3) = 14 (counter-clockwise)
23+ *
24+ * @tparam T
25+ * @param a
26+ * @param b
27+ * @param c
28+ * @return T positive for counter-clockwise, negative for clockwise, zero for
29+ * collinear
30+ */
31+ template <typename T>
32+ T GetCrossProduct (const Point<T> &a, const Point<T> &b, const Point<T> &c) {
33+ return ((b.x - a.x ) * (c.y - a.y ) - (b.y - a.y ) * (c.x - a.x ));
34+ }
35+
36+ template <typename T>
37+ Point<T> SwapAndGetFirstPoint (std::vector<Point<T>> &points) {
38+ auto iterator = std::min_element (
39+ points.begin (), points.end (), [](const Point<T> a, const Point<T> b) {
40+ return ((a.y < b.y ) || ((a.y == b.y ) && (a.x < b.x )));
41+ });
42+ auto old_first_point = points[0 ];
43+ points[0 ] = *iterator;
44+ *iterator = old_first_point;
45+ return points[0 ];
46+ }
47+
48+ template <typename T>
49+ std::vector<Point<T>> GetConvexHull (std::vector<Point<T>> &points) {
50+ if (points.size () < 3 ) {
51+ return std::vector<Point<T>>{};
52+ }
53+ auto first_point = SwapAndGetFirstPoint (points);
54+ std::sort (points.begin () + 1 , points.end (),
55+ [&](const Point<T> &b, const Point<T> &c) {
56+ return (GetCrossProduct (first_point, b, c) < 0 );
57+ });
58+
59+ std::vector<Point<T>> result;
60+ auto it = points.begin ();
61+
62+ auto value = *it++;
63+ std::cout << " Adding... " << value.x << " " << value.y << std::endl;
64+
65+ result.push_back (value);
66+
67+ value = *it++;
68+ std::cout << " Adding... " << value.x << " " << value.y << std::endl;
69+
70+ result.push_back (value);
71+
72+ value = *it++;
73+ std::cout << " Adding... " << value.x << " " << value.y << std::endl;
74+
75+ result.push_back (value);
76+
77+ while (it != points.end ()) {
78+ // StepConverge
79+ // Pop off any points that make a convex angle with *it
80+ while (GetCrossProduct (*(result.rbegin () + 1 ), *(result.rbegin ()), *it) >=
81+ 0 ) {
82+ std::cout << " Popping out: " << result.back ().x << " " << result.back ().y
83+ << std::endl;
84+ result.pop_back ();
85+ }
86+
87+ auto value = *it++;
88+ std::cout << " Adding... " << value.x << " " << value.y << std::endl;
89+ result.push_back (value);
90+ }
91+
92+ return result;
93+ }
94+
95+ template <typename T>
96+ class ConvexHull {
97+ public:
98+ using Callback = std::function<void (const Point<T> &)>;
99+ explicit ConvexHull (const std::vector<Point<T>> &points)
100+ : points_(points), current_index_(0 ) {
101+ auto first_point = SwapAndGetFirstPoint (points_);
102+ std::sort (points_.begin () + 1 , points_.end (),
103+ [&](const Point<T> &b, const Point<T> &c) {
104+ return (GetCrossProduct (first_point, b, c) < 0 );
105+ });
106+ initialize_first_three_points_ = true ;
107+ }
108+
109+ std::optional<std::vector<Point<T>>> StepForward (Callback insert_callback,
110+ Callback pop_callback) {
111+ if (points_.size () < 3 ) {
112+ return std::nullopt ;
113+ } else {
114+ if (initialize_first_three_points_) {
115+ for (auto i = 0 ; i < 3 ; ++i) {
116+ InsertCurrentValueWithCallback (insert_callback);
117+ }
118+ initialize_first_three_points_ = false ;
119+ return std::nullopt ;
120+ }
121+ }
122+
123+ if (current_index_ < points_.size ()) {
124+ if (GetCrossProduct (*(results_.rbegin () + 1 ), *(results_.rbegin ()),
125+ points_.at (current_index_)) >= 0 ) {
126+ PopCurrentValueWithCallback (pop_callback);
127+ } else {
128+ InsertCurrentValueWithCallback (insert_callback);
129+ }
130+ } else {
131+ return results_;
132+ }
133+
134+ return std::nullopt ;
135+ }
136+
137+ private:
138+ void InsertCurrentValueWithCallback (Callback insert_callback) {
139+ const auto value = points_.at (current_index_);
140+ insert_callback (value);
141+ results_.push_back (value);
142+ current_index_++;
143+ }
144+
145+ void PopCurrentValueWithCallback (Callback pop_callback) {
146+ pop_callback (results_.back ());
147+ results_.pop_back ();
148+ }
149+
150+ std::vector<Point<T>> points_;
151+ std::vector<Point<T>> results_;
152+ size_t current_index_;
153+ bool initialize_first_three_points_;
154+ };
155+
156+ int main () {
157+ auto points = std::vector<Point<int >>{{0 , 3 }, {1 , 1 }, {2 , 2 }, {4 , 4 },
158+ {0 , 0 }, {1 , 2 }, {3 , 1 }, {3 , 3 }};
159+ auto result = GetConvexHull (points);
160+ std::cout << " Results" << std::endl;
161+ for (const auto value : result) {
162+ std::cout << value.x << " " << value.y << std::endl;
163+ }
164+
165+ std::cout << " -------------------" << std::endl;
166+ auto convex_hull = ConvexHull (points);
167+ while (convex_hull.StepForward (
168+ [](const Point<int > &point) {
169+ std::cout << " Adding... " << point.x << " " << point.y
170+ << std::endl;
171+ },
172+ [](const Point<int > &point) {
173+ std::cout << " Popping out: " << point.x << " " << point.y
174+ << std::endl;
175+ }) == std::nullopt ) {
176+ }
177+ auto new_result = convex_hull.StepForward ([](const Point<int > &) {},
178+ [](const Point<int > &) {});
179+ std::cout << " Results" << std::endl;
180+ for (const auto value : *new_result) {
181+ std::cout << value.x << " " << value.y << std::endl;
182+ }
183+ }
0 commit comments