Compare commits

..

No commits in common. "c0bba8b2648e115ce4a32a5d1a8105bed87fbb72" and "7dd46556c84258b25c174783925f57d1ef0fff7b" have entirely different histories.

5 changed files with 36 additions and 190 deletions

12
Line.h
View File

@ -12,8 +12,6 @@ private:
public: public:
Line(Point from, Point to) : m_from(from), m_to(to), m_to_from_origin(to - from) {} Line(Point from, Point to) : m_from(from), m_to(to), m_to_from_origin(to - from) {}
Line() = default;
Point from() const Point from() const
{ {
return m_from; return m_from;
@ -24,16 +22,6 @@ public:
return m_to; return m_to;
} }
void set_from(Point from)
{
m_from = from;
}
void set_to(Point to)
{
m_to = to;
}
// Return true if the given point is to the right of this line. // Return true if the given point is to the right of this line.
// False is also returned if the point is directly on the line. // False is also returned if the point is directly on the line.
bool is_point_right(Point other) const bool is_point_right(Point other) const

View File

@ -1,6 +1,5 @@
CXX = g++ CXX = g++
CXXFLAGS = -Wall -O3 -g CXXFLAGS = -Wall -O3 -g
SFMLFLAGS = -lsfml-system -lsfml-window -lsfml-graphics
# In case you installed SFML to a non-standard path, you'll need to tell the compiler where to find the SFML headers (.hpp files): # In case you installed SFML to a non-standard path, you'll need to tell the compiler where to find the SFML headers (.hpp files):
# g++ -c main.cpp -I<sfml-install-path>/include # g++ -c main.cpp -I<sfml-install-path>/include
@ -10,10 +9,10 @@ SFMLFLAGS = -lsfml-system -lsfml-window -lsfml-graphics
quickhull: main.o Quickhull.h Point.h Line.h Triangle.h Timing.o Display.o quickhull: main.o Quickhull.h Point.h Line.h Triangle.h Timing.o Display.o
# link with sfml libs; -lsfml-network -lsfml-audio currently not needed # link with sfml libs; -lsfml-network -lsfml-audio currently not needed
$(CXX) $(CXXFLAGS) -o quickhull main.o Quickhull.h Point.h Line.h Triangle.h Timing.o Display.o $(SFMLFLAGS) $(CXX) $(CXXFLAGS) -o quickhull main.o Quickhull.h Point.h Line.h Triangle.h Timing.o Display.o -lsfml-system -lsfml-window -lsfml-graphics
performance: performance.cpp Display.o performance: performance.cpp
$(CXX) $(CXXFLAGS) -o performance Display.o performance.cpp $(SFMLFLAGS) $(CXX) $(CXXFLAGS) -o performance performance.cpp
main.o: main.cpp Timing.h main.o: main.cpp Timing.h
$(CXX) $(CXXFLAGS) -c main.cpp $(CXX) $(CXXFLAGS) -c main.cpp

View File

@ -1,72 +0,0 @@
#include "Point.h"
#include <list>
#include <cstdlib> // for srand, rand
class NumberGenerator
{
private:
static const int OFFSET = 0;
static const int WIDTH = 700;
static const int HEIGHT = 700;
public:
static std::list<Point> get_random_numbers(int valCount)
{
std::list<Point> points;
//srand(static_cast <unsigned> (time(0)));
srand(static_cast <unsigned> (0)); // fixed seed for testing
float x = 0;
float y = 0;
for (int i = 0; i < valCount; ++i)
{
x = OFFSET + static_cast <float> (rand()) / (static_cast <float> (RAND_MAX / (WIDTH - 2*OFFSET)));
y = OFFSET + static_cast <float> (rand()) / (static_cast <float> (RAND_MAX / (HEIGHT - 2*OFFSET)));
points.push_back(Point(x, y));
}
return points;
}
static std::list<Point> get_rectangle_numbers(int valCount)
{
std::list<Point> points;
float diff = (2.f * HEIGHT / valCount);
float x = 0;
float y = 0;
for (int i = 0; i < valCount; ++i)
{
x = (i%2 == 0) ? OFFSET : WIDTH - OFFSET;
y = diff/2 + (i/2) * diff;
points.push_back(Point(x, y));
}
return points;
}
static std::list<Point> get_circle_numbers(int valCount)
{
std::list<Point> points;
float rad = HEIGHT / 2; //8.0f;
float deg = (float)(2 * 3.14159 / valCount);
float x = 0;
float y = 0;
for (int i = 0; i < valCount; ++i)
{
x = WIDTH/2 + rad * cosf(i * deg);
y = HEIGHT/2 + rad * sinf(i * deg);
points.push_back(Point(x, y));
}
return points;
}
};

View File

@ -10,7 +10,7 @@
class Quickhull class Quickhull
{ {
public: public:
static void get_hull(std::list<Point> input, std::list<Point> &output) static void get_hull(std::list<Point> &input, std::list<Point> &output)
{ {
// Get leftmost and rightmost point // Get leftmost and rightmost point
Point leftmost(INFINITY, 0.0), rightmost(-INFINITY, 0.0); Point leftmost(INFINITY, 0.0), rightmost(-INFINITY, 0.0);
@ -63,7 +63,7 @@ private:
// Find the point which is furthest away from the line, add it to the output // Find the point which is furthest away from the line, add it to the output
Point furthest_point; Point furthest_point;
float furthest_distance = -1.0; float furthest_distance = 0.0;
for (const Point &point : input) for (const Point &point : input)
{ {
@ -75,78 +75,43 @@ private:
} }
} }
// TODO: e.g. in the case of a rectangle, it's possible for there to be
// multiple closest points (sometimes all at distance 0). How do we handle
// these properly? We definitely need to remove them all from input later;
// do we also need to handle them all further? This hotfix works, but seems
// like an unnecessarily big performance hit for that edge case.
// FIXME: This workaround also causes problems with extremely large numbers
// of randomly generated numbers, causing random lines within the data!
// Points inside the hull are added because of random lines.
for (const Point &point : input)
{
float this_distance = line.distance_to(point);
// TODO: Both are required, otherwise only one side of the rectangle is
// taken -- why?
if (this_distance == furthest_distance || line.distance_to(point) == 0)
{
output.emplace_back(point);
}
}
input.remove_if([furthest_distance, line](Point point)
{
return furthest_distance == line.distance_to(point);
});
// Hotfix end
output.emplace_back(furthest_point); output.emplace_back(furthest_point);
input.remove(furthest_point); input.remove(furthest_point);
// Build a triangle with these 3 points // Build a triangle with these 3 points
// We need to differentiate based on which side the furthest point is on // The order with which we must pass the points depends on where the new furthest point is
// in order to keep the meaning of left/right consistent. // TODO: Is there a nicer way to do this?
Point a, b, c;
Line new_line1, new_line2;
if (line.is_point_right(furthest_point)) if (line.is_point_right(furthest_point))
{ {
// TOOD: It's probably more efficient to set the fields? a = line.from();
new_line1 = Line(line.to(), furthest_point); b = line.to();
new_line2 = Line(furthest_point, line.from()); c = furthest_point;
} }
else else
{ {
new_line1 = Line(line.from(), furthest_point); a = line.from();
new_line2 = Line(furthest_point, line.to()); b = furthest_point;
c = line.to();
} }
// TODO: Test if this improves performance Triangle triangle(a, b, c);
//Triangle triangle(a, b, c);
//input.remove_if([triangle](Point point)
//{
// return triangle.is_point_inside(point);
//});
// Get points right of new_line1 and 2 // Remove points inside this triangle
std::list<Point> left_of_line1, left_of_line2; // TODO: I think we can actually skip this, and instead only
// pass points to the left (?) of the individual line to the
for (const Point& point : input) // new get_hull_with_line call. That way the ones inside are
// implicitly ignored.
input.remove_if([triangle](Point point)
{ {
if (!new_line1.is_point_right(point)) return triangle.is_point_inside(point);
{ });
left_of_line1.emplace_back(point);
}
// TODO: Can we do else if here, or could we then miss out on points?
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 // Recursively call get_hull_with_line for each side of the triangle
// TODO: We can skip the original one // TODO: We can skip the original one
get_hull_with_line(left_of_line1, output, new_line1); get_hull_with_line(input, output, triangle.l1());
get_hull_with_line(left_of_line2, output, new_line2); get_hull_with_line(input, output, triangle.l2());
get_hull_with_line(input, output, triangle.l3());
} }
}; };

View File

@ -1,56 +1,22 @@
#include "Quickhull.h" #include "Quickhull.h"
#include "NumberGenerator.h"
#include "Display.h"
int main() int main()
{ {
std::list<Point> points = NumberGenerator::get_rectangle_numbers(100); std::list<Point> points = {
Point(-1, -1),
Point(-1, 1),
Point(1, -1),
Point(1, 1),
Point(0.5, 0) // Should not be in the hull
};
std::list<Point> hull; std::list<Point> hull;
Quickhull::get_hull(points, hull); Quickhull::get_hull(points, hull);
// create the window
sf::RenderWindow window(sf::VideoMode(800, 800), "k-d-tree");
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() + 50, point.y() + 50);
window.draw(normal_p);
}
// Draw hull points
for (const Point &point : hull) for (const Point &point : hull)
{ {
hull_p.setPosition(point.x() + 50, point.y() + 50); std::cout << point.x() << ", " << point.y() << std::endl;
window.draw(hull_p);
} }
// end the current frame
window.display();
}
return 0; return 0;
} }