| // Boost.Polygon library voronoi_visualizer.cpp file |
| |
| // Copyright Andrii Sydorchuk 2010-2012. |
| // Distributed under the Boost Software License, Version 1.0. |
| // (See accompanying file LICENSE_1_0.txt or copy at |
| // http://www.boost.org/LICENSE_1_0.txt) |
| |
| // See http://www.boost.org for updates, documentation, and revision history. |
| |
| #include <iostream> |
| #include <vector> |
| |
| #include <QtOpenGL/QGLWidget> |
| #include <QtGui/QtGui> |
| |
| #include <boost/polygon/polygon.hpp> |
| #include <boost/polygon/voronoi.hpp> |
| using namespace boost::polygon; |
| |
| #include "voronoi_visual_utils.hpp" |
| |
| class GLWidget : public QGLWidget { |
| Q_OBJECT |
| |
| public: |
| explicit GLWidget(QMainWindow* parent = NULL) : |
| QGLWidget(QGLFormat(QGL::SampleBuffers), parent), |
| primary_edges_only_(false), |
| internal_edges_only_(false) { |
| startTimer(40); |
| } |
| |
| QSize sizeHint() const { |
| return QSize(600, 600); |
| } |
| |
| void build(const QString& file_path) { |
| // Clear all containers. |
| clear(); |
| |
| // Read data. |
| read_data(file_path); |
| |
| // No data, don't proceed. |
| if (!brect_initialized_) { |
| return; |
| } |
| |
| // Construct bounding rectangle. |
| construct_brect(); |
| |
| // Construct voronoi diagram. |
| construct_voronoi( |
| point_data_.begin(), point_data_.end(), |
| segment_data_.begin(), segment_data_.end(), |
| &vd_); |
| |
| // Color exterior edges. |
| for (const_edge_iterator it = vd_.edges().begin(); |
| it != vd_.edges().end(); ++it) { |
| if (!it->is_finite()) { |
| color_exterior(&(*it)); |
| } |
| } |
| |
| // Update view port. |
| update_view_port(); |
| } |
| |
| void show_primary_edges_only() { |
| primary_edges_only_ ^= true; |
| } |
| |
| void show_internal_edges_only() { |
| internal_edges_only_ ^= true; |
| } |
| |
| protected: |
| void initializeGL() { |
| glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); |
| glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
| glEnable(GL_BLEND); |
| glEnable(GL_POINT_SMOOTH); |
| } |
| |
| void paintGL() { |
| qglClearColor(QColor::fromRgb(255, 255, 255)); |
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
| |
| draw_points(); |
| draw_segments(); |
| draw_vertices(); |
| draw_edges(); |
| } |
| |
| void resizeGL(int width, int height) { |
| int side = qMin(width, height); |
| glViewport((width - side) / 2, (height - side) / 2, side, side); |
| } |
| |
| void timerEvent(QTimerEvent* e) { |
| update(); |
| } |
| |
| private: |
| typedef double coordinate_type; |
| typedef point_data<coordinate_type> point_type; |
| typedef segment_data<coordinate_type> segment_type; |
| typedef rectangle_data<coordinate_type> rect_type; |
| typedef voronoi_builder<int> VB; |
| typedef voronoi_diagram<coordinate_type> VD; |
| typedef VD::cell_type cell_type; |
| typedef VD::cell_type::source_index_type source_index_type; |
| typedef VD::cell_type::source_category_type source_category_type; |
| typedef VD::edge_type edge_type; |
| typedef VD::cell_container_type cell_container_type; |
| typedef VD::cell_container_type vertex_container_type; |
| typedef VD::edge_container_type edge_container_type; |
| typedef VD::const_cell_iterator const_cell_iterator; |
| typedef VD::const_vertex_iterator const_vertex_iterator; |
| typedef VD::const_edge_iterator const_edge_iterator; |
| |
| static const std::size_t EXTERNAL_COLOR = 1; |
| |
| void clear() { |
| brect_initialized_ = false; |
| point_data_.clear(); |
| segment_data_.clear(); |
| vd_.clear(); |
| } |
| |
| void read_data(const QString& file_path) { |
| QFile data(file_path); |
| if (!data.open(QFile::ReadOnly)) { |
| QMessageBox::warning( |
| this, tr("Voronoi Visualizer"), |
| tr("Disable to open file ") + file_path); |
| } |
| QTextStream in_stream(&data); |
| std::size_t num_points, num_segments; |
| int x1, y1, x2, y2; |
| in_stream >> num_points; |
| for (std::size_t i = 0; i < num_points; ++i) { |
| in_stream >> x1 >> y1; |
| point_type p(x1, y1); |
| update_brect(p); |
| point_data_.push_back(p); |
| } |
| in_stream >> num_segments; |
| for (std::size_t i = 0; i < num_segments; ++i) { |
| in_stream >> x1 >> y1 >> x2 >> y2; |
| point_type lp(x1, y1); |
| point_type hp(x2, y2); |
| update_brect(lp); |
| update_brect(hp); |
| segment_data_.push_back(segment_type(lp, hp)); |
| } |
| in_stream.flush(); |
| } |
| |
| void update_brect(const point_type& point) { |
| if (brect_initialized_) { |
| encompass(brect_, point); |
| } else { |
| set_points(brect_, point, point); |
| brect_initialized_ = true; |
| } |
| } |
| |
| void construct_brect() { |
| double side = (std::max)(xh(brect_) - xl(brect_), yh(brect_) - yl(brect_)); |
| center(shift_, brect_); |
| set_points(brect_, shift_, shift_); |
| bloat(brect_, side * 1.2); |
| } |
| |
| void color_exterior(const VD::edge_type* edge) { |
| if (edge->color() == EXTERNAL_COLOR) { |
| return; |
| } |
| edge->color(EXTERNAL_COLOR); |
| edge->twin()->color(EXTERNAL_COLOR); |
| const VD::vertex_type* v = edge->vertex1(); |
| if (v == NULL || !edge->is_primary()) { |
| return; |
| } |
| v->color(EXTERNAL_COLOR); |
| const VD::edge_type* e = v->incident_edge(); |
| do { |
| color_exterior(e); |
| e = e->rot_next(); |
| } while (e != v->incident_edge()); |
| } |
| |
| void update_view_port() { |
| glMatrixMode(GL_PROJECTION); |
| glLoadIdentity(); |
| rect_type view_rect = brect_; |
| deconvolve(view_rect, shift_); |
| glOrtho(xl(view_rect), xh(view_rect), |
| yl(view_rect), yh(view_rect), |
| -1.0, 1.0); |
| glMatrixMode(GL_MODELVIEW); |
| } |
| |
| void draw_points() { |
| // Draw input points and endpoints of the input segments. |
| glColor3f(0.0f, 0.5f, 1.0f); |
| glPointSize(9); |
| glBegin(GL_POINTS); |
| for (std::size_t i = 0; i < point_data_.size(); ++i) { |
| point_type point = point_data_[i]; |
| deconvolve(point, shift_); |
| glVertex2f(point.x(), point.y()); |
| } |
| for (std::size_t i = 0; i < segment_data_.size(); ++i) { |
| point_type lp = low(segment_data_[i]); |
| lp = deconvolve(lp, shift_); |
| glVertex2f(lp.x(), lp.y()); |
| point_type hp = high(segment_data_[i]); |
| hp = deconvolve(hp, shift_); |
| glVertex2f(hp.x(), hp.y()); |
| } |
| glEnd(); |
| } |
| |
| void draw_segments() { |
| // Draw input segments. |
| glColor3f(0.0f, 0.5f, 1.0f); |
| glLineWidth(2.7f); |
| glBegin(GL_LINES); |
| for (std::size_t i = 0; i < segment_data_.size(); ++i) { |
| point_type lp = low(segment_data_[i]); |
| lp = deconvolve(lp, shift_); |
| glVertex2f(lp.x(), lp.y()); |
| point_type hp = high(segment_data_[i]); |
| hp = deconvolve(hp, shift_); |
| glVertex2f(hp.x(), hp.y()); |
| } |
| glEnd(); |
| } |
| |
| void draw_vertices() { |
| // Draw voronoi vertices. |
| glColor3f(0.0f, 0.0f, 0.0f); |
| glPointSize(6); |
| glBegin(GL_POINTS); |
| for (const_vertex_iterator it = vd_.vertices().begin(); |
| it != vd_.vertices().end(); ++it) { |
| if (internal_edges_only_ && (it->color() == EXTERNAL_COLOR)) { |
| continue; |
| } |
| point_type vertex(it->x(), it->y()); |
| vertex = deconvolve(vertex, shift_); |
| glVertex2f(vertex.x(), vertex.y()); |
| } |
| glEnd(); |
| } |
| void draw_edges() { |
| // Draw voronoi edges. |
| glColor3f(0.0f, 0.0f, 0.0f); |
| glLineWidth(1.7f); |
| for (const_edge_iterator it = vd_.edges().begin(); |
| it != vd_.edges().end(); ++it) { |
| if (primary_edges_only_ && !it->is_primary()) { |
| continue; |
| } |
| if (internal_edges_only_ && (it->color() == EXTERNAL_COLOR)) { |
| continue; |
| } |
| std::vector<point_type> samples; |
| if (!it->is_finite()) { |
| clip_infinite_edge(*it, &samples); |
| } else { |
| point_type vertex0(it->vertex0()->x(), it->vertex0()->y()); |
| samples.push_back(vertex0); |
| point_type vertex1(it->vertex1()->x(), it->vertex1()->y()); |
| samples.push_back(vertex1); |
| if (it->is_curved()) { |
| sample_curved_edge(*it, &samples); |
| } |
| } |
| glBegin(GL_LINE_STRIP); |
| for (std::size_t i = 0; i < samples.size(); ++i) { |
| point_type vertex = deconvolve(samples[i], shift_); |
| glVertex2f(vertex.x(), vertex.y()); |
| } |
| glEnd(); |
| } |
| } |
| |
| void clip_infinite_edge( |
| const edge_type& edge, std::vector<point_type>* clipped_edge) { |
| const cell_type& cell1 = *edge.cell(); |
| const cell_type& cell2 = *edge.twin()->cell(); |
| point_type origin, direction; |
| // Infinite edges could not be created by two segment sites. |
| if (cell1.contains_point() && cell2.contains_point()) { |
| point_type p1 = retrieve_point(cell1); |
| point_type p2 = retrieve_point(cell2); |
| origin.x((p1.x() + p2.x()) * 0.5); |
| origin.y((p1.y() + p2.y()) * 0.5); |
| direction.x(p1.y() - p2.y()); |
| direction.y(p2.x() - p1.x()); |
| } else { |
| origin = cell1.contains_segment() ? |
| retrieve_point(cell2) : |
| retrieve_point(cell1); |
| segment_type segment = cell1.contains_segment() ? |
| retrieve_segment(cell1) : |
| retrieve_segment(cell2); |
| coordinate_type dx = high(segment).x() - low(segment).x(); |
| coordinate_type dy = high(segment).y() - low(segment).y(); |
| if ((low(segment) == origin) ^ cell1.contains_point()) { |
| direction.x(dy); |
| direction.y(-dx); |
| } else { |
| direction.x(-dy); |
| direction.y(dx); |
| } |
| } |
| coordinate_type side = xh(brect_) - xl(brect_); |
| coordinate_type koef = |
| side / (std::max)(fabs(direction.x()), fabs(direction.y())); |
| if (edge.vertex0() == NULL) { |
| clipped_edge->push_back(point_type( |
| origin.x() - direction.x() * koef, |
| origin.y() - direction.y() * koef)); |
| } else { |
| clipped_edge->push_back( |
| point_type(edge.vertex0()->x(), edge.vertex0()->y())); |
| } |
| if (edge.vertex1() == NULL) { |
| clipped_edge->push_back(point_type( |
| origin.x() + direction.x() * koef, |
| origin.y() + direction.y() * koef)); |
| } else { |
| clipped_edge->push_back( |
| point_type(edge.vertex1()->x(), edge.vertex1()->y())); |
| } |
| } |
| |
| void sample_curved_edge( |
| const edge_type& edge, |
| std::vector<point_type>* sampled_edge) { |
| coordinate_type max_dist = 1E-3 * (xh(brect_) - xl(brect_)); |
| point_type point = edge.cell()->contains_point() ? |
| retrieve_point(*edge.cell()) : |
| retrieve_point(*edge.twin()->cell()); |
| segment_type segment = edge.cell()->contains_point() ? |
| retrieve_segment(*edge.twin()->cell()) : |
| retrieve_segment(*edge.cell()); |
| voronoi_visual_utils<coordinate_type>::discretize( |
| point, segment, max_dist, sampled_edge); |
| } |
| |
| point_type retrieve_point(const cell_type& cell) { |
| source_index_type index = cell.source_index(); |
| source_category_type category = cell.source_category(); |
| if (category == SOURCE_CATEGORY_SINGLE_POINT) { |
| return point_data_[index]; |
| } |
| index -= point_data_.size(); |
| if (category == SOURCE_CATEGORY_SEGMENT_START_POINT) { |
| return low(segment_data_[index]); |
| } else { |
| return high(segment_data_[index]); |
| } |
| } |
| |
| segment_type retrieve_segment(const cell_type& cell) { |
| source_index_type index = cell.source_index() - point_data_.size(); |
| return segment_data_[index]; |
| } |
| |
| point_type shift_; |
| std::vector<point_type> point_data_; |
| std::vector<segment_type> segment_data_; |
| rect_type brect_; |
| VB vb_; |
| VD vd_; |
| bool brect_initialized_; |
| bool primary_edges_only_; |
| bool internal_edges_only_; |
| }; |
| |
| class MainWindow : public QWidget { |
| Q_OBJECT |
| |
| public: |
| MainWindow() { |
| glWidget_ = new GLWidget(); |
| file_dir_ = QDir(QDir::currentPath(), tr("*.txt")); |
| file_name_ = tr(""); |
| |
| QHBoxLayout* centralLayout = new QHBoxLayout; |
| centralLayout->addWidget(glWidget_); |
| centralLayout->addLayout(create_file_layout()); |
| setLayout(centralLayout); |
| |
| update_file_list(); |
| setWindowTitle(tr("Voronoi Visualizer")); |
| layout()->setSizeConstraint(QLayout::SetFixedSize); |
| } |
| |
| private slots: |
| void primary_edges_only() { |
| glWidget_->show_primary_edges_only(); |
| } |
| |
| void internal_edges_only() { |
| glWidget_->show_internal_edges_only(); |
| } |
| |
| void browse() { |
| QString new_path = QFileDialog::getExistingDirectory( |
| 0, tr("Choose Directory"), file_dir_.absolutePath()); |
| if (new_path.isEmpty()) { |
| return; |
| } |
| file_dir_.setPath(new_path); |
| update_file_list(); |
| } |
| |
| void build() { |
| file_name_ = file_list_->currentItem()->text(); |
| QString file_path = file_dir_.filePath(file_name_); |
| message_label_->setText("Building..."); |
| glWidget_->build(file_path); |
| message_label_->setText("Double click the item to build voronoi diagram:"); |
| setWindowTitle(tr("Voronoi Visualizer - ") + file_path); |
| } |
| |
| void print_scr() { |
| if (!file_name_.isEmpty()) { |
| QImage screenshot = glWidget_->grabFrameBuffer(true); |
| QString output_file = file_dir_.absolutePath() + tr("/") + |
| file_name_.left(file_name_.indexOf('.')) + tr(".png"); |
| screenshot.save(output_file, 0, -1); |
| } |
| } |
| |
| private: |
| QGridLayout* create_file_layout() { |
| QGridLayout* file_layout = new QGridLayout; |
| |
| message_label_ = new QLabel("Double click item to build voronoi diagram:"); |
| |
| file_list_ = new QListWidget(); |
| file_list_->connect(file_list_, |
| SIGNAL(itemDoubleClicked(QListWidgetItem*)), |
| this, |
| SLOT(build())); |
| |
| QCheckBox* primary_checkbox = new QCheckBox("Show primary edges only."); |
| connect(primary_checkbox, SIGNAL(clicked()), |
| this, SLOT(primary_edges_only())); |
| |
| QCheckBox* internal_checkbox = new QCheckBox("Show internal edges only."); |
| connect(internal_checkbox, SIGNAL(clicked()), |
| this, SLOT(internal_edges_only())); |
| |
| QPushButton* browse_button = |
| new QPushButton(tr("Browse Input Directory")); |
| connect(browse_button, SIGNAL(clicked()), this, SLOT(browse())); |
| browse_button->setMinimumHeight(50); |
| |
| QPushButton* print_scr_button = new QPushButton(tr("Make Screenshot")); |
| connect(print_scr_button, SIGNAL(clicked()), this, SLOT(print_scr())); |
| print_scr_button->setMinimumHeight(50); |
| |
| file_layout->addWidget(message_label_, 0, 0); |
| file_layout->addWidget(file_list_, 1, 0); |
| file_layout->addWidget(primary_checkbox, 2, 0); |
| file_layout->addWidget(internal_checkbox, 3, 0); |
| file_layout->addWidget(browse_button, 4, 0); |
| file_layout->addWidget(print_scr_button, 5, 0); |
| |
| return file_layout; |
| } |
| |
| void update_file_list() { |
| QFileInfoList list = file_dir_.entryInfoList(); |
| file_list_->clear(); |
| if (file_dir_.count() == 0) { |
| return; |
| } |
| QFileInfoList::const_iterator it; |
| for (it = list.begin(); it != list.end(); it++) { |
| file_list_->addItem(it->fileName()); |
| } |
| file_list_->setCurrentRow(0); |
| } |
| |
| QDir file_dir_; |
| QString file_name_; |
| GLWidget* glWidget_; |
| QListWidget* file_list_; |
| QLabel* message_label_; |
| }; |
| |
| int main(int argc, char* argv[]) { |
| QApplication app(argc, argv); |
| MainWindow window; |
| window.show(); |
| return app.exec(); |
| } |
| |
| #include "voronoi_visualizer.moc" |