quickhull/Display.cpp

409 lines
15 KiB
C++
Raw Blame History

#include <iostream>
#include <chrono>
#include <thread>
#include "Display.h"
//#include "Line.h"
#include "Point.h"
#include "Utility.h"
Display::Display (const std::vector<Point> &pts, int stepSize) : m_stepSize(stepSize)
{
if (!m_font.loadFromFile("Resources/LiberationSans-Regular.ttf"))
{
std::cerr << "font LiberationSans-Regular.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);
size_t points = pts.size();
for (size_t i = 0; i < points; ++i)
{
const Point& pt = pts[i];
sf::CircleShape shape(OFFSET);
//shape.setPosition(pt.x() - OFFSET, pt.y() - OFFSET); // handle with origin or manually calc position
shape.setOrigin(OFFSET, OFFSET);
shape.setPosition(pt.x(), pt.y());
shape.setFillColor(sf::Color::Green);
shape.setOutlineThickness(1.f);
shape.setOutlineColor(sf::Color::Black);
m_points.push_back(shape);
//m_points.append(shape);
sf::Text label;
//label.setPosition(pt.x() - OFFSET / 2, pt.y() - OFFSET / 2 - 3);
label.setOrigin(OFFSET / 2 - 1, OFFSET / 2 + 3);
label.setPosition(pt.x(), pt.y());
label.setFont(m_font);
label.setString(std::to_string(i));
label.setCharacterSize(12);
label.setFillColor(sf::Color::Black);
m_labels.push_back(label);
//m_points.append(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::ContextSettings settings;
settings.antialiasingLevel = 8;
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 frameTime = sf::seconds(2); //sf::seconds(1.f / 60.f)
sf::Time timeSinceLastUpdate = frameTime; //sf::Time::Zero;*/
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
// a)
//update(elapsed);
//render(window);
/* b)
timeSinceLastUpdate += clock.restart();
// only handle every few seconds because we need no animations
if (timeSinceLastUpdate >= frameTime)
{
std::cout << "entering update and render" << std::endl;
// start by getting the most left and right point
timeSinceLastUpdate -= frameTime;
update();
render(window);
}*/
// c)
// choose a simple sleep
update();
render(window);
std::this_thread::sleep_for(std::chrono::milliseconds(m_stepSize));
}
}
void Display::update ()
{
// TODO: maybe include Akl<6B>Toussaint heuristic first?
// https://en.wikipedia.org/wiki/Convex_hull_algorithms#Akl%E2%80%93Toussaint_heuristic
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
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::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));*/
// if use Akl<6B>Toussaint 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;
// TODO: only check explicit x and y seperate!
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(4);
convex.setPoint(0, topLeft);
convex.setPoint(1, topRight);
convex.setPoint(2, botRight);
convex.setPoint(3, botLeft);*/
//m_ hull as VertexArray
//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 as VertexArray
//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));
m_lines.push_back(Line(Point(left.x, left.y), Point(right.x, right.y)));
m_lines.push_back(Line(Point(right.x, right.y), Point(left.x, left.y))); // add first line in both directions to work with "right side"
m_curLine = &m_lines[1];
// TODO: split points and use map
m_convex.setPointCount(2);
m_convex.setFillColor(sf::Color::Transparent);
m_convex.setOutlineColor(sf::Color::Red);
m_convex.setOutlineThickness(2);
m_convex.setPoint(0, left);
m_convex.setPoint(1, right);
// TODO: could add here -> set color of points for current split
}
}
else if (curStep == 2)
{
// second step: split board
m_textStatus.setString(text + "split board...");
// TODO: get current line with points
if (m_curLine == nullptr)
{
size_t lines = m_lines.size();
if (lines < 1) // no more open lines -> fin
{
m_points.clear();
m_textStatus.setString(text + "finished calculating convex hull");
return;
}
m_curLine = &m_lines[lines - 1];
}
std::cout << "lines " << std::to_string(m_lines.size()) << std::endl;
for (auto& line : m_lines)
{
std::cout << " " <<
std::to_string(line.from().x()) << ", " <<
std::to_string(line.from().y()) << " - " <<
std::to_string(line.to().x()) << ", " <<
std::to_string(line.to().y()) << " " << std::endl;
}
for (auto& pt : m_points)
{
pt.setFillColor(sign(
pt.getPosition().x, pt.getPosition().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);
m_curLine->from().x(), m_curLine->from().y(),
m_curLine->to().x(), m_curLine->to().y()) > 0 ? sf::Color::Red : sf::Color::Green);
}
}
else if (curStep == 3)
{
// get line with more than one point -> or use m_curLine
// calc furthest point
// create new lines
// third step: draw triangle, remove inner points
m_textStatus.setString(text + "find furthest point and draw triangle...");
sf::Vector2f pos = m_points[0].getPosition();
float maxDistance = 0;
for (auto& pt : m_points)
{
/*
if (pt.getFillColor() == sf::Color::Green)
{
std::cout << "distance to green point " << pt.getPosition().x << ", " << pt.getPosition().y << ": " << distance << std::endl;
}
else if (pt.getFillColor() == sf::Color::Red)
{
std::cout << "distance to red point " << pt.getPosition().x << ", " << pt.getPosition().y << ": " << distance << std::endl;
}
*/
if (pt.getFillColor() == sf::Color::Green) continue;
float distance = pDistance(
pt.getPosition().x, pt.getPosition().y,
//m_hull[0].position.x, m_hull[0].position.y,
//m_hull[1].position.x, m_hull[1].position.y);
m_curLine->from().x(), m_curLine->from().y(),
m_curLine->to().x(), m_curLine->from().y());
if (distance > maxDistance)
{
pos = pt.getPosition();
maxDistance = distance;
}
}
if (maxDistance > 0)
{
std::cout << "max point at " << pos.x << ", " << pos.y << std::endl;
// not append but insert between last line points
// -> append a point, then swap values
size_t vertices = m_hull.getVertexCount();
m_hull.append(sf::Vertex(pos, sf::Color::Blue));
m_hull[vertices] = m_hull[vertices - 1];
m_hull[vertices - 1].position = pos;
size_t points = m_convex.getPointCount();
m_convex.setPointCount(points + 1);
m_convex.setPoint(points, pos);
//m_lines.push_back(Line(m_curLine->from(), Point(pos.x, pos.y)));
//m_lines.push_back(Line(Point(pos.x, pos.y), m_curLine->to()));
m_lines.push_back(Line(Point(pos.x, pos.y), m_curLine->to()));
//m_curLine->set_to(Point(pos.x, pos.y)); // only changes the value of the pointer
//(*m_curLine).set_to(Point(pos.x, pos.y)); // doesn't update list
//(*m_curLine) = Line(m_curLine->from(), Point(pos.x, pos.y)); // doesn't update list
m_lines[m_lines.size() - 2] = Line(m_curLine->from(), Point(pos.x, pos.y)); // updates list entry
std::cout << "added to lines " << std::to_string(m_lines.size()) << std::endl;
for (auto& line : m_lines)
{
std::cout << " " <<
std::to_string(line.from().x()) << ", " <<
std::to_string(line.from().y()) << " - " <<
std::to_string(line.to().x()) << ", " <<
std::to_string(line.to().y()) << " " << std::endl;
}
}
else
{
m_lines.pop_back(); // remove last element -> TODO: directly use stack?
}
}
else if (curStep == 4)
{
// fourth step: remove inner points
m_textStatus.setString(text + "remove inner points...");
// TODO: wip
/*size_t lines = m_lines.size() - 1;
Point pt1 = m_lines[lines - 1].from();
Point pt2 = m_lines[lines - 1].to();
Point pt3 = m_lines[lines].to();
for (size_t i = m_points.size(); i > 0; i--)
{
Point pt(m_points[i].getPosition().x, m_points[i].getPosition().y);
if (IsPointInTriangle(pt, pt1, pt2, pt3))
{
m_points.erase(m_points.begin() + i);
}
}*/
m_curLine = nullptr;
}
else if (curStep == 5)
{
// fifth step: adding new hull point
// TEMP: TEST check if ends
//if (m_step >= 10) m_points.clear();
if (m_points.size() == 0) m_textStatus.setString(text + "finished calculating convex hull");
else m_textStatus.setString(text + "adding new hull point...");
}
else if (m_step > 0) m_textStatus.setString(text + "invalid status!");
if (m_stepSize == 0 && curStep > 0)
{
std::cout << "any key to continue with next step...";
std::cin.get();
}
if (curStep != 5 || m_points.size() > 0) m_step++;
}
void Display::render (sf::RenderWindow &window)
{
window.clear(sf::Color::White);
// draw already calculated hull
//if (step >= 1)
{
/*size_t vertices = m_hull.getVertexCount();
for (size_t i = 0; i < vertices; ++i)
{
std::cout << "hull pt[" << i << "] color: " << std::to_string(m_hull[i].color.toInteger()) << " >> " <<
std::to_string(m_hull[i].color.a) << ", " <<
std::to_string(m_hull[i].color.r) << ", " <<
std::to_string(m_hull[i].color.g) << ", " <<
std::to_string(m_hull[i].color.b) << std::endl;
}*/
//window.draw(&m_hull[0], m_hull.size(), sf::Lines);
window.draw(&m_hull[0], m_hull.getVertexCount(), m_hull.getPrimitiveType());
// TODO: either insert points at correct location
// or only sort, then draw final hull
//window.draw(m_convex);
}
// always print remaining points
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 line the algorithm is currently working on
if (m_curLine != nullptr)
{
sf::Vertex line[] =
{
sf::Vertex(sf::Vector2f(m_curLine->from().x(), m_curLine->from().y()), sf::Color::Red),
sf::Vertex(sf::Vector2f(m_curLine->to().x(), m_curLine->to().y()), sf::Color::Red)
};
window.draw(line, 2, sf::Lines);
}
// show amount of steps and current status
window.draw(m_textStatus);
window.display();
}