1 //#define CGAL_USE_BOOST_BIMAP
2
3 #include <fstream>
4 #include <vector>
5
6 // CGAL headers
7
8 #include "svd-typedefs.h"
9 #include <boost/config.hpp>
10 #include <boost/version.hpp>
11 #include <CGAL/Timer.h>
12
13 // Qt headers
14 #include <QtGui>
15 #include <QString>
16 #include <QActionGroup>
17 #include <QFileDialog>
18 #include <QInputDialog>
19 #include <QDragEnterEvent>
20 #include <QDropEvent>
21 #include <QMessageBox>
22 #include <QGraphicsLineItem>
23
24 // GraphicsView items and event filters (input classes)
25 #include <CGAL/Qt/GraphicsViewPolylineInput.h>
26 #include <CGAL/Qt/SegmentDelaunayGraphGraphicsItem.h>
27 #include <CGAL/Constraints_loader.h>
28 #if BOOST_VERSION >= 105600 && (! defined(BOOST_GCC) || BOOST_GCC >= 40500)
29 #include <CGAL/IO/WKT.h>
30 #endif
31 //#include <CGAL/Qt/Converter.h>
32
33 // the two base classes
34 #include "ui_Segment_voronoi_2.h"
35 #include <CGAL/Qt/DemosMainWindow.h>
36
37 // for viewportsBbox(QGraphicsScene*)
38 #include <CGAL/Qt/utility.h>
39
40 typedef Rep K;
41 typedef SDG_2 SVD;
42
43 typedef SVD::Vertex_handle Vertex_handle;
44
45
46
47 class MainWindow :
48 public CGAL::Qt::DemosMainWindow,
49 public Ui::Segment_voronoi_2
50 {
51 Q_OBJECT
52
53 private:
54 SVD svd;
55 QGraphicsScene scene;
56 std::list<Point_2> seeds;
57
58 CGAL::Qt::SegmentDelaunayGraphGraphicsItem<SVD> * sdggi;
59
60 CGAL::Qt::GraphicsViewPolylineInput<K> * pi;
61
62 public:
63 MainWindow();
64
65 private:
66 template <typename Iterator>
insert_polyline(Iterator b,Iterator e)67 void insert_polyline(Iterator b, Iterator e)
68 {
69 Point_2 p, q;
70 SVD::Vertex_handle vh, wh;
71 Iterator it = b;
72 vh = svd.insert(*it);
73 p = *it;
74 ++it;
75 for(; it != e; ++it){
76 q = *it;
77 if(p != q){
78 wh = svd.insert(*it);
79 svd.insert(vh,wh);
80 vh = wh;
81 p = q;
82 } else {
83 std::cout << "duplicate point: " << p << std::endl;
84 }
85 }
86 Q_EMIT( changed());
87 }
88
89 protected Q_SLOTS:
90 virtual void open(QString);
91
92 public Q_SLOTS:
93
94 void processInput(CGAL::Object o);
95
96 void on_actionInsertPolyline_toggled(bool checked);
97
98 void on_actionClear_triggered();
99
100 void on_actionRecenter_triggered();
101
102 void on_actionLoadSegments_triggered();
103
104 void loadPolygonConstraints(QString);
105
106 void loadEdgConstraints(QString);
107 void loadWKTConstraints(QString fileName);
108
109 Q_SIGNALS:
110 void changed();
111 };
112
113
MainWindow()114 MainWindow::MainWindow()
115 : DemosMainWindow()
116 {
117 setupUi(this);
118
119 this->graphicsView->setAcceptDrops(false);
120
121 // Add a GraphicItem for the SVD triangulation
122 sdggi = new CGAL::Qt::SegmentDelaunayGraphGraphicsItem<SVD>(&svd);
123 QColor segmentColor(::Qt::blue);
124 QColor voronoiColor(::Qt::black);
125 segmentColor.setAlpha(150);
126 sdggi->setSegmentPen(QPen(segmentColor,0));
127 sdggi->setVoronoiPen(QPen(voronoiColor,0));
128
129 QObject::connect(this, SIGNAL(changed()),
130 sdggi, SLOT(modelChanged()));
131
132 sdggi->setVerticesPen(QPen(Qt::red, 3, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
133
134 sdggi->setZValue(-1);
135 scene.addItem(sdggi);
136
137 // Setup input handlers. They get events before the scene gets them
138 // and the input they generate is passed to the triangulation with
139 // the signal/slot mechanism
140 pi = new CGAL::Qt::GraphicsViewPolylineInput<K>(this, &scene, 0, true); // inputs polylines which are not closed
141 QObject::connect(pi, SIGNAL(generate(CGAL::Object)),
142 this, SLOT(processInput(CGAL::Object)));
143
144
145
146 //
147 // Manual handling of actions
148 //
149 QObject::connect(this->actionQuit, SIGNAL(triggered()),
150 this, SLOT(close()));
151
152 // We put mutually exclusive actions in an QActionGroup
153 QActionGroup* ag = new QActionGroup(this);
154 ag->addAction(this->actionInsertPolyline);
155
156 // Check two actions
157 this->actionInsertPolyline->setChecked(true);
158 this->actionShowVoronoi->setChecked(true);
159 this->actionShowConstraints->setChecked(true);
160
161 //
162 // Setup the scene and the view
163 //
164 scene.setItemIndexMethod(QGraphicsScene::NoIndex);
165 scene.setSceneRect(-100, -100, 100, 100);
166 this->graphicsView->setScene(&scene);
167 this->graphicsView->setMouseTracking(true);
168
169 // Turn the vertical axis upside down
170 this->graphicsView->scale(1, -1);
171
172 // The navigation adds zooming and translation functionality to the
173 // QGraphicsView
174 this->addNavigation(this->graphicsView);
175
176 this->setupStatusBar();
177 this->setupOptionsMenu();
178 this->addAboutDemo(":/cgal/help/about_Segment_voronoi_2.html");
179 this->addAboutCGAL();
180
181 this->addRecentFiles(this->menuFile, this->actionQuit);
182 connect(this, SIGNAL(openRecentFile(QString)),
183 this, SLOT(open(QString)));
184 }
185
186
187 void
processInput(CGAL::Object o)188 MainWindow::processInput(CGAL::Object o)
189 {
190
191 std::list<Point_2> points;
192 if(CGAL::assign(points, o)){
193 if(points.size() == 1) {
194 svd.insert(points.front());
195 }
196 else {
197 /*
198 std::cout.precision(12);
199 std::cout << points.size() << std::endl;
200 for( std::list<Point_2>::iterator it = points.begin(); it != points.end(); ++it){
201 std::cout << *it << std::endl;
202 }
203 */
204 insert_polyline(points.begin(), points.end());
205 }
206 }
207
208
209 Q_EMIT( changed());
210 }
211
212
213 /*
214 * Qt Automatic Connections
215 * https://doc.qt.io/qt-5/designer-using-a-ui-file.html#automatic-connections
216 *
217 * setupUi(this) generates connections to the slots named
218 * "on_<action_name>_<signal_name>"
219 */
220 void
on_actionInsertPolyline_toggled(bool checked)221 MainWindow::on_actionInsertPolyline_toggled(bool checked)
222 {
223 if(checked){
224 scene.installEventFilter(pi);
225 } else {
226 scene.removeEventFilter(pi);
227 }
228 }
229
230
231
232 void
on_actionClear_triggered()233 MainWindow::on_actionClear_triggered()
234 {
235 svd.clear();
236 Q_EMIT( changed());
237 }
238
239
240 void
open(QString fileName)241 MainWindow::open(QString fileName)
242 {
243 if(! fileName.isEmpty()){
244 if(fileName.endsWith(".polygons.cgal")){
245 loadPolygonConstraints(fileName);
246 this->addToRecentFiles(fileName);
247 } else if(fileName.endsWith(".edg")){
248 loadEdgConstraints(fileName);
249 this->addToRecentFiles(fileName);
250 } else if(fileName.endsWith(".wkt", Qt::CaseInsensitive)){
251 #if BOOST_VERSION >= 105600 && (! defined(BOOST_GCC) || BOOST_GCC >= 40500)
252 loadWKTConstraints(fileName);
253 this->addToRecentFiles(fileName);
254 #endif
255 }
256 }
257 }
258
259 void
on_actionLoadSegments_triggered()260 MainWindow::on_actionLoadSegments_triggered()
261 {
262 QString fileName = QFileDialog::getOpenFileName(this,
263 tr("Open Constraint File"),
264 ".",
265 tr("Edge files (*.edg);;"
266 "Polyline files (*.polygons.cgal);;"
267 #if BOOST_VERSION >= 105600 && (! defined(BOOST_GCC) || BOOST_GCC >= 40500)
268 "WKT files (*.wkt *.WKT)"
269 #endif
270 ));
271 open(fileName);
272 }
273
274 void
loadPolygonConstraints(QString fileName)275 MainWindow::loadPolygonConstraints(QString fileName)
276 {
277 K::Point_2 p,q, first;
278 SVD::Vertex_handle vp, vq, vfirst;
279 std::ifstream ifs(qPrintable(fileName));
280 int n;
281 while(ifs >> n){
282 ifs >> first;
283 p = first;
284 vfirst = vp = svd.insert(p);
285 n--;
286 while(n--){
287 ifs >> q;
288 vq = svd.insert(q, vp);
289 svd.insert(vp,vq);
290 p = q;
291 vp = vq;
292 }
293 svd.insert(vp, vfirst);
294 }
295
296
297 Q_EMIT( changed());
298 actionRecenter->trigger();
299 }
300
301
302 void
loadEdgConstraints(QString fileName)303 MainWindow::loadEdgConstraints(QString fileName)
304 {
305 // wait cursor
306 QApplication::setOverrideCursor(Qt::WaitCursor);
307 CGAL::Timer tim;
308 tim.start();
309 std::ifstream ifs(qPrintable(fileName));
310 bool first=true;
311 int n;
312 ifs >> n;
313
314 K::Point_2 p,q, qold(0,0); // Initialize qold, as otherwise some g++ issue a unjustified warning
315
316 SVD::Vertex_handle vp, vq, vqold;
317 while(ifs >> p) {
318 ifs >> q;
319 if(p == q){
320 continue;
321 }
322 if((!first) && (p == qold)){
323 vp = vqold;
324 } else {
325 vp = svd.insert(p);
326 }
327 vq = svd.insert(q, vp);
328 svd.insert(vp,vq);
329 qold = q;
330 vqold = vq;
331 first = false;
332 }
333
334
335 tim.stop();
336 statusBar()->showMessage(QString("Insertion took %1 seconds").arg(tim.time()), 2000);
337 // default cursor
338 QApplication::restoreOverrideCursor();
339 Q_EMIT( changed());
340 actionRecenter->trigger();
341 }
342
343 void
loadWKTConstraints(QString fileName)344 MainWindow::loadWKTConstraints(QString
345 #if BOOST_VERSION >= 105600 && (! defined(BOOST_GCC) || BOOST_GCC >= 40500)
346 fileName
347 #endif
348 )
349 {
350 #if BOOST_VERSION >= 105600 && (! defined(BOOST_GCC) || BOOST_GCC >= 40500)
351 typedef CGAL::Polygon_with_holes_2<K> Polygon;
352 typedef std::vector<K::Point_2> LineString;
353
354 //Multipolygon
355 K::Point_2 p,q, first;
356 SVD::Vertex_handle vp, vq, vfirst;
357 std::ifstream ifs(qPrintable(fileName));
358 do{
359 std::vector<Polygon> polygons;
360 CGAL::IO::read_multi_polygon_WKT(ifs, polygons);
361 for(const Polygon& poly : polygons)
362 {
363 if(poly.outer_boundary().is_empty())
364 continue;
365 Polygon::General_polygon_2::const_iterator it
366 =poly.outer_boundary().begin();
367 first = *(it++);
368 p = first;
369 vfirst = vp = svd.insert(p);
370 for(; it !=
371 poly.outer_boundary().end();
372 ++it){
373 q = *it;
374 vq = svd.insert(q, vp);
375 svd.insert(vp,vq);
376 p = q;
377 vp = vq;
378 }
379 if(vp != vfirst)
380 svd.insert(vp, vfirst);
381 }
382 }while(ifs.good() && !ifs.eof());
383 //MultiLineString
384 ifs.clear();
385 ifs.seekg(ifs.beg);
386 K::Point_2 qold(0,0); // Initialize qold, as otherwise some g++ issue a unjustified warning
387 SVD::Vertex_handle vqold;
388 do{
389 std::vector<LineString > linestrings;
390 CGAL::IO::read_multi_linestring_WKT(ifs, linestrings);
391 for(const LineString& ls : linestrings)
392 {
393 bool first_pass=true;
394 LineString::const_iterator it = ls.begin();
395 for(; it!=ls.end(); ++it){
396 p = *it++;
397 q = *it;
398 if(p == q){
399 continue;
400 }
401 if((!first_pass) && (p == qold)){
402 vp = vqold;
403 } else {
404 vp = svd.insert(p);
405 }
406 vq = svd.insert(q, vp);
407 if(vp != vq)
408 svd.insert(vp,vq);
409 qold = q;
410 vqold = vq;
411 first_pass = false;
412 }
413 }
414 }while(ifs.good() && !ifs.eof());
415 Q_EMIT( changed());
416 actionRecenter->trigger();
417 #endif
418 }
419
420 void
on_actionRecenter_triggered()421 MainWindow::on_actionRecenter_triggered()
422 {
423 this->graphicsView->setSceneRect(sdggi->boundingRect());
424 this->graphicsView->fitInView(sdggi->boundingRect(), Qt::KeepAspectRatio);
425 }
426
427 #include "Segment_voronoi_2.moc"
428 #include <CGAL/Qt/resources.h>
429
main(int argc,char ** argv)430 int main(int argc, char **argv)
431 {
432 QApplication app(argc, argv);
433
434 app.setOrganizationDomain("geometryfactory.com");
435 app.setOrganizationName("GeometryFactory");
436 app.setApplicationName("Segment Voronoi 2 demo");
437
438 // Import resources from libCGAL (Qt5).
439 CGAL_QT_INIT_RESOURCES;
440
441 MainWindow mainWindow;
442 mainWindow.show();
443 return app.exec();
444 }
445