Compare commits

...

2 Commits

7 changed files with 232 additions and 17 deletions

View File

@ -1,16 +1,67 @@
// make sure sfml is installed:
// linux - sudo apt-get install libsfml-dev
// windows - manual dl from https://www.sfml-dev.org/download.php
#include <SFML/Graphics.hpp>
#include <iostream>
#include <chrono>
#include <thread>
#include "Display.h"
#include "Point.h"
#include "Utility.h"
#define RADIUS 10.f
Display::Display()
{
if (!m_font.loadFromFile("arial.ttf"))
{
std::cerr << "font arial.ttf could not be loaded!" << std::endl;
}
m_textStatus.setPosition(OFFSET, HEIGHT - 2*OFFSET);
m_textStatus.setFont(m_font);
m_textStatus.setString("initializing...");
m_textStatus.setCharacterSize(12);
m_textStatus.setFillColor(sf::Color::Black);
}
void Display::drawPoint(sf::RenderWindow& window, size_t index)
{
Point &pt = m_points[index];
sf::CircleShape shape(RADIUS);
//shape.setOrigin(RADIUS, RADIUS); // handle with origin or manually calc position
shape.setPosition(pt.x() - RADIUS, pt.y() - RADIUS);
shape.setFillColor(sf::Color::Green);
shape.setOutlineThickness(1.f);
shape.setOutlineColor(sf::Color::Black);
window.draw(shape);
sf::Text label;
label.setPosition(pt.x() - RADIUS/2, pt.y() - RADIUS/2 - 3);
label.setFont(m_font);
label.setString(std::to_string(index));
label.setCharacterSize(12);
label.setFillColor(sf::Color::Black);
window.draw(label);
}
// multiple options possible:
// a) draw every frame, using elapsed time for updates
// --> not really needed because we have no animations... also uses a lot of performance
// b) checking every frame if elapsed time since last tick is larger than a specified offset, then draw the field
// --> mostly doing nothing but still uses a lot of performance O_o
// c) just start a simple (c++11 <3) non-busy sleep
// --> may delay user input / events but don't care atm :p
void Display::show()
{
sf::RenderWindow window(sf::VideoMode(WIDTH, HEIGHT), "SFML works!");
sf::ContextSettings settings;
settings.antialiasingLevel = 4;
sf::RenderWindow window(sf::VideoMode(WIDTH, HEIGHT), "ALGO Prog2: Quickhull - visualization", sf::Style::Default, settings);
// b)
//window.setFramerateLimit(0.2);
//sf::Clock clock;
//sf::Time timeSinceLastUpdate = sf::Time::Zero;
//sf::Time frameTime = sf::seconds(2); //sf::seconds(1.f / 60.f)
while (window.isOpen())
{
sf::Event event;
@ -20,18 +71,95 @@ void Display::show()
window.close();
}
window.clear();
for (auto &pt : m_points)
std::cout << "entering gameloop" << std::endl;
/* b)
timeSinceLastUpdate += clock.restart();
// only handle every few seconds because we need no animations
if (timeSinceLastUpdate > frameTime)
{
sf::CircleShape shape(10.f);
shape.setPosition(pt.x(), pt.y());
shape.setFillColor(sf::Color::Green);
window.draw(shape);
// start by getting the most left and right point
timeSinceLastUpdate -= frameTime;
}*/
// a)
//update(elapsed);
update();
window.clear(sf::Color::White);
// TODO: refactor -> parse into sf::Vertices or Shapes
// always print remaining points
size_t points = m_points.size();
for (size_t i = 0; i < points; ++i)
{
drawPoint(window, i);
}
// draw already calculated hull
//if (step >= 1)
{
//window.draw(&m_hull[0], m_hull.size(), sf::Lines);
window.draw(&m_hull[0], m_hull.getVertexCount(), m_hull.getPrimitiveType());
}
window.draw(m_textStatus);
window.display();
// c)
// choose a simple sleep
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
}
}
void Display::update()
{
// TODO: maybe include AklToussaint heuristic first?
// https://en.wikipedia.org/wiki/Convex_hull_algorithms#Akl%E2%80%93Toussaint_heuristic
if (m_curStep == 1)
{
// first step: select min - max x coordinates
m_textStatus.setString("step " + std::to_string(m_curStep) + ": select min - max x coordinates...");
std::pair<Point, Point> minmax = getMinMaxX(m_points);
std::cout << "min: " << minmax.first.x() << ", " << minmax.first.y() <<
", max: " << minmax.second.x() << ", " << minmax.second.y() << std::endl;
m_hull.setPrimitiveType(sf::Lines);
m_hull.append(sf::Vertex(sf::Vector2f(minmax.first.x(), minmax.first.y()), sf::Color::Blue));
m_hull.append(sf::Vertex(sf::Vector2f(minmax.second.x(), minmax.second.y()), sf::Color::Blue));
}
else if (m_curStep == 2)
{
// second step: split board and find furthest point
m_textStatus.setString("step " + std::to_string(m_curStep) + ": split board and find furthest point...");
}
else if (m_curStep == 3)
{
// third step: draw triangle, remove inner points
m_textStatus.setString("step " + std::to_string(m_curStep) + ": draw triangle, remove inner points...");
}
else if (m_curStep == 4)
{
// fourth step: remove inner points
m_textStatus.setString("step " + std::to_string(m_curStep) + ": remove inner points...");
}
else if (m_curStep == 5)
{
// fourth step: remove inner points
m_textStatus.setString("step " + std::to_string(m_curStep) + ": finished calculating convex hull");
}
m_curStep++;
// if finished removing inner points and there are still points left -> repeat from step 2
// TODO:
if (m_curStep == 5 && m_points.size() > 0)
{
m_curStep = 2;
}
}
void Display::setData(std::vector<Point> pts)
{
m_points = pts;

View File

@ -1,17 +1,37 @@
#pragma once
#ifndef DISPLAY_H
// make sure sfml is installed:
// linux - sudo apt-get install libsfml-dev
// windows - manual dl from https://www.sfml-dev.org/download.php
#include <SFML/Graphics.hpp>
class Point;
#define OFFSET 10
#define WIDTH 800
#define HEIGHT 600
class Display
{
private:
sf::Font m_font;
sf::Text m_textStatus;
std::vector<Point> m_points;
//std::vector<sf::Vertex> m_hull;
sf::VertexArray m_hull;
void drawPoint(sf::RenderWindow &, size_t);
int m_curStep = 0;
void update();
public:
Display();
void show();
void setData (std::vector<Point>);
};
#endif // DISPLAY_H

View File

@ -8,12 +8,12 @@ private:
public:
Point(float x, float y) : m_x(x), m_y(y) {}
float x()
float x () const
{
return m_x;
}
float y()
float y () const
{
return m_y;
}

View File

@ -1,2 +1,10 @@
# quickhull
## TODO:
- Point class
- adding "features" such as "IsInTriangle", ... ?
- direct member access instead of getters?

53
Utility.h Normal file
View File

@ -0,0 +1,53 @@
#pragma once
#ifndef UTILITY_H
static float sign (Point &p1, Point &p2, Point &p3)
{
return (p1.x() - p3.x()) * (p2.y() - p3.y()) - (p2.x() - p3.x()) * (p1.y() - p3.y());
}
static bool IsPointInTriangle(Point &pt, Point &p1, Point &p2, Point &p3)
{
float d1 = sign(pt, p1, p2);
float d2 = sign(pt, p2, p3);
float d3 = sign(pt, p3, p1);
bool has_neg = (d1 < 0) || (d2 < 0) || (d3 < 0);
bool has_pos = (d1 > 0) || (d2 > 0) || (d3 > 0);
return !(has_neg && has_pos);
}
static bool SortForMinXMaxY (const Point& a, const Point& b)
{
if (a.x() != b.x())
{
return (a.x() < b.x());
}
return (a.y() > b.y());
}
static bool SortForMinYMaxX(const Point& a, const Point& b)
{
if (a.y() != b.y())
{
return (a.x() < b.x());
}
return (a.y() > b.y());
}
static void sortPoints (std::vector<Point>& pts)
{
std::sort(pts.begin(), pts.end(), SortForMinXMaxY);
}
// TODO: what happens if all/more points are on hor/vert line? -> sort for x, than y should handle this
static std::pair<Point, Point> getMinMaxX(std::vector<Point>& pts)
{
// TODO: check if already sorted? assume array is sorted? call sort utility function???
//sortPoints(pts);
std::sort(pts.begin(), pts.end(), SortForMinXMaxY);
return std::make_pair(pts[0], pts[pts.size() - 1]);
}
#endif // UTILITY_H

BIN
arial.ttf Normal file

Binary file not shown.

View File

@ -8,6 +8,7 @@
#include "Display.h"
#include "Point.h" // TODO: check if there is a usable SFML or c++ class
#include "Timing.h"
#include "Utility.h"
// TODOs:
// - use SFML vec2 instead of Point class
@ -43,8 +44,8 @@ int main (int argc, char **argv)
srand(static_cast <unsigned> (0)); // fixed seed for testing
for (int i = 0; i < valCount; ++i)
{
float x = static_cast <float> (rand()) / (static_cast <float> (RAND_MAX / WIDTH));
float y = static_cast <float> (rand()) / (static_cast <float> (RAND_MAX / HEIGHT));
float x = OFFSET + static_cast <float> (rand()) / (static_cast <float> (RAND_MAX / (WIDTH - OFFSET)));
float y = OFFSET + static_cast <float> (rand()) / (static_cast <float> (RAND_MAX / (HEIGHT - OFFSET)));
points.push_back(Point(x, y));
}
}
@ -76,7 +77,8 @@ int main (int argc, char **argv)
}
}
// TODO: need sort here?
// TODO: sort here, once and for all? xD
sortPoints(points);
for (Point& pt : points)
{
std::cout << "pt: " << pt.x() << ", " << pt.y() << std::endl;
@ -85,6 +87,10 @@ int main (int argc, char **argv)
if (vis)
{
// TODO: use data as ctor argument? pointer?
// TEST to check SFML coordinate system
points.push_back(Point(0, 0));
Display display;
display.setData(points);
display.show();