Compare commits

...

3 Commits

4 changed files with 204 additions and 94 deletions

View File

@ -7,9 +7,7 @@
#include "Point.h" #include "Point.h"
#include "Utility.h" #include "Utility.h"
#define RADIUS 10.f Display::Display (const std::vector<Point> &pts)
Display::Display()
{ {
if (!m_font.loadFromFile("arial.ttf")) if (!m_font.loadFromFile("arial.ttf"))
{ {
@ -21,26 +19,32 @@ Display::Display()
m_textStatus.setString("initializing..."); m_textStatus.setString("initializing...");
m_textStatus.setCharacterSize(12); m_textStatus.setCharacterSize(12);
m_textStatus.setFillColor(sf::Color::Black); m_textStatus.setFillColor(sf::Color::Black);
}
void Display::drawPoint(sf::RenderWindow& window, size_t index) size_t points = pts.size();
{ for (size_t i = 0; i < points; ++i)
Point &pt = m_points[index]; {
sf::CircleShape shape(RADIUS); const Point& pt = pts[i];
//shape.setOrigin(RADIUS, RADIUS); // handle with origin or manually calc position sf::CircleShape shape(OFFSET);
shape.setPosition(pt.x() - RADIUS, pt.y() - RADIUS); //shape.setPosition(pt.x() - OFFSET, pt.y() - OFFSET); // handle with origin or manually calc position
shape.setFillColor(sf::Color::Green); shape.setOrigin(OFFSET, OFFSET);
shape.setOutlineThickness(1.f); shape.setPosition(pt.x(), pt.y());
shape.setOutlineColor(sf::Color::Black); shape.setFillColor(sf::Color::Green);
window.draw(shape); shape.setOutlineThickness(1.f);
shape.setOutlineColor(sf::Color::Black);
m_points.push_back(shape);
//m_points.append(shape);
sf::Text label; sf::Text label;
label.setPosition(pt.x() - RADIUS/2, pt.y() - RADIUS/2 - 3); //label.setPosition(pt.x() - OFFSET / 2, pt.y() - OFFSET / 2 - 3);
label.setFont(m_font); label.setOrigin(OFFSET / 2 - 1, OFFSET / 2 + 3);
label.setString(std::to_string(index)); label.setPosition(pt.x(), pt.y());
label.setCharacterSize(12); label.setFont(m_font);
label.setFillColor(sf::Color::Black); label.setString(std::to_string(i));
window.draw(label); label.setCharacterSize(12);
label.setFillColor(sf::Color::Black);
m_labels.push_back(label);
//m_points.append(label);
}
} }
// multiple options possible: // multiple options possible:
@ -53,15 +57,15 @@ void Display::drawPoint(sf::RenderWindow& window, size_t index)
void Display::show() void Display::show()
{ {
sf::ContextSettings settings; sf::ContextSettings settings;
settings.antialiasingLevel = 4; settings.antialiasingLevel = 8;
sf::RenderWindow window(sf::VideoMode(WIDTH, HEIGHT), "ALGO Prog2: Quickhull - visualization", sf::Style::Default, settings); sf::RenderWindow window(sf::VideoMode(WIDTH, HEIGHT), "ALGO Prog2: Quickhull - visualization", sf::Style::Default, settings);
// b) /* b)
//window.setFramerateLimit(0.2); window.setFramerateLimit(0.2);
//sf::Clock clock; sf::Clock clock;
//sf::Time timeSinceLastUpdate = sf::Time::Zero; sf::Time frameTime = sf::seconds(2); //sf::seconds(1.f / 60.f)
//sf::Time frameTime = sf::seconds(2); //sf::seconds(1.f / 60.f) sf::Time timeSinceLastUpdate = frameTime; //sf::Time::Zero;*/
while (window.isOpen()) while (window.isOpen())
{ {
sf::Event event; sf::Event event;
@ -71,96 +75,176 @@ void Display::show()
window.close(); window.close();
} }
std::cout << "entering gameloop" << std::endl; // a)
//update(elapsed);
//render(window);
/* b) /* b)
timeSinceLastUpdate += clock.restart(); timeSinceLastUpdate += clock.restart();
// only handle every few seconds because we need no animations // only handle every few seconds because we need no animations
if (timeSinceLastUpdate > frameTime) if (timeSinceLastUpdate >= frameTime)
{ {
std::cout << "entering update and render" << std::endl;
// start by getting the most left and right point // start by getting the most left and right point
timeSinceLastUpdate -= frameTime; timeSinceLastUpdate -= frameTime;
update();
render(window);
}*/ }*/
// 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) // c)
// choose a simple sleep // choose a simple sleep
update();
render(window);
std::this_thread::sleep_for(std::chrono::milliseconds(2000)); std::this_thread::sleep_for(std::chrono::milliseconds(2000));
} }
} }
void Display::update() void Display::update ()
{ {
// TODO: maybe include AklToussaint heuristic first? // TODO: maybe include AklToussaint heuristic first?
// https://en.wikipedia.org/wiki/Convex_hull_algorithms#Akl%E2%80%93Toussaint_heuristic // https://en.wikipedia.org/wiki/Convex_hull_algorithms#Akl%E2%80%93Toussaint_heuristic
if (m_curStep == 1)
unsigned int curStep = (m_step > 5) ? ((m_step - 2) % 4 + 2) : (m_step % 6); // skip init and first step after first run
std::string text = "(" + std::to_string(m_step) + ") step " + std::to_string(curStep) + ": ";
if (curStep == 1)
{ {
// first step: select min - max x coordinates // first step: select min - max x coordinates
m_textStatus.setString("step " + std::to_string(m_curStep) + ": select min - max x coordinates..."); m_textStatus.setString(text + "select min - max x coordinates...");
/* EDIT: manual iteration for combining x and x minmax; also need not previous sorting
std::pair<Point, Point> minmax = getMinMaxX(m_points); std::pair<Point, Point> minmax = getMinMaxX(m_points);
std::cout << "min: " << minmax.first.x() << ", " << minmax.first.y() << std::cout << "min: " << minmax.first.x() << ", " << minmax.first.y() <<
", max: " << minmax.second.x() << ", " << minmax.second.y() << std::endl; ", max: " << minmax.second.x() << ", " << minmax.second.y() << std::endl;
m_hull.setPrimitiveType(sf::Lines); 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.first.x(), minmax.first.y()), sf::Color::Blue));
m_hull.append(sf::Vertex(sf::Vector2f(minmax.second.x(), minmax.second.y()), sf::Color::Blue)); m_hull.append(sf::Vertex(sf::Vector2f(minmax.second.x(), minmax.second.y()), sf::Color::Blue));*/
// if use AklToussaint heuristic
bool useAkl = false;
if (useAkl)
{
sf::Vector2f topLeft(WIDTH, HEIGHT);
sf::Vector2f topRight(0, HEIGHT);
sf::Vector2f botLeft(WIDTH, 0);
sf::Vector2f botRight(0, 0);
for (auto& pt : m_points)
{
sf::Vector2f pos = pt.getPosition();
float x = pos.x;
float y = pos.y;
if (x < topLeft.x && y < topLeft.y) topLeft = pos;
if (x > topRight.x && y < topLeft.y) topRight = pos;
if (x < botLeft.x && y > botLeft.y) botLeft = pos;
if (x > botRight.x && y > botRight.y) botRight = pos;
}
/* TODO: use a convex shape? Or build from vertices in render?
sf::ConvexShape convex;
convex.setPointCount(5);
convex.setPoint(0, topLeft);
convex.setPoint(0, topRight);
convex.setPoint(0, botRight);
convex.setPoint(0, botLeft);*/
//m_hull.setPrimitiveType(sf::Lines);
m_hull.setPrimitiveType(sf::LineStrip);
m_hull.append(sf::Vertex(topLeft, sf::Color::Blue));
m_hull.append(sf::Vertex(topRight, sf::Color::Blue));
m_hull.append(sf::Vertex(botRight, sf::Color::Blue));
m_hull.append(sf::Vertex(botLeft, sf::Color::Blue));
m_hull.append(sf::Vertex(topLeft, sf::Color::Blue));
}
else
{
sf::Vector2f left(WIDTH, HEIGHT);
sf::Vector2f right(0, 0);
for (auto& pt : m_points)
{
sf::Vector2f pos = pt.getPosition();
float x = pos.x;
float y = pos.y;
if (x < left.x) left = pos;
if (x > right.x) right = pos;
}
//m_hull.setPrimitiveType(sf::Lines);
m_hull.setPrimitiveType(sf::LineStrip);
m_hull.append(sf::Vertex(left, sf::Color::Blue));
m_hull.append(sf::Vertex(right, sf::Color::Blue));
}
} }
else if (m_curStep == 2) else if (curStep == 2)
{ {
// second step: split board and find furthest point // second step: split board and find furthest point
m_textStatus.setString("step " + std::to_string(m_curStep) + ": split board and find furthest point..."); m_textStatus.setString(text + "split board and find furthest point...");
for (auto& pt : m_points)
{
sf::Vector2f pos = pt.getPosition();
float x = pos.x;
float y = pos.y;
pt.setFillColor(sign(x, y, m_hull[0].position.x, m_hull[0].position.y, m_hull[1].position.x, m_hull[1].position.y) > 0 ? sf::Color::Red : sf::Color::Green);
}
} }
else if (m_curStep == 3) else if (curStep == 3)
{ {
// third step: draw triangle, remove inner points // third step: draw triangle, remove inner points
m_textStatus.setString("step " + std::to_string(m_curStep) + ": draw triangle, remove inner points..."); m_textStatus.setString(text + "find furthest point and draw triangle...");
} }
else if (m_curStep == 4) else if (curStep == 4)
{ {
// fourth step: remove inner points // fourth step: remove inner points
m_textStatus.setString("step " + std::to_string(m_curStep) + ": remove inner points..."); m_textStatus.setString(text + "remove inner points...");
} }
else if (m_curStep == 5) else if (curStep == 5)
{ {
// fourth step: remove inner points // fifth step: adding new hull point
m_textStatus.setString("step " + std::to_string(m_curStep) + ": finished calculating convex hull");
}
m_curStep++; // TEMP: TEST check if ends
// if finished removing inner points and there are still points left -> repeat from step 2 //if (m_step >= 10) m_points.clear();
// TODO:
if (m_curStep == 5 && m_points.size() > 0)
{
m_curStep = 2;
}
if (m_points.size() == 0) m_textStatus.setString(text + "finished calculating convex hull");
else m_textStatus.setString(text + "adding new hull point...");
}
else m_textStatus.setString(text + "invalid status!");
if (curStep != 5 || m_points.size() > 0) m_step++;
} }
void Display::setData(std::vector<Point> pts) void Display::render (sf::RenderWindow &window)
{ {
m_points = pts; window.clear(sf::Color::White);
// always print remaining points
/*size_t points = m_points.size();
for (size_t i = 0; i < points; ++i)
{
drawPoint(window, i);
}*/
//for (auto& pt : m_points) // points and labels should have the same size -> combine in one loop
size_t points = m_points.size();
for (size_t i = 0; i < points; ++i)
{
window.draw(m_points[i]);
window.draw(m_labels[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();
} }

View File

@ -8,7 +8,7 @@
class Point; class Point;
#define OFFSET 10 #define OFFSET 10.f
#define WIDTH 800 #define WIDTH 800
#define HEIGHT 600 #define HEIGHT 600
@ -18,20 +18,22 @@ private:
sf::Font m_font; sf::Font m_font;
sf::Text m_textStatus; sf::Text m_textStatus;
std::vector<Point> m_points; //std::vector<Point> m_points;
std::vector<sf::CircleShape> m_points;
std::vector<sf::Text> m_labels;
//sf::VertexArray m_points;
//sf::VertexArray m_labels;
//std::vector<sf::Vertex> m_hull; //std::vector<sf::Vertex> m_hull;
sf::VertexArray m_hull; sf::VertexArray m_hull;
void drawPoint(sf::RenderWindow &, size_t); unsigned int m_step = 0;
int m_curStep = 0;
void update(); void update();
void render(sf::RenderWindow &);
public: public:
Display(); Display(const std::vector<Point> &);
void show(); void show();
void setData (std::vector<Point>);
}; };
#endif // DISPLAY_H #endif // DISPLAY_H

View File

@ -1,6 +1,11 @@
#pragma once #pragma once
#ifndef UTILITY_H #ifndef UTILITY_H
static float sign(float x1, float y1, float x2, float y2, float x3, float y3)
{
return (x1 - x3) * (y2 - y3) - (x2 - x3) * (y1 - y3);
}
static float sign (Point &p1, Point &p2, Point &p3) 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()); return (p1.x() - p3.x()) * (p2.y() - p3.y()) - (p2.x() - p3.x()) * (p1.y() - p3.y());

View File

@ -42,10 +42,32 @@ int main (int argc, char **argv)
std::cout << "generating random numbers..." << std::endl; std::cout << "generating random numbers..." << std::endl;
//srand(static_cast <unsigned> (time(0))); //srand(static_cast <unsigned> (time(0)));
srand(static_cast <unsigned> (0)); // fixed seed for testing srand(static_cast <unsigned> (0)); // fixed seed for testing
// 2) rectangle
//float diff = (2.f * HEIGHT / valCount);
// 3) circle
//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) for (int i = 0; i < valCount; ++i)
{ {
float x = OFFSET + static_cast <float> (rand()) / (static_cast <float> (RAND_MAX / (WIDTH - OFFSET))); // 1) random generation
float y = OFFSET + static_cast <float> (rand()) / (static_cast <float> (RAND_MAX / (HEIGHT - OFFSET))); //x = OFFSET + static_cast <float> (rand()) / (static_cast <float> (RAND_MAX / (WIDTH - OFFSET)));
//y = OFFSET + static_cast <float> (rand()) / (static_cast <float> (RAND_MAX / (HEIGHT - OFFSET)));
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)));
// 2) rectangle
//x = (i%2 == 0) ? OFFSET : WIDTH - OFFSET;
//y = diff/2 + (i/2) * diff;
// 3) position in circle around center, x/y plane
//x = WIDTH/2 + rad * cosf(i * deg);
//y = HEIGHT/2 + rad * sinf(i * deg);
points.push_back(Point(x, y)); points.push_back(Point(x, y));
} }
} }
@ -78,7 +100,7 @@ int main (int argc, char **argv)
} }
// TODO: sort here, once and for all? xD // TODO: sort here, once and for all? xD
sortPoints(points); //sortPoints(points);
for (Point& pt : points) for (Point& pt : points)
{ {
std::cout << "pt: " << pt.x() << ", " << pt.y() << std::endl; std::cout << "pt: " << pt.x() << ", " << pt.y() << std::endl;
@ -86,13 +108,10 @@ int main (int argc, char **argv)
if (vis) if (vis)
{ {
// TODO: use data as ctor argument? pointer?
// TEST to check SFML coordinate system // TEST to check SFML coordinate system
points.push_back(Point(0, 0)); //points.push_back(Point(0, 0));
Display display; Display display(points);
display.setData(points);
display.show(); display.show();
} }