diff --git a/lib/qtgraph/CMakeLists.txt b/lib/qtgraph/CMakeLists.txt index 9ef4870..3bbb66c 100644 --- a/lib/qtgraph/CMakeLists.txt +++ b/lib/qtgraph/CMakeLists.txt @@ -6,6 +6,10 @@ set (CXX_FLAGS "-Wall -Wextra -pedantic -Wshadow " "-ggdb " "--std=c++0x " ) +set ( CMAKE_CXX_COMPILER "/usr/bin/g++-4.9.2") +# set ( CMAKE_CXX_COMPILER "distcc") + + add_definitions( ${CXX_FLAGS} ) FIND_PACKAGE(Qt4 REQUIRED) @@ -25,9 +29,10 @@ main.cpp edge.cpp node.cpp graphwidget.cpp +marching_squares.cpp ${qtgraph_HEADERS_MOC} ) -target_link_libraries ( qtgraph ${QT_LIBRARIES} xml2 ) +target_link_libraries ( qtgraph ${QT_LIBRARIES} xml2 png) add_custom_target (clean2 COMMAND diff --git a/lib/qtgraph/graphwidget.cpp b/lib/qtgraph/graphwidget.cpp index 3a4b3c1..1a1f48c 100644 --- a/lib/qtgraph/graphwidget.cpp +++ b/lib/qtgraph/graphwidget.cpp @@ -5,7 +5,9 @@ #include #include + #include "floats.hpp" +#include "marching_squares.hpp" #include #include @@ -15,6 +17,7 @@ #include #include +#include namespace std { template <> @@ -85,8 +88,11 @@ QList calculateShortestRoute(const QGraphicsScene* scene, QGraphicsItem* source_item = scene->itemAt(QPointFFromfloat2(shortestPath[i])); QGraphicsItem* destination_item = scene->itemAt(QPointFFromfloat2(shortestPath[i+1])); Node* source_node = dynamic_cast(source_item); + assert(source_node); Node* destination_node = dynamic_cast(destination_item); + assert(destination_node); Edge* e = source_node->edgeTo(destination_node); + assert(e); route.append(e); } return route; @@ -99,7 +105,13 @@ GraphWidget::GraphWidget(Graph* graph, const QString& png_file, QWidget : QGraphicsView(p) , m_graph(graph) , m_background(0) + , m_lines() + , m_lines_are_visible(true) { + MarchingSquares ms; + ms.ReadImage(png_file.toStdString()); + m_lines = ms.RunMarchingSquares(); + m_background = new QPixmap(png_file); if (m_background->isNull()) throw std::invalid_argument("Could not load image from png file."); @@ -193,6 +205,11 @@ void GraphWidget::keyPressEvent(QKeyEvent *e) } case Qt::Key_Space: { modifyRoute(); + break; + } + case Qt::Key_L: { + showLines(); + break; } default: QGraphicsView::keyPressEvent(e); @@ -206,7 +223,7 @@ void GraphWidget::modifyRoute() QList selectedItems = scene()->selectedItems(); if (selectedItems.isEmpty()) - break; + return; QGraphicsItem* selectedItem = selectedItems.first(); Node* selectedNode = dynamic_cast(selectedItem); @@ -225,6 +242,53 @@ void GraphWidget::modifyRoute() } } +void GraphWidget::showLines() +{ + m_lines_are_visible = !m_lines_are_visible; + if (m_lines_are_visible) { + resetGraph(); + for (std::size_t i = 0; i < m_lines.size(); ++i) { + const std::pair e = m_lines[i]; + const float2 s = e.first; + Node* s_n = 0; + if (!contains(*m_graph, s)) { + s_n = new Node(this); + scene()->addItem(s_n); + s_n->setPos(s.x, s.y); + } else { + QGraphicsItem* gi = scene()->itemAt(QPointFFromfloat2(s)); + s_n = dynamic_cast(gi); + } + const float2 d = e.second; + Node* d_n = 0; + if (!contains(*m_graph, d)) { + d_n = new Node(this); + scene()->addItem(d_n); + d_n->setPos(d.x, d.y); + } else { + QGraphicsItem* gi = scene()->itemAt(QPointFFromfloat2(d)); + d_n = dynamic_cast(gi); + } + insertEdge(s_n, d_n); + m_graph->addEdge(s, d); + } + } +} + +void GraphWidget::resetGraph() +{ + for (QList::iterator it = m_route.begin(); it != m_route.end(); ++it) + (*it)->setIsRoute(false); + + m_route.clear(); + m_graph->clear(); + QList all_items = scene()->items(); + for (int i = 0; i < all_items.size(); ++i) { + scene()->removeItem(all_items.at(i)); + delete all_items.at(i); + } +} + void GraphWidget::removeEdge(Node* selectedNode, Node* nodeUnderMouse) { diff --git a/lib/qtgraph/graphwidget.hpp b/lib/qtgraph/graphwidget.hpp index 2d8d0eb..7822759 100644 --- a/lib/qtgraph/graphwidget.hpp +++ b/lib/qtgraph/graphwidget.hpp @@ -7,7 +7,7 @@ class float2; template class Graph; class Node; class Edge; -// class QPixmap; +class QString; class GraphWidget : public QGraphicsView { @@ -39,10 +39,14 @@ private: void insertEdge(Node* selectedNode, Node* nodeUnderMouse); void removeEdge(Node* selectedNode, Node* nodeUnderMouse); void modifyRoute(); + void showLines(); + void resetGraph(); Graph* m_graph; QPixmap* m_background; QList m_route; + std::vector< std::pair > m_lines; + bool m_lines_are_visible; }; #endif diff --git a/lib/qtgraph/marching_squares.cpp b/lib/qtgraph/marching_squares.cpp new file mode 100644 index 0000000..c74a926 --- /dev/null +++ b/lib/qtgraph/marching_squares.cpp @@ -0,0 +1,135 @@ +#include "marching_squares.hpp" + +#include "floats.hpp" + +// #include // for strerror needed by png++/error.hpp +#include // for strerror needed by png++/error.hpp + +#include +// #include +#include + + +MarchingSquares::MarchingSquares() + : width_(0) + , height_(0) + , cells_() +{ +} + +void MarchingSquares::ReadImage(const std::string& filename) +{ + png::image image(filename); // throws std_error(filename); + + width_ = image.get_width(); + height_ = image.get_height(); + cells_.reserve(width_ * height_); + + for (size_t y = 0; y < height_; ++y) { + const png::image::row_type& row = image[y]; + for (size_t x = 0; x < width_; ++x) { + CellType c; // map pixel luminance to cell type + if (row[x] < 16) c = SOLID; // black + else if (row[x] >= 240) c = FREE; // white + else c = DESTROYABLE; // everything else + + // mark borders as SOLID, such that the entities in the world cannot fall off + // if (x == 0 || x == width_ - 1 || y == 0 || y == height_ - 1) + // c = SOLID; + + cells_.push_back(c); + } + } +} + + +std::vector< std::pair > +MarchingSquares::RunMarchingSquares() { + + std::vector< std::pair > lines; + for (size_t y = 1; y < height_; ++y) { + for (size_t x = 1; x < width_; ++x) { + + const CellType quad[4] = { + cells_[(y-1) * width_ + (x-1)], cells_[(y-1) * width_ + x], // TL T + cells_[ y * width_ + (x-1)], cells_[ y * width_ + x] }; // R X + + const int mask = ((quad[0] == FREE) ? 0x0 : 0x1) + | ((quad[1] == FREE) ? 0x0 : 0x2) + | ((quad[2] == FREE) ? 0x0 : 0x4) + | ((quad[3] == FREE) ? 0x0 : 0x8); + + const float2 points[8] = { // clockwise, starting in top-left corner + float2(x-1, y-1 ), // TL 0 + float2(x-0.5f, y-1 ), // T 1 + float2(x, y-1 ), // TR 2 + float2(x, y-0.5f), // R 3 + float2(x, y ), // BR 4 + float2(x-0.5f, y ), // B 5 + float2(x-1, y ), // BL 6 + float2(x-1, y-0.5f) // L 7 + }; + + // these are the marching squares cases + int s = -1, e = -1; + switch (mask) { + case 0x0: /* all free, nothing to do */ break; + + case 0x1: /* TL = TL */ s = 7; e = 1; break; + case 0x2: /* TR = TR */ s = 1; e = 3; break; + case 0x4: /* BL = BL */ s = 5; e = 7; break; + case 0x8: /* BR = BR */ s = 3; e = 5; break; + + case 0x3: /* TLTR = top */ s = 7; e = 3; break; + case 0x5: /* TL BL = left */ s = 1; e = 5; break; + case 0xa: /* TR BR = right */ s = 5; e = 1; break; + case 0xc: /* BLBR = bottom */ s = 3; e = 7; break; + + // two lines + case 0x6: /* TRBL = left desc diagonal */ s = 1 | 5 << 4; e = 3 | 7 << 4; break; + case 0x9: /* TL BR = right asc diagonal */ s = 7 | 3 << 4; e = 1 | 5 << 4; break; + + case 0x7: /* TLTRBL = BR free */ s = 3; e = 5; break; + case 0xb: /* TLTR BR = BL free */ s = 5; e = 7; break; + case 0xd: /* TL BLBR = TR free */ s = 1; e = 3; break; + case 0xe: /* TRBLBR = TL free */ s = 7; e = 1; break; + + case 0xf: /* all blocked, nothing to do */ break; + } + + if (s == -1) { + // assert: e == -1 + } + else if ((s & 0xf) == s) { + // assert: (e & 0xf) == e + lines.push_back(std::pair(points[s], points[e])); + } + else { + int s1 = s & 0xf, e1 = e & 0xf; + int s2 = s >> 4, e2 = e >> 4; + lines.push_back(std::pair(points[s1], points[e1])); + lines.push_back(std::pair(points[s2], points[e2])); + } + } + } + + // build quadtree and combine lines (there are many, many small pieces around now, combine to longer lines) + + return lines; +} + +std::string +dumpLinesToHtml(const std::vector< std::pair< float2, float2 > >& lines, int w, int h) +{ + std::stringstream ss; + + // dump to HTML/SVG + ss << "" << std::endl; + ss << "" << std::endl; + for (size_t i = 0; i < lines.size(); ++i) { + const std::pair& l = lines[i]; + ss << "\t" << std::endl; + } + ss << "" << std::endl; + return ss.str(); +} diff --git a/lib/qtgraph/marching_squares.hpp b/lib/qtgraph/marching_squares.hpp new file mode 100644 index 0000000..45c8968 --- /dev/null +++ b/lib/qtgraph/marching_squares.hpp @@ -0,0 +1,27 @@ +#ifndef WORLD_WORLD_HPP +#define WORLD_WORLD_HPP + +#include +#include + +class float2; + +class MarchingSquares { + enum CellType { FREE, SOLID, DESTROYABLE }; + +public: + MarchingSquares(); + + void ReadImage(const std::string& filename); + std::vector< std::pair > RunMarchingSquares(); + +private: + + std::size_t width_; + std::size_t height_; + std::vector cells_; +}; + +std::string dumpLinesToHtml(const std::vector< std::pair >& lines, int w, int h); + +#endif // WORLD_WORLD_HPP