1+ #include < algorithm>
2+ #include < iostream>
3+ #include < sstream>
4+ #include < string>
5+ #include < vector>
6+
7+ struct Point
8+ {
9+ int x{};
10+ int y{};
11+
12+ Point () = default ;
13+ Point (int x_, int y_) : x(x_), y(y_)
14+ {
15+ }
16+
17+ bool operator <(const Point &other) const
18+ {
19+ if (x != other.x )
20+ {
21+ return x < other.x ;
22+ }
23+ return y < other.y ;
24+ }
25+ };
26+
27+ [[noreturn]] void usage ()
28+ {
29+ std::cerr
30+ << R"( Usage: please provide at least 3 x and y coordinates as separate lists (e.g. "100, 440, 210"))"
31+ << ' \n ' ;
32+ std::exit (1 );
33+ }
34+
35+ std::vector<int > parseList (const std::string &input)
36+ {
37+ std::vector<int > out;
38+ std::stringstream ss (input);
39+
40+ for (std::string token; std::getline (ss, token, ' ,' );)
41+ {
42+ auto start = token.find_first_not_of (" \t " );
43+ if (start == std::string::npos)
44+ {
45+ usage ();
46+ }
47+
48+ const auto end = token.find_last_not_of (" \t " );
49+ token = token.substr (start, end - start + 1 );
50+
51+ size_t pos = 0 ;
52+ int value = 0 ;
53+
54+ try
55+ {
56+ size_t pos = 0 ;
57+ const int value = std::stoi (token, &pos);
58+
59+ if (pos != token.size ())
60+ {
61+ usage ();
62+ }
63+
64+ out.push_back (value);
65+ }
66+ catch (...)
67+ {
68+ usage ();
69+ }
70+ }
71+
72+ return out;
73+ }
74+
75+ std::vector<Point> parseCoordinates (const std::string &xs,
76+ const std::string &ys)
77+ {
78+ auto x = parseList (xs);
79+ auto y = parseList (ys);
80+
81+ if (x.size () != y.size () || x.size () < 3 )
82+ {
83+ usage ();
84+ }
85+
86+ std::vector<Point> pts;
87+ pts.reserve (x.size ());
88+
89+ std::transform (x.begin (), x.end (), y.begin (), std::back_inserter (pts),
90+ [](int a, int b) { return Point{a, b}; });
91+
92+ return pts;
93+ }
94+
95+ long long cross (const Point &a, const Point &b, const Point &c)
96+ {
97+ return 1LL * (b.x - a.x ) * (c.y - a.y ) - 1LL * (b.y - a.y ) * (c.x - a.x );
98+ }
99+
100+ std::vector<Point> convexHull (std::vector<Point> pts)
101+ {
102+ if (pts.size () < 3 )
103+ {
104+ usage ();
105+ }
106+
107+ std::sort (pts.begin (), pts.end ());
108+
109+ std::vector<Point> lower, upper;
110+ lower.reserve (pts.size ());
111+ upper.reserve (pts.size ());
112+
113+ auto build_half = [&](auto begin, auto end, auto &hull) {
114+ for (auto it = begin; it != end; ++it)
115+ {
116+ const auto &p = *it;
117+
118+ while (hull.size () >= 2 &&
119+ cross (hull[hull.size () - 2 ], hull.back (), p) <= 0 )
120+ {
121+ hull.pop_back ();
122+ }
123+
124+ hull.push_back (p);
125+ }
126+ };
127+
128+ build_half (pts.begin (), pts.end (), lower);
129+ build_half (pts.rbegin (), pts.rend (), upper);
130+
131+ lower.pop_back ();
132+ upper.pop_back ();
133+
134+ lower.insert (lower.end (), std::make_move_iterator (upper.begin ()),
135+ std::make_move_iterator (upper.end ()));
136+
137+ return lower;
138+ }
139+
140+ int main (int argc, char *argv[])
141+ {
142+ if (argc != 3 )
143+ {
144+ usage ();
145+ }
146+
147+ const auto hull = convexHull (parseCoordinates (argv[1 ], argv[2 ]));
148+
149+ for (const auto &[x, y] : hull)
150+ {
151+ std::cout << ' (' << x << " , " << y << " )\n " ;
152+ }
153+ }
0 commit comments