diff --git a/GraphicsView/demo/Triangulation_2/Boolean_2.cpp b/GraphicsView/demo/Triangulation_2/Boolean_2.cpp new file mode 100644 index 000000000000..b163eb14ad44 --- /dev/null +++ b/GraphicsView/demo/Triangulation_2/Boolean_2.cpp @@ -0,0 +1,319 @@ +#include +// CGAL headers +#include +#include +#include +#include +#include +#include +#include + +// Qt headers +#include +#include +#include +#include +#include + +// GraphicsView items and event filters (input classes) +#include +#include +#include +#include +#include +#include + +// the two base classes +#include "ui_Boolean_2.h" +#include + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef K::Point_2 Point_2; +typedef K::Segment_2 Segment_2; +typedef K::Line_2 Line_2; + +typedef CGAL::Polygon_2 Polygon2; +typedef CGAL::Polygon_with_holes_2 Polygon_with_holes_2; +typedef CGAL::Multipolygon_with_holes_2 Multipolygon_with_holes_2; + +typedef CGAL::Triangulations::Boolean Boolean; +typedef std::shared_ptr PolygonPtr ; + +typedef std::vector PolygonPtr_vector ; + +class MainWindow : + public CGAL::Qt::DemosMainWindow, + public Ui::Polygon_2 +{ + Q_OBJECT + +private: + + bool add_2_A = true; + CGAL::Qt::Converter convert; + Polygon2 poly; + Polygon_with_holes_2 pwhA, pwhB; + Multipolygon_with_holes_2 mpwhA, mpwhB, mpwhC; + QGraphicsScene scene; + + CGAL::Qt::MultipolygonWithHolesGraphicsItem *mpwhAgi, *mpwhBgi, *mpwhCgi; + + CGAL::Qt::GraphicsViewPolygonWithHolesInput * pi; + + +public: + MainWindow(); + +public Q_SLOTS: + + void processInput(CGAL::Object o); + + void on_actionClear_triggered(); + + void on_actionLoadPolygon_triggered(); + void on_actionSavePolygon_triggered(); + + void on_actionRecenter_triggered(); + + void on_actionAdd_to_A_triggered(); + void on_actionAdd_to_B_triggered(); + + void on_actionCreateInputPolygon_toggled(bool); + + void clear(); + + virtual void open(QString); +Q_SIGNALS: + void changed(); +}; + + +MainWindow::MainWindow() + : DemosMainWindow() +{ + setupUi(this); + + this->graphicsView->setAcceptDrops(false); + + mpwhAgi = new CGAL::Qt::MultipolygonWithHolesGraphicsItem(&mpwhA); + mpwhAgi->setBrush(QBrush(::Qt::green)); + mpwhAgi->setZValue(3); + QObject::connect(this, SIGNAL(changed()), + mpwhAgi, SLOT(modelChanged())); + + scene.addItem(mpwhAgi); + + mpwhBgi = new CGAL::Qt::MultipolygonWithHolesGraphicsItem(&mpwhB); + mpwhBgi->setZValue(4); + mpwhBgi->setBrush(QBrush(QColor(255, 0, 0, 100))); + QObject::connect(this, SIGNAL(changed()), + mpwhBgi, SLOT(modelChanged())); + + scene.addItem(mpwhBgi); + + mpwhCgi = new CGAL::Qt::MultipolygonWithHolesGraphicsItem(&mpwhC); + mpwhCgi->setZValue(5); + mpwhCgi->setBrush(QBrush(QColor(211, 211, 211, 150))); + QObject::connect(this, SIGNAL(changed()), + mpwhCgi, SLOT(modelChanged())); + + scene.addItem(mpwhCgi); + + + // Setup input handlers. They get events before the scene gets them + // pi = new CGAL::Qt::GraphicsViewPolylineInput(this, &scene, 0, true); + pi = new CGAL::Qt::GraphicsViewPolygonWithHolesInput(this, &scene); + pi->setZValue(10); + + this->actionCreateInputPolygon->setChecked(true); + QObject::connect(pi, SIGNAL(generate(CGAL::Object)), + this, SLOT(processInput(CGAL::Object))); + + // + // Manual handling of actions + // + QObject::connect(this->actionQuit, SIGNAL(triggered()), + this, SLOT(close())); + + + // + // Setup the scene and the view + // + scene.setItemIndexMethod(QGraphicsScene::NoIndex); + scene.setSceneRect(-100, -100, 100, 100); + this->graphicsView->setScene(&scene); + this->graphicsView->setMouseTracking(true); + + // Turn the vertical axis upside down + this->graphicsView->scale(1, -1); + + // The navigation adds zooming and translation functionality to the + // QGraphicsView + this->addNavigation(this->graphicsView); + + this->setupStatusBar(); + this->setupOptionsMenu(); + this->addAboutDemo(":/cgal/help/about_Polygon_2.html"); + this->addAboutCGAL(); + this->setupExportSVG(action_Export_SVG, graphicsView); + + this->addRecentFiles(this->menuFile, this->actionQuit); + connect(this, SIGNAL(openRecentFile(QString)), + this, SLOT(open(QString))); +} + + +void +MainWindow::processInput(CGAL::Object o) +{ + if(add_2_A){ + if(assign(pwhA, o)){ + mpwhA.add_polygon_with_holes(pwhA); + } + }else{ + if(assign(pwhB, o)){ + mpwhB.add_polygon_with_holes(pwhB); + } + } + if((! mpwhA.is_empty()) && (! mpwhB.is_empty())){ + Boolean boolean; + boolean.insert(mpwhA, mpwhB); + mpwhC = boolean([](bool a, bool b){ return a && b;}); + } + Q_EMIT( changed()); +} + +/* + * Qt Automatic Connections + * https://doc.qt.io/qt-5/designer-using-a-ui-file.html#automatic-connections + * + * setupUi(this) generates connections to the slots named + * "on__" + */ + +void +MainWindow::on_actionAdd_to_A_triggered() +{ + this->actionAdd_to_A->setEnabled(false); + this->actionAdd_to_B->setEnabled(true); + add_2_A = true; +} +void +MainWindow::on_actionAdd_to_B_triggered() +{ + this->actionAdd_to_B->setEnabled(false); + this->actionAdd_to_A->setEnabled(true); + add_2_A = false; +} + +void +MainWindow::on_actionClear_triggered() +{ + pwhA.clear(); + mpwhA.clear(); + clear(); + this->actionCreateInputPolygon->setChecked(true); + Q_EMIT( changed()); +} + + +void +MainWindow::on_actionLoadPolygon_triggered() +{ + QString fileName = QFileDialog::getOpenFileName(this, + tr("Open Polygon File"), + ".", + tr( "WKT files (*.wkt *.WKT);;" + "All file (*)")); + if(! fileName.isEmpty()){ + open(fileName); + } +} + +void +MainWindow::open(QString fileName) +{ + this->actionCreateInputPolygon->setChecked(false); + std::ifstream ifs(qPrintable(fileName)); + pwhA.clear(); + if(fileName.endsWith(".wkt", Qt::CaseInsensitive)) + { + CGAL::IO::read_polygon_WKT(ifs, pwhA); + } + else + { + std::cout << "not supported" << std::endl; + } + clear(); + + this->addToRecentFiles(fileName); + Q_EMIT( changed()); +} + + +void +MainWindow::on_actionSavePolygon_triggered() +{ + QString fileName = QFileDialog::getSaveFileName(this, + tr("Save Polygon"), + ".", + tr( "WKT files (*.wkt *.WKT);;" + "All file (*)")); + if(! fileName.isEmpty()){ + std::ofstream ofs(qPrintable(fileName)); + if(fileName.endsWith(".wkt", Qt::CaseInsensitive)) + { + CGAL::IO::write_polygon_WKT(ofs, pwhA); + } + else{ + std::cout << "not supported" << std::endl; + } + } +} + + +void +MainWindow::on_actionCreateInputPolygon_toggled(bool checked) +{ + // poly.clear(); + clear(); + if(checked){ + scene.installEventFilter(pi); + } else { + scene.removeEventFilter(pi); + } + Q_EMIT( changed()); +} + +void +MainWindow::on_actionRecenter_triggered() +{ + this->graphicsView->setSceneRect(mpwhAgi->boundingRect()); + this->graphicsView->fitInView(mpwhAgi->boundingRect(), Qt::KeepAspectRatio); +} + +void +MainWindow::clear() +{} + + +#include "Boolean_2.moc" +#include + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + app.setOrganizationDomain("geometryfactory.com"); + app.setOrganizationName("GeometryFactory"); + app.setApplicationName("2D Boolean Operations"); + + // Import resources from libCGAL (Qt6). + // See https://doc.qt.io/qt-5/qdir.html#Q_INIT_RESOURCE + CGAL_QT_INIT_RESOURCES; + Q_INIT_RESOURCE(Boolean_2); + + MainWindow mainWindow; + mainWindow.show(); + return app.exec(); +} diff --git a/GraphicsView/demo/Triangulation_2/Boolean_2.qrc b/GraphicsView/demo/Triangulation_2/Boolean_2.qrc new file mode 100644 index 000000000000..010beb6b83ac --- /dev/null +++ b/GraphicsView/demo/Triangulation_2/Boolean_2.qrc @@ -0,0 +1,6 @@ + + + ../resources/about_CGAL.html + about_Boolean_2.html + + diff --git a/GraphicsView/demo/Triangulation_2/Boolean_2.ui b/GraphicsView/demo/Triangulation_2/Boolean_2.ui new file mode 100644 index 000000000000..e02fb69fd47a --- /dev/null +++ b/GraphicsView/demo/Triangulation_2/Boolean_2.ui @@ -0,0 +1,257 @@ + + + GeometryFactory + Polygon_2 + + + + 0 + 0 + 568 + 325 + + + + CGAL 2D Polygon + + + + :/cgal/logos/cgal_icon:/cgal/logos/cgal_icon + + + + + + + Qt::Horizontal + + + + + 2 + 0 + + + + Qt::StrongFocus + + + Qt::ScrollBarAlwaysOn + + + Qt::ScrollBarAlwaysOn + + + QGraphicsView::NoAnchor + + + + + + + + + + File Tools + + + TopToolBarArea + + + false + + + + + + + + Visualization Tools + + + TopToolBarArea + + + false + + + + + + + 0 + 0 + 568 + 22 + + + + + &File + + + + + + + + + + + + &Algorithms + + + + + + + + + + + + &About + + + + + About &CGAL + + + + + &Quit + + + Ctrl+Q + + + + + + :/cgal/fileToolbar/fileNew.png:/cgal/fileToolbar/fileNew.png + + + &Clear + + + Ctrl+C + + + + + + :/cgal/fileToolbar/fileOpen.png:/cgal/fileToolbar/fileOpen.png + + + &Load Polygon + + + Ctrl+L + + + + + + :/cgal/fileToolbar/fileSave.png:/cgal/fileToolbar/fileSave.png + + + &Save Polygon + + + Ctrl+S + + + + + + :/cgal/Input/zoom-best-fit:/cgal/Input/zoom-best-fit + + + Re&center the viewport + + + Ctrl+R + + + + + Y-monotone Partition + + + + + true + + + Create Input Polygon + + + + + Minkowski sum with itself + + + + + Inner Skeleton + + + + + Outer Offset + + + + + Optimal Convex Partition + + + + + Approximate Convex Partition + + + + + Linear Least Squares Fitting of Points + + + + + Linear Least Squares Fitting of Segments + + + + + Maximum Area Triangle + + + + + &Export SVG... + + + + + Add to A + + + + + false + + + Add to B + + + + + + + + + + + diff --git a/GraphicsView/demo/Triangulation_2/CMakeLists.txt b/GraphicsView/demo/Triangulation_2/CMakeLists.txt index 515dfca67812..187ce6476ddd 100644 --- a/GraphicsView/demo/Triangulation_2/CMakeLists.txt +++ b/GraphicsView/demo/Triangulation_2/CMakeLists.txt @@ -58,8 +58,20 @@ target_link_libraries(Regular_triangulation_2 PRIVATE CGAL::CGAL CGAL::CGAL_Qt6 add_to_cached_list(CGAL_EXECUTABLE_TARGETS Regular_triangulation_2) +#-------------------------------- +# The "Boolean" demo: Boolean_2 +#-------------------------------- + +qt_add_executable( + Boolean_2 Boolean_2.cpp + Boolean_2.ui Boolean_2.qrc) +target_link_libraries(Boolean_2 PRIVATE CGAL::CGAL CGAL::CGAL_Qt6 + Qt6::Widgets) + +add_to_cached_list(CGAL_EXECUTABLE_TARGETS Boolean_2) + include(${CGAL_MODULES_DIR}/CGAL_add_test.cmake) foreach(target Constrained_Delaunay_triangulation_2 Delaunay_triangulation_2 - Regular_triangulation_2) + Regular_triangulation_2 Boolean_2) cgal_add_compilation_test(${target}) endforeach() diff --git a/GraphicsView/demo/Triangulation_2/about_Boolean_2.html b/GraphicsView/demo/Triangulation_2/about_Boolean_2.html new file mode 100644 index 000000000000..b72bfadad229 --- /dev/null +++ b/GraphicsView/demo/Triangulation_2/about_Boolean_2.html @@ -0,0 +1,10 @@ + + +

2D Boolean Operations

+

Copyright © 2024 GeometryFactory

+

This application illustrates the 2D Boolean operations based on 2D triangulations + of CGAL

+

See also the online + manual.

+ + diff --git a/Triangulation_2/examples/Triangulation_2/CMakeLists.txt b/Triangulation_2/examples/Triangulation_2/CMakeLists.txt index 3e3aafa5f56a..ed6100f50a9d 100644 --- a/Triangulation_2/examples/Triangulation_2/CMakeLists.txt +++ b/Triangulation_2/examples/Triangulation_2/CMakeLists.txt @@ -16,7 +16,8 @@ foreach(cppfile ${cppfiles}) endforeach() if(CGAL_Qt6_FOUND) - target_link_libraries(constrained PUBLIC CGAL::CGAL_Basic_viewer) + target_link_libraries(boolean_constrained_plus PUBLIC CGAL::CGAL_Basic_viewer) + target_link_libraries(constrained PUBLIC CGAL::CGAL_Basic_viewer) target_link_libraries(draw_triangulation_2 PUBLIC CGAL::CGAL_Basic_viewer) target_link_libraries(polygon_triangulation PUBLIC CGAL::CGAL_Basic_viewer) target_link_libraries(star_conflict_zone PUBLIC CGAL::CGAL_Basic_viewer) diff --git a/Triangulation_2/examples/Triangulation_2/boolean_constrained_plus.cpp b/Triangulation_2/examples/Triangulation_2/boolean_constrained_plus.cpp new file mode 100644 index 000000000000..2b6748638e6c --- /dev/null +++ b/Triangulation_2/examples/Triangulation_2/boolean_constrained_plus.cpp @@ -0,0 +1,79 @@ +#include + +#include +#include + + +#include +#include +#include + +#include + + +#include +#include + +#include + +using K = CGAL::Exact_predicates_exact_constructions_kernel; +using Vector_2 = K::Vector_2; +using Transformation = K::Aff_transformation_2; +using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; +using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; + +int +main(int argc, char* argv[]) +{ + CGAL::Triangulations::Boolean bops; + + Multipolygon_with_holes_2 pA, pB; + if(argc == 2) { + std::ifstream in(argv[1]); + CGAL::IO::read_multi_polygon_WKT(in, pA); + + CGAL::Bbox_2 bb = pA.bbox(); + double w = bb.xmax() - bb.xmin(); + Vector_2 vec(w / 10.0, w / 10.0); + pB = CGAL::transform(Transformation(CGAL::TRANSLATION, vec), pA); + } + else if (argc == 3) { + { + std::ifstream in(argv[1]); + CGAL::IO::read_multi_polygon_WKT(in, pA); + } + { + std::ifstream in(argv[2]); + CGAL::IO::read_multi_polygon_WKT(in, pB); + } + } + else { + { + std::istringstream is("MULTIPOLYGON( ((0 0, 20 0, 20 30, 0 30, 0 0), (1 1, 1 2, 2 2, 2 1, 1 1 ) ) , (( 50 0, 60 0, 60 60, 50 60)) )"); + //std::istringstream is("MULTIPOLYGON( ((0 0, 2 0, 2 3, 0 3) ) )"); // (0.1 0.1, 0.1 0.4, 0.4 0.1) + CGAL::IO::read_multi_polygon_WKT(is, pA); + } + { + std::istringstream is("MULTIPOLYGON( ((10 1, 30 1, 30 2, 20 2, 20 4, 10 4)) )"); + //std::istringstream is("MULTIPOLYGON( ((2 1, 3 1, 3 2, 2 2)) "); + CGAL::IO::read_multi_polygon_WKT(is, pB); + } + } + + bops.insert(pA,pB); + Multipolygon_with_holes_2 mpwh = bops([](bool a, bool b){ return a || b;}); + std::ofstream out("result.wkt"); + CGAL::IO::write_multi_polygon_WKT(out, mpwh); + CGAL::draw(mpwh); + + /* + std::map map; + for(auto fh : bops.cdt.finite_face_handles()){ + map[fh] = fh->info().in_domain(0) || fh->info().in_domain(1); + assert(map[fh] == (fh->info().label != 0)); + } + + CGAL::draw(bops.cdt, boost::make_assoc_property_map(map)); + */ + return 0; +} diff --git a/Triangulation_2/include/CGAL/Triangulation_2/Boolean.h b/Triangulation_2/include/CGAL/Triangulation_2/Boolean.h new file mode 100644 index 000000000000..a0d75456ad26 --- /dev/null +++ b/Triangulation_2/include/CGAL/Triangulation_2/Boolean.h @@ -0,0 +1,410 @@ +// Copyright (c) 2024 GeometryFactory SARL (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Andreas Fabri + +#ifndef CGAL_TRIANGULATION_2_BOOLEAN_H +#define CGAL_TRIANGULATION_2_BOOLEAN_H + +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +namespace CGAL { +namespace Triangulations { + +/*! +\ingroup PkgTriangulation2Miscellaneous + +\tparam Kernel must be +*/ + +template +class Boolean { + +private: + struct FaceInfo { + + FaceInfo() + {} + + int label; + int nesting_level[2]; + bool processed; + + bool + in_domain(int i) const + { + return nesting_level[i] % 2 == 1; + } + + template + bool + in_domain(const Fct& fct) const + { + return fct(in_domain(0), in_domain(1)); + } + + }; + + using K = Kernel; + using Point_2 = typename K::Point_2; + using Polygon_2 = CGAL::Polygon_2; + using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; + using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; + + using Itag = std::conditional_t, Exact_predicates_tag, Exact_intersections_tag>; + using Vb = CGAL::Triangulation_vertex_base_2; + using Fbb = CGAL::Triangulation_face_base_with_info_2; + using Fb = CGAL::Constrained_triangulation_face_base_2; + using Tds = CGAL::Triangulation_data_structure_2; + using CDT = CGAL::Constrained_Delaunay_triangulation_2; + using CDTplus = CGAL::Constrained_triangulation_plus_2; + using Face_handle = typename CDTplus::Face_handle; + using Face_circulator = typename CDTplus::Face_circulator; + using Vertex_handle = typename CDTplus::Vertex_handle; + using Constraint_id = typename CDTplus::Constraint_id; + using Edge = typename CDTplus::Edge; + using Context = typename CDTplus::Context; + + + // @todo taken from Polygon_repair should be factorized + struct Polygon_less { + + bool + operator()(const Polygon_2& pa, const Polygon_2& pb) const + { + typename Polygon_2::Vertex_iterator va = pa.vertices_begin(); + typename Polygon_2::Vertex_iterator vb = pb.vertices_begin(); + while (va != pa.vertices_end() && vb != pb.vertices_end()) { + if (*va != *vb) return *va < *vb; + ++va; + ++vb; + } + if (vb == pb.vertices_end()) return false; + return true; + } + + }; + + + // @todo taken from Polygon_repair should be factorized + struct Polygon_with_holes_less { + Polygon_less pl; + + bool + operator()(const Polygon_with_holes_2& pa, const Polygon_with_holes_2& pb) const + { + if (pl(pa.outer_boundary(), pb.outer_boundary())) return true; + if (pl(pb.outer_boundary(), pa.outer_boundary())) return false; + typename Polygon_with_holes_2::Hole_const_iterator ha = pa.holes_begin(); + typename Polygon_with_holes_2::Hole_const_iterator hb = pb.holes_begin(); + while (ha != pa.holes_end() && hb != pb.holes_end()) { + if (pl(*ha, *hb)) return true; + if (pl(*hb, *ha)) return false; + } + if (hb == pb.holes_end()) return false; + return true; + } + + }; + + + CDTplus cdt; + std::set idA, idB; + +public: + +/*! +default constructor. +*/ + Boolean() = default; + + +/*! +sets the polygons as input of the %Boolean operation. +*/ + void + insert(const Multipolygon_with_holes_2& pA, const Multipolygon_with_holes_2& pB) + { + for(const auto& pwh : pA.polygons_with_holes()){ + Constraint_id cidA = cdt.insert_constraint(pwh.outer_boundary().vertices_begin(), pwh.outer_boundary().vertices_end(), true); + idA.insert(cidA); + for(auto const& hole : pwh.holes()){ + cidA = cdt.insert_constraint(hole.vertices_begin(), hole.vertices_end(), true); + idA.insert(cidA); + } + } + + for(const auto& pwh : pB.polygons_with_holes()){ + Constraint_id cidB = cdt.insert_constraint(pwh.outer_boundary().vertices_begin(), pwh.outer_boundary().vertices_end(), true); + idB.insert(cidB); + for(auto const& hole : pwh.holes()){ + cidB = cdt.insert_constraint(hole.vertices_begin(), hole.vertices_end(), true); + idB.insert(cidB); + } + } + + mark_domains(idA, 0); + mark_domains(idB, 1); + } + +private: + + void + mark_domains(Face_handle start, + int index, + std::list& border, + const std::set& cids, + int aorb) + { + if(start->info().nesting_level[aorb] != -1){ + return; + } + std::list queue; + queue.push_back(start); + + while(! queue.empty()){ + Face_handle fh = queue.front(); + queue.pop_front(); + if(fh->info().nesting_level[aorb] == -1){ + fh->info().nesting_level[aorb] = index; + for(int i = 0; i < 3; i++){ + Edge e(fh,i); + Face_handle n = fh->neighbor(i); + if(n->info().nesting_level[aorb] == -1){ + if(cdt.is_constrained(e)){ + bool found = false; + for(Context c : cdt.contexts(e.first->vertex(cdt.cw(e.second)), + e.first->vertex(cdt.ccw(e.second)))){ + if(cids.find(c.id()) != cids.end()){ + found = true; + break; + } + } + if (found) { + border.push_back(e); + } else { + queue.push_back(n); + } + }else{ + queue.push_back(n); + } + } + } + } + } + } + + + // this marks the domains for either the first or the second multipolygon + void + mark_domains(const std::set& cids, int aorb) + { + for(Face_handle f : cdt.all_face_handles()){ + f->info().nesting_level[aorb] = -1; + } + + std::list border; + mark_domains(cdt.infinite_face(), 0, border, cids, aorb); + + while(! border.empty()){ + Edge e = border.front(); + border.pop_front(); + Face_handle n = e.first->neighbor(e.second); + if(n->info().nesting_level[aorb] == -1){ + mark_domains(n, e.first->info().nesting_level[aorb]+1, border, cids, aorb); + } + } + } + + + template + void + label_domains(Face_handle start, int label, const Fct& fct) + { + std::list queue; + start->info().label = label; + queue.push_back(start); + + while(! queue.empty()){ + Face_handle fh = queue.front(); + queue.pop_front(); + + for(int i = 0; i < 3; i++){ + Face_handle n = fh->neighbor(i); + if(n->info().in_domain(fct)){ + if(n->info().label == 0){ + n->info().label = label; + queue.push_back(n); + } + } + } + } + } + + // this marks the domain for the Boolean operation applied on the two multipolygons + template + int + label_domains(const Fct& fct) + { + for (auto const face: cdt.all_face_handles()) { + face->info().processed = false; + face->info().label = 0; + } + int label = 1; + for (auto const face: cdt.all_face_handles()) { + if(face->info().in_domain(fct) && face->info().label == 0){ + label_domains(face, label, fct); + ++label; + } + } + return label; + } + + + + // @todo taken from Polygon_repair and adapted; might be factorized + // Reconstruct ring boundary starting from an edge (face + opposite vertex) that is part of it + template + void + reconstruct_ring(std::list& ring, + Face_handle face_adjacent_to_boundary, + int opposite_vertex, + const Fct& fct) + { + // Create ring + Face_handle current_face = face_adjacent_to_boundary; + int current_opposite_vertex = opposite_vertex; + CGAL_assertion(face_adjacent_to_boundary->info().in_domain(fct)); + do { + CGAL_assertion(current_face->info().in_domain(fct) == face_adjacent_to_boundary->info().in_domain(fct)); + current_face->info().processed = true; + Vertex_handle pivot_vertex = current_face->vertex(current_face->cw(current_opposite_vertex)); + // std::cout << "\tAdding point " << pivot_vertex->point() << std::endl; + ring.push_back(pivot_vertex->point()); + Face_circulator fc = cdt.incident_faces(pivot_vertex, current_face); + do { + ++fc; + } while (fc->info().label != current_face->info().label); + current_face = fc; + current_opposite_vertex = fc->cw(fc->index(pivot_vertex)); + } while (current_face != face_adjacent_to_boundary || + current_opposite_vertex != opposite_vertex); + + // Start at lexicographically smallest vertex + typename std::list::iterator smallest_vertex = ring.begin(); + for (typename std::list::iterator current_vertex = ring.begin(); + current_vertex != ring.end(); ++current_vertex) { + if (*current_vertex < *smallest_vertex) smallest_vertex = current_vertex; + } + if (ring.front() != *smallest_vertex) { + ring.splice(ring.begin(), ring, smallest_vertex, ring.end()); + } + } + + +public: + + // @todo taken from Polygon_repair and adapted; might be factorized + +/*! +performs the Boolean operation applying `fct` and returns the result as a multipolygon with holes. + +\tparam Fct must have the operator `bool operator()(bool, bool)`. +*/ + template + Multipolygon_with_holes_2 + operator()(const Fct& fct) + { + int number_of_polygons = label_domains(fct) - 1; + + Multipolygon_with_holes_2 mp; + std::vector polygons; // outer boundaries + std::vector> holes; // holes are ordered (per polygon) + polygons.resize(number_of_polygons); + holes.resize(number_of_polygons); + + for (auto const face: cdt.all_face_handles()) { + face->info().processed = false; + } + + /* + for (auto const face: cdt.all_face_handles()) { + std::cout << face->vertex(0)->point() << " " << face->vertex(1)->point() << " " << face->vertex(2)->point() << std::endl; + std::cout << "label = " << face->info().label << std::endl; + if(face->info().in_domain(fct)) std::cout << "in domain" << std::endl; else std::cout << "not in domain" << std::endl; + } + */ + for (auto const &face: cdt.finite_face_handles()) { + if (! face->info().in_domain(fct)) continue; // exterior triangle + if (face->info().processed) continue; // already reconstructed + for (int opposite_vertex = 0; opposite_vertex < 3; ++opposite_vertex) { + if (face->info().in_domain(fct) == face->neighbor(opposite_vertex)->info().in_domain(fct)) continue; // not adjacent to boundary + + // Reconstruct ring + std::list ring; + reconstruct_ring(ring, face, opposite_vertex, fct); + + // Put ring in polygons + Polygon_2 polygon; + polygon.reserve(ring.size()); + polygon.insert(polygon.vertices_end(), ring.begin(), ring.end()); + if (polygon.orientation() == CGAL::COUNTERCLOCKWISE) { + polygons[face->info().label-1] = std::move(polygon); + } else { + holes[face->info().label-1].insert(std::move(polygon)); + } break; + } + } + + // Create polygons with holes and put in multipolygon + std::set ordered_polygons; + for (std::size_t i = 0; i < polygons.size(); ++i) { + ordered_polygons.insert(Polygon_with_holes_2(std::move(polygons[i]), + std::make_move_iterator(holes[i].begin()), + std::make_move_iterator(holes[i].end()))); + } + for (auto const& polygon: ordered_polygons) { + mp.add_polygon_with_holes(std::move(polygon)); + } + return mp; + } + + +/*! +access to the underlying constrained triangulation. +*/ + const CDTplus& + triangulation() const + { + return cdt; + } + +}; + + +} // namespace Triangulations +} //namespace CGAL + +#endif CGAL_TRIANGULATION_2_BOOLEAN_H