#pragma once #include #include // For INT_MIN & INT_MAX #include "Point.h" #include "Line.h" #include "Triangle.h" class Quickhull { public: static void get_hull(std::list &input, std::list &output) { // Get leftmost and rightmost point Point leftmost(INFINITY, 0.0), rightmost(-INFINITY, 0.0); for (const Point &point : input) { if (point.x() < leftmost.x()) { leftmost = point; } else if (point.y() > rightmost.y()) { rightmost = point; } } // Add them to the output output.emplace_back(leftmost); output.emplace_back(rightmost); // Remove them from the input (as well as duplicates) input.remove(leftmost); input.remove(rightmost); // Create a line from leftmost to rightmost Line line = Line(leftmost, rightmost); // Sort points between left and right of that line std::list points_left, points_right; for (const Point &point : input) { if (line.is_point_right(point)) { points_right.emplace_back(point); } else { points_left.emplace_back(point); } } // Call get_hull_with_line with the left points, as well as with the right points, and the line get_hull_with_line(points_left, output, line); get_hull_with_line(points_right, output, line); } private: static void get_hull_with_line(std::list &input, std::list &output, const Line &line) { // If the input vector is empty, we're done if (input.empty()) return; // Find the point which is furthest away from the line, add it to the output Point furthest_point; float furthest_distance = 0.0; for (const Point &point : input) { float this_distance = line.distance_to(point); if (this_distance > furthest_distance) { furthest_distance = this_distance; furthest_point = point; } } output.emplace_back(furthest_point); input.remove(furthest_point); // Build a triangle with these 3 points // The order with which we must pass the points depends on where the new furthest point is // TODO: Is there a nicer way to do this? Point a, b, c; if (line.is_point_right(furthest_point)) { a = line.from(); b = line.to(); c = furthest_point; } else { a = line.from(); b = furthest_point; c = line.to(); } Triangle triangle(a, b, c); // Remove points inside this triangle // TODO: I think we can actually skip this, and instead only // pass points to the left (?) of the individual line to the // new get_hull_with_line call. That way the ones inside are // implicitly ignored. input.remove_if([triangle](Point point) { return triangle.is_point_inside(point); }); // Recursively call get_hull_with_line for each side of the triangle // TODO: We can skip the original one get_hull_with_line(input, output, triangle.l1()); get_hull_with_line(input, output, triangle.l2()); get_hull_with_line(input, output, triangle.l3()); } };