quickhull/Quickhull.h

193 lines
6.2 KiB
C++

#ifndef QUICKHULL_H
#define QUICKHULL_H
#include <list>
#include <limits.h>
#include <chrono>
#include <iostream>
#include "Display.h"
#include "Triangle.h"
class Quickhull
{
public:
static void get_hull(std::list<Point> &input, std::list<Point> &output, bool akl)
{
auto start = std::chrono::high_resolution_clock::now();
// 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<Point> 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);
auto diff = std::chrono::duration<double, std::milli>(std::chrono::high_resolution_clock::now() - start);
std::cout << "-- inner diff: " << diff.count() << "ms" << std::endl;
}
static void show (std::list<Point> &points, std::list<Point>& hull)
{
// showing points in window after calculation
// create the window
sf::RenderWindow window(sf::VideoMode(WIDTH, HEIGHT), "ALGO Prog2: Quickhull - performance");
sf::CircleShape normal_p(2);
normal_p.setFillColor(sf::Color(250, 250, 250));
sf::CircleShape hull_p(2);
hull_p.setFillColor(sf::Color(250, 100, 50));
// run the program as long as the window is open
while (window.isOpen())
{
// check all the window's events that were triggered since the last iteration of the loop
sf::Event event;
while (window.pollEvent(event))
{
// "close requested" event: we close the window
if (event.type == sf::Event::Closed)
window.close();
}
// clear the window with black color
window.clear(sf::Color::Black);
// Draw all points
for (const Point& point : points)
{
normal_p.setPosition(point.x(), point.y());
window.draw(normal_p);
}
// Draw hull points
for (const Point& point : hull)
{
hull_p.setPosition(point.x(), point.y());
window.draw(hull_p);
}
// end the current frame
window.display();
}
}
private:
static void get_hull_with_line(std::list<Point> &input, std::list<Point> &output, const Line &line)
{
// If the input vector is empty, we're done
if (input.empty()) return;
// Find the points which are furthest away from the line
float furthest_distance = -1.0;
std::list<Point> furthest_points;
for (const Point &point : input)
{
float this_distance = line.distance_squared_to(point);
// It's possible for there to be multiple closests points (e.g. in the case)
// of a rectangle). We need to handle all these properly, so we make a list
// of all points at the furthest distance.
if (this_distance > furthest_distance)
{
furthest_distance = this_distance;
furthest_points.clear();
furthest_points.emplace_back(point);
}
else if (this_distance == furthest_distance)
{
furthest_points.emplace_back(point);
}
}
for (const Point &point : furthest_points)
{
// All furthest points we found are part of the hull, so add them to the output
// and remove them from the input.
output.emplace_back(point);
input.remove(point);
}
Point furthest_point = furthest_points.front();
// Build a triangle with these 3 points
// We need to differentiate based on which side the furthest point is on
// in order to keep the meaning of left/right consistent.
Line new_line1, new_line2;
if (line.is_point_right(furthest_point))
{
new_line1 = Line(line.to(), furthest_point);
new_line2 = Line(furthest_point, line.from());
}
else
{
new_line1 = Line(line.from(), furthest_point);
new_line2 = Line(furthest_point, line.to());
}
// We don't need to remove points inside the triangle created by those lines.
// That happens implicitly since they are not handled further due to the
// following step, which assigns each line its corresponding points to handle:
// Get points right of new_line1 and 2
std::list<Point> left_of_line1, left_of_line2;
for (const Point& point : input)
{
if (!new_line1.is_point_right(point))
{
left_of_line1.emplace_back(point);
}
// TODO: Are there any possible edge cases where this 'else if' won't work,
// and we need an 'if' instead?
else if (!new_line2.is_point_right(point))
{
left_of_line2.emplace_back(point);
}
}
// Recursively call get_hull_with_line for each side of the triangle
get_hull_with_line(left_of_line1, output, new_line1);
get_hull_with_line(left_of_line2, output, new_line2);
}
};
#endif // QUICKHULL_H