voronoi_visualizer.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. // Boost.Polygon library voronoi_visualizer.cpp file
  2. // Copyright Andrii Sydorchuk 2010-2012.
  3. // Distributed under the Boost Software License, Version 1.0.
  4. // (See accompanying file LICENSE_1_0.txt or copy at
  5. // http://www.boost.org/LICENSE_1_0.txt)
  6. // See http://www.boost.org for updates, documentation, and revision history.
  7. #include <iostream>
  8. #include <vector>
  9. #include <QtOpenGL/QGLWidget>
  10. #include <QtGui/QtGui>
  11. #include <boost/polygon/polygon.hpp>
  12. #include <boost/polygon/voronoi.hpp>
  13. using namespace boost::polygon;
  14. #include "voronoi_visual_utils.hpp"
  15. class GLWidget : public QGLWidget {
  16. Q_OBJECT
  17. public:
  18. explicit GLWidget(QMainWindow* parent = NULL) :
  19. QGLWidget(QGLFormat(QGL::SampleBuffers), parent),
  20. primary_edges_only_(false),
  21. internal_edges_only_(false) {
  22. startTimer(40);
  23. }
  24. QSize sizeHint() const {
  25. return QSize(600, 600);
  26. }
  27. void build(const QString& file_path) {
  28. // Clear all containers.
  29. clear();
  30. // Read data.
  31. read_data(file_path);
  32. // No data, don't proceed.
  33. if (!brect_initialized_) {
  34. return;
  35. }
  36. // Construct bounding rectangle.
  37. construct_brect();
  38. // Construct voronoi diagram.
  39. construct_voronoi(
  40. point_data_.begin(), point_data_.end(),
  41. segment_data_.begin(), segment_data_.end(),
  42. &vd_);
  43. // Color exterior edges.
  44. for (const_edge_iterator it = vd_.edges().begin();
  45. it != vd_.edges().end(); ++it) {
  46. if (!it->is_finite()) {
  47. color_exterior(&(*it));
  48. }
  49. }
  50. // Update view port.
  51. update_view_port();
  52. }
  53. void show_primary_edges_only() {
  54. primary_edges_only_ ^= true;
  55. }
  56. void show_internal_edges_only() {
  57. internal_edges_only_ ^= true;
  58. }
  59. protected:
  60. void initializeGL() {
  61. glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
  62. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  63. glEnable(GL_BLEND);
  64. glEnable(GL_POINT_SMOOTH);
  65. }
  66. void paintGL() {
  67. qglClearColor(QColor::fromRgb(255, 255, 255));
  68. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  69. draw_points();
  70. draw_segments();
  71. draw_vertices();
  72. draw_edges();
  73. }
  74. void resizeGL(int width, int height) {
  75. int side = qMin(width, height);
  76. glViewport((width - side) / 2, (height - side) / 2, side, side);
  77. }
  78. void timerEvent(QTimerEvent* e) {
  79. update();
  80. }
  81. private:
  82. typedef double coordinate_type;
  83. typedef point_data<coordinate_type> point_type;
  84. typedef segment_data<coordinate_type> segment_type;
  85. typedef rectangle_data<coordinate_type> rect_type;
  86. typedef voronoi_builder<int> VB;
  87. typedef voronoi_diagram<coordinate_type> VD;
  88. typedef VD::cell_type cell_type;
  89. typedef VD::cell_type::source_index_type source_index_type;
  90. typedef VD::cell_type::source_category_type source_category_type;
  91. typedef VD::edge_type edge_type;
  92. typedef VD::cell_container_type cell_container_type;
  93. typedef VD::cell_container_type vertex_container_type;
  94. typedef VD::edge_container_type edge_container_type;
  95. typedef VD::const_cell_iterator const_cell_iterator;
  96. typedef VD::const_vertex_iterator const_vertex_iterator;
  97. typedef VD::const_edge_iterator const_edge_iterator;
  98. static const std::size_t EXTERNAL_COLOR = 1;
  99. void clear() {
  100. brect_initialized_ = false;
  101. point_data_.clear();
  102. segment_data_.clear();
  103. vd_.clear();
  104. }
  105. void read_data(const QString& file_path) {
  106. QFile data(file_path);
  107. if (!data.open(QFile::ReadOnly)) {
  108. QMessageBox::warning(
  109. this, tr("Voronoi Visualizer"),
  110. tr("Disable to open file ") + file_path);
  111. }
  112. QTextStream in_stream(&data);
  113. std::size_t num_points, num_segments;
  114. int x1, y1, x2, y2;
  115. in_stream >> num_points;
  116. for (std::size_t i = 0; i < num_points; ++i) {
  117. in_stream >> x1 >> y1;
  118. point_type p(x1, y1);
  119. update_brect(p);
  120. point_data_.push_back(p);
  121. }
  122. in_stream >> num_segments;
  123. for (std::size_t i = 0; i < num_segments; ++i) {
  124. in_stream >> x1 >> y1 >> x2 >> y2;
  125. point_type lp(x1, y1);
  126. point_type hp(x2, y2);
  127. update_brect(lp);
  128. update_brect(hp);
  129. segment_data_.push_back(segment_type(lp, hp));
  130. }
  131. in_stream.flush();
  132. }
  133. void update_brect(const point_type& point) {
  134. if (brect_initialized_) {
  135. encompass(brect_, point);
  136. } else {
  137. set_points(brect_, point, point);
  138. brect_initialized_ = true;
  139. }
  140. }
  141. void construct_brect() {
  142. double side = (std::max)(xh(brect_) - xl(brect_), yh(brect_) - yl(brect_));
  143. center(shift_, brect_);
  144. set_points(brect_, shift_, shift_);
  145. bloat(brect_, side * 1.2);
  146. }
  147. void color_exterior(const VD::edge_type* edge) {
  148. if (edge->color() == EXTERNAL_COLOR) {
  149. return;
  150. }
  151. edge->color(EXTERNAL_COLOR);
  152. edge->twin()->color(EXTERNAL_COLOR);
  153. const VD::vertex_type* v = edge->vertex1();
  154. if (v == NULL || !edge->is_primary()) {
  155. return;
  156. }
  157. v->color(EXTERNAL_COLOR);
  158. const VD::edge_type* e = v->incident_edge();
  159. do {
  160. color_exterior(e);
  161. e = e->rot_next();
  162. } while (e != v->incident_edge());
  163. }
  164. void update_view_port() {
  165. glMatrixMode(GL_PROJECTION);
  166. glLoadIdentity();
  167. rect_type view_rect = brect_;
  168. deconvolve(view_rect, shift_);
  169. glOrtho(xl(view_rect), xh(view_rect),
  170. yl(view_rect), yh(view_rect),
  171. -1.0, 1.0);
  172. glMatrixMode(GL_MODELVIEW);
  173. }
  174. void draw_points() {
  175. // Draw input points and endpoints of the input segments.
  176. glColor3f(0.0f, 0.5f, 1.0f);
  177. glPointSize(9);
  178. glBegin(GL_POINTS);
  179. for (std::size_t i = 0; i < point_data_.size(); ++i) {
  180. point_type point = point_data_[i];
  181. deconvolve(point, shift_);
  182. glVertex2f(point.x(), point.y());
  183. }
  184. for (std::size_t i = 0; i < segment_data_.size(); ++i) {
  185. point_type lp = low(segment_data_[i]);
  186. lp = deconvolve(lp, shift_);
  187. glVertex2f(lp.x(), lp.y());
  188. point_type hp = high(segment_data_[i]);
  189. hp = deconvolve(hp, shift_);
  190. glVertex2f(hp.x(), hp.y());
  191. }
  192. glEnd();
  193. }
  194. void draw_segments() {
  195. // Draw input segments.
  196. glColor3f(0.0f, 0.5f, 1.0f);
  197. glLineWidth(2.7f);
  198. glBegin(GL_LINES);
  199. for (std::size_t i = 0; i < segment_data_.size(); ++i) {
  200. point_type lp = low(segment_data_[i]);
  201. lp = deconvolve(lp, shift_);
  202. glVertex2f(lp.x(), lp.y());
  203. point_type hp = high(segment_data_[i]);
  204. hp = deconvolve(hp, shift_);
  205. glVertex2f(hp.x(), hp.y());
  206. }
  207. glEnd();
  208. }
  209. void draw_vertices() {
  210. // Draw voronoi vertices.
  211. glColor3f(0.0f, 0.0f, 0.0f);
  212. glPointSize(6);
  213. glBegin(GL_POINTS);
  214. for (const_vertex_iterator it = vd_.vertices().begin();
  215. it != vd_.vertices().end(); ++it) {
  216. if (internal_edges_only_ && (it->color() == EXTERNAL_COLOR)) {
  217. continue;
  218. }
  219. point_type vertex(it->x(), it->y());
  220. vertex = deconvolve(vertex, shift_);
  221. glVertex2f(vertex.x(), vertex.y());
  222. }
  223. glEnd();
  224. }
  225. void draw_edges() {
  226. // Draw voronoi edges.
  227. glColor3f(0.0f, 0.0f, 0.0f);
  228. glLineWidth(1.7f);
  229. for (const_edge_iterator it = vd_.edges().begin();
  230. it != vd_.edges().end(); ++it) {
  231. if (primary_edges_only_ && !it->is_primary()) {
  232. continue;
  233. }
  234. if (internal_edges_only_ && (it->color() == EXTERNAL_COLOR)) {
  235. continue;
  236. }
  237. std::vector<point_type> samples;
  238. if (!it->is_finite()) {
  239. clip_infinite_edge(*it, &samples);
  240. } else {
  241. point_type vertex0(it->vertex0()->x(), it->vertex0()->y());
  242. samples.push_back(vertex0);
  243. point_type vertex1(it->vertex1()->x(), it->vertex1()->y());
  244. samples.push_back(vertex1);
  245. if (it->is_curved()) {
  246. sample_curved_edge(*it, &samples);
  247. }
  248. }
  249. glBegin(GL_LINE_STRIP);
  250. for (std::size_t i = 0; i < samples.size(); ++i) {
  251. point_type vertex = deconvolve(samples[i], shift_);
  252. glVertex2f(vertex.x(), vertex.y());
  253. }
  254. glEnd();
  255. }
  256. }
  257. void clip_infinite_edge(
  258. const edge_type& edge, std::vector<point_type>* clipped_edge) {
  259. const cell_type& cell1 = *edge.cell();
  260. const cell_type& cell2 = *edge.twin()->cell();
  261. point_type origin, direction;
  262. // Infinite edges could not be created by two segment sites.
  263. if (cell1.contains_point() && cell2.contains_point()) {
  264. point_type p1 = retrieve_point(cell1);
  265. point_type p2 = retrieve_point(cell2);
  266. origin.x((p1.x() + p2.x()) * 0.5);
  267. origin.y((p1.y() + p2.y()) * 0.5);
  268. direction.x(p1.y() - p2.y());
  269. direction.y(p2.x() - p1.x());
  270. } else {
  271. origin = cell1.contains_segment() ?
  272. retrieve_point(cell2) :
  273. retrieve_point(cell1);
  274. segment_type segment = cell1.contains_segment() ?
  275. retrieve_segment(cell1) :
  276. retrieve_segment(cell2);
  277. coordinate_type dx = high(segment).x() - low(segment).x();
  278. coordinate_type dy = high(segment).y() - low(segment).y();
  279. if ((low(segment) == origin) ^ cell1.contains_point()) {
  280. direction.x(dy);
  281. direction.y(-dx);
  282. } else {
  283. direction.x(-dy);
  284. direction.y(dx);
  285. }
  286. }
  287. coordinate_type side = xh(brect_) - xl(brect_);
  288. coordinate_type koef =
  289. side / (std::max)(fabs(direction.x()), fabs(direction.y()));
  290. if (edge.vertex0() == NULL) {
  291. clipped_edge->push_back(point_type(
  292. origin.x() - direction.x() * koef,
  293. origin.y() - direction.y() * koef));
  294. } else {
  295. clipped_edge->push_back(
  296. point_type(edge.vertex0()->x(), edge.vertex0()->y()));
  297. }
  298. if (edge.vertex1() == NULL) {
  299. clipped_edge->push_back(point_type(
  300. origin.x() + direction.x() * koef,
  301. origin.y() + direction.y() * koef));
  302. } else {
  303. clipped_edge->push_back(
  304. point_type(edge.vertex1()->x(), edge.vertex1()->y()));
  305. }
  306. }
  307. void sample_curved_edge(
  308. const edge_type& edge,
  309. std::vector<point_type>* sampled_edge) {
  310. coordinate_type max_dist = 1E-3 * (xh(brect_) - xl(brect_));
  311. point_type point = edge.cell()->contains_point() ?
  312. retrieve_point(*edge.cell()) :
  313. retrieve_point(*edge.twin()->cell());
  314. segment_type segment = edge.cell()->contains_point() ?
  315. retrieve_segment(*edge.twin()->cell()) :
  316. retrieve_segment(*edge.cell());
  317. voronoi_visual_utils<coordinate_type>::discretize(
  318. point, segment, max_dist, sampled_edge);
  319. }
  320. point_type retrieve_point(const cell_type& cell) {
  321. source_index_type index = cell.source_index();
  322. source_category_type category = cell.source_category();
  323. if (category == SOURCE_CATEGORY_SINGLE_POINT) {
  324. return point_data_[index];
  325. }
  326. index -= point_data_.size();
  327. if (category == SOURCE_CATEGORY_SEGMENT_START_POINT) {
  328. return low(segment_data_[index]);
  329. } else {
  330. return high(segment_data_[index]);
  331. }
  332. }
  333. segment_type retrieve_segment(const cell_type& cell) {
  334. source_index_type index = cell.source_index() - point_data_.size();
  335. return segment_data_[index];
  336. }
  337. point_type shift_;
  338. std::vector<point_type> point_data_;
  339. std::vector<segment_type> segment_data_;
  340. rect_type brect_;
  341. VB vb_;
  342. VD vd_;
  343. bool brect_initialized_;
  344. bool primary_edges_only_;
  345. bool internal_edges_only_;
  346. };
  347. class MainWindow : public QWidget {
  348. Q_OBJECT
  349. public:
  350. MainWindow() {
  351. glWidget_ = new GLWidget();
  352. file_dir_ = QDir(QDir::currentPath(), tr("*.txt"));
  353. file_name_ = tr("");
  354. QHBoxLayout* centralLayout = new QHBoxLayout;
  355. centralLayout->addWidget(glWidget_);
  356. centralLayout->addLayout(create_file_layout());
  357. setLayout(centralLayout);
  358. update_file_list();
  359. setWindowTitle(tr("Voronoi Visualizer"));
  360. layout()->setSizeConstraint(QLayout::SetFixedSize);
  361. }
  362. private slots:
  363. void primary_edges_only() {
  364. glWidget_->show_primary_edges_only();
  365. }
  366. void internal_edges_only() {
  367. glWidget_->show_internal_edges_only();
  368. }
  369. void browse() {
  370. QString new_path = QFileDialog::getExistingDirectory(
  371. 0, tr("Choose Directory"), file_dir_.absolutePath());
  372. if (new_path.isEmpty()) {
  373. return;
  374. }
  375. file_dir_.setPath(new_path);
  376. update_file_list();
  377. }
  378. void build() {
  379. file_name_ = file_list_->currentItem()->text();
  380. QString file_path = file_dir_.filePath(file_name_);
  381. message_label_->setText("Building...");
  382. glWidget_->build(file_path);
  383. message_label_->setText("Double click the item to build voronoi diagram:");
  384. setWindowTitle(tr("Voronoi Visualizer - ") + file_path);
  385. }
  386. void print_scr() {
  387. if (!file_name_.isEmpty()) {
  388. QImage screenshot = glWidget_->grabFrameBuffer(true);
  389. QString output_file = file_dir_.absolutePath() + tr("/") +
  390. file_name_.left(file_name_.indexOf('.')) + tr(".png");
  391. screenshot.save(output_file, 0, -1);
  392. }
  393. }
  394. private:
  395. QGridLayout* create_file_layout() {
  396. QGridLayout* file_layout = new QGridLayout;
  397. message_label_ = new QLabel("Double click item to build voronoi diagram:");
  398. file_list_ = new QListWidget();
  399. file_list_->connect(file_list_,
  400. SIGNAL(itemDoubleClicked(QListWidgetItem*)),
  401. this,
  402. SLOT(build()));
  403. QCheckBox* primary_checkbox = new QCheckBox("Show primary edges only.");
  404. connect(primary_checkbox, SIGNAL(clicked()),
  405. this, SLOT(primary_edges_only()));
  406. QCheckBox* internal_checkbox = new QCheckBox("Show internal edges only.");
  407. connect(internal_checkbox, SIGNAL(clicked()),
  408. this, SLOT(internal_edges_only()));
  409. QPushButton* browse_button =
  410. new QPushButton(tr("Browse Input Directory"));
  411. connect(browse_button, SIGNAL(clicked()), this, SLOT(browse()));
  412. browse_button->setMinimumHeight(50);
  413. QPushButton* print_scr_button = new QPushButton(tr("Make Screenshot"));
  414. connect(print_scr_button, SIGNAL(clicked()), this, SLOT(print_scr()));
  415. print_scr_button->setMinimumHeight(50);
  416. file_layout->addWidget(message_label_, 0, 0);
  417. file_layout->addWidget(file_list_, 1, 0);
  418. file_layout->addWidget(primary_checkbox, 2, 0);
  419. file_layout->addWidget(internal_checkbox, 3, 0);
  420. file_layout->addWidget(browse_button, 4, 0);
  421. file_layout->addWidget(print_scr_button, 5, 0);
  422. return file_layout;
  423. }
  424. void update_file_list() {
  425. QFileInfoList list = file_dir_.entryInfoList();
  426. file_list_->clear();
  427. if (file_dir_.count() == 0) {
  428. return;
  429. }
  430. QFileInfoList::const_iterator it;
  431. for (it = list.begin(); it != list.end(); it++) {
  432. file_list_->addItem(it->fileName());
  433. }
  434. file_list_->setCurrentRow(0);
  435. }
  436. QDir file_dir_;
  437. QString file_name_;
  438. GLWidget* glWidget_;
  439. QListWidget* file_list_;
  440. QLabel* message_label_;
  441. };
  442. int main(int argc, char* argv[]) {
  443. QApplication app(argc, argv);
  444. MainWindow window;
  445. window.show();
  446. return app.exec();
  447. }
  448. #include "voronoi_visualizer.moc"