#include #include #include #include "Display.h" #include "Point.h" #include "Utility.h" Display::Display (const std::vector &pts) { if (!m_font.loadFromFile("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(2000)); } } void Display::update () { // TODO: maybe include Akl–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 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–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(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 (curStep == 2) { // second step: split board and find furthest point m_textStatus.setString(text + "split board and find furthest point..."); 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); } } else if (curStep == 3) { // 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) { 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); 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 (distance > maxDistance) { pos = pt.getPosition(); maxDistance = distance; } } if (maxDistance > 0) { // TODO: not append but insert between last line points m_hull.append(sf::Vertex(pos, sf::Color::Blue)); } } else if (curStep == 4) { // fourth step: remove inner points m_textStatus.setString(text + "remove inner points..."); } 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 m_textStatus.setString(text + "invalid status!"); if (curStep != 5 || m_points.size() > 0) m_step++; } void Display::render (sf::RenderWindow &window) { 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(); }