Files
cgal/GraphicsView/demo/Triangulation_2/mesh_2_demo.cpp
T

1746 lines
54 KiB
C++

// Copyright (c) 2003-2006 INRIA Sophia-Antipolis (France).
// Copyright (c) 2026 GeometryFactory Sarl (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org); you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 3 of the License,
// or (at your option) any later version.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// Author(s) : Laurent Rineau
#include <CGAL/Bbox_2.h>
#include <CGAL/Constrained_Delaunay_triangulation_2.h>
#include <CGAL/Constrained_triangulation_2.h>
#include <CGAL/Constrained_voronoi_diagram_2.h>
#include <CGAL/Delaunay_mesh_face_base_2.h>
#include <CGAL/Delaunay_mesh_local_size_criteria_2.h>
#include <CGAL/Delaunay_mesher_2.h>
#include <CGAL/Delaunay_triangulation_2.h>
#include <CGAL/Filtered_kernel.h>
#include <CGAL/IO/File_poly.h>
#include <CGAL/IO/OBJ.h>
#include <CGAL/IO/write_VTU.h>
#include <CGAL/Iterator_range.h>
#include <CGAL/Mesh_2/Face_badness.h>
#include <CGAL/Object.h>
#include <CGAL/Qt/Converter.h>
#include <CGAL/Qt/DelaunayMeshTriangulationGraphicsItem.h>
#include <CGAL/Qt/DemosMainWindow.h>
#include <CGAL/Qt/GraphicsItem.h>
#include <CGAL/Qt/GraphicsViewInput.h>
#include <CGAL/Qt/GraphicsViewPolylineInput.h>
#include <CGAL/Qt/resources.h>
#include <CGAL/Qt/utility.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Triangulation_conformer_2.h>
#include <CGAL/Triangulation_data_structure_2.h>
#include <CGAL/Triangulation_vertex_base_2.h>
#include "DelaunayMeshInsertSeeds.h"
#include "TriangulationCircumcircle.h"
#include <QColor>
#include <QObject>
#include <QAction>
#include <QActionGroup>
#include <QApplication>
#include <QCheckBox>
#include <QColorDialog>
#include <QComboBox>
#include <QDialog>
#include <QDockWidget>
#include <QDoubleSpinBox>
#include <QFileDialog>
#include <QFormLayout>
#include <QGraphicsScene>
#include <QGraphicsSceneHoverEvent>
#include <QGraphicsView>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QInputDialog>
#include <QLabel>
#include <QLineEdit>
#include <QMainWindow>
#include <QMenu>
#include <QMenuBar>
#include <QMessageBox>
#include <QObject>
#include <QPainter>
#include <QPalette>
#include <QPushButton>
#include <QRegularExpression>
#include <QSettings>
#include <QSpinBox>
#include <QStatusBar>
#include <QString>
#include <QStringList>
#include <Qt>
#include <QTimer>
#include <QToolBar>
#include <QVBoxLayout>
#include <qgraphicsitem.h>
#include <qobjectdefs.h>
#include <qtmetamacros.h>
#include <algorithm>
#include <cstddef>
#include <fstream>
#include <iostream>
#include <iterator>
#include <limits>
#include <list>
#include <random>
#include <utility>
#include <vector>
using K1 = CGAL::Simple_cartesian<double>;
using Kernel = CGAL::Filtered_kernel<K1>;
struct K : public Kernel
{};
using FT = K::FT;
using Point_2 = K::Point_2;
using Segment_2 = K::Segment_2;
using Triangle_2 = K::Triangle_2;
using Vb = CGAL::Triangulation_vertex_base_2<K>;
using Fb = CGAL::Delaunay_mesh_face_base_2<K>;
using Tds = CGAL::Triangulation_data_structure_2<Vb, Fb>;
using CDT = CGAL::Constrained_Delaunay_triangulation_2<K, Tds, CGAL::Exact_predicates_tag>;
using Criteria = CGAL::Delaunay_mesh_local_size_criteria_2<CDT>;
using Mesher = CGAL::Delaunay_mesher_2<CDT, Criteria>;
using Vertex_handle = CDT::Vertex_handle;
using Face_handle = CDT::Face_handle;
using Edge = CDT::Edge;
template <class CDTType> void read_constraints(CDTType& t, std::istream& f) {
using Point = typename CDTType::Point;
t.clear();
int nedges = 0;
f >> nedges;
for(int n = 0; n < nedges; ++n) {
Point p1, p2;
f >> p1 >> p2;
t.insert_constraint(p1, p2);
}
}
template <class CDTType> void write_constraints(const CDTType& t, std::ostream& f) {
int number_of_constrained_edges = 0;
for(const auto& [face, edge_idx] : t.finite_edges()) {
if(face->is_constrained(edge_idx)) {
++number_of_constrained_edges;
}
}
f << number_of_constrained_edges << '\n';
for(const auto& [face, edge_idx] : t.finite_edges()) {
if(face->is_constrained(edge_idx)) {
f << face->vertex(t.cw(edge_idx))->point() << " " << face->vertex(t.ccw(edge_idx))->point() << '\n';
}
}
}
// Helper range adaptors for Mesher API that only provides begin/end pairs
namespace {
auto bad_faces_range(const Mesher* m) {
return CGAL::make_range(m->bad_faces_begin(), m->bad_faces_end());
}
auto encroached_edges_range(const Mesher* m) {
return CGAL::make_range(m->encroached_edges_begin(), m->encroached_edges_end());
}
template <typename Clusters> auto clusters_vertices_range(const Clusters& c) {
return CGAL::make_range(c.clusters_vertices_begin(), c.clusters_vertices_end());
}
QPen pen(QColor color,
int width = 0,
Qt::PenStyle style = Qt::SolidLine,
Qt::PenCapStyle cap = Qt::RoundCap,
Qt::PenJoinStyle join = Qt::RoundJoin) {
QPen pen_{color, static_cast<qreal>(width), style, cap, join};
pen_.setCosmetic(true);
return pen_;
}
} // end anonymous namespace
class BadFacesGraphicsItem : public CGAL::Qt::GraphicsItem
{
public:
BadFacesGraphicsItem(const CDT* cdt, const Mesher* mesher)
: cdt_(cdt)
, mesher_(mesher)
, bounds_(-100.0, -100.0, 200.0, 200.0) {}
void setMesher(const Mesher* mesher) {
mesher_ = mesher;
update();
}
void updateBounds(const QRectF& rect) {
bounds_ = rect;
prepareGeometryChange();
update();
}
QRectF boundingRect() const override { return bounds_; }
void paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) override {
if(mesher_ == nullptr || cdt_ == nullptr) {
return;
}
CGAL::Qt::Converter<K> convert;
painter->setBrush(brush_);
painter->setPen(Qt::NoPen);
for(auto fit : bad_faces_range(mesher_)) {
Triangle_2 tri(fit->vertex(0)->point(), fit->vertex(1)->point(), fit->vertex(2)->point());
painter->drawPolygon(convert(tri));
}
}
void modelChanged() override {
// do nothing
}
void setBrush(const QBrush& brush) {
brush_ = brush;
update();
}
QBrush brush() const { return brush_; }
private:
const CDT* cdt_;
const Mesher* mesher_;
QRectF bounds_;
QBrush brush_{QColor(0, 160, 0, 128)};
};
class EncroachedEdgesGraphicsItem : public CGAL::Qt::GraphicsItem
{
public:
EncroachedEdgesGraphicsItem(const CDT* cdt, const Mesher* mesher)
: cdt_(cdt)
, mesher_(mesher)
, bounds_(-100.0, -100.0, 200.0, 200.0) {}
void setMesher(const Mesher* mesher) {
mesher_ = mesher;
update();
}
void updateBounds(const QRectF& rect) {
bounds_ = rect;
prepareGeometryChange();
update();
}
QRectF boundingRect() const override { return bounds_; }
void paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) override {
if(mesher_ == nullptr || cdt_ == nullptr) {
return;
}
CGAL::Qt::Converter<K> convert;
painter->setPen(pen_);
for(const auto& e : encroached_edges_range(mesher_)) {
Segment_2 s = cdt_->segment(e);
painter->drawLine(convert(s));
}
}
void modelChanged() override {
// do nothing
}
void setPen(const QPen& pen) {
pen_ = pen;
update();
}
QPen pen() const { return pen_; }
private:
const CDT* cdt_;
const Mesher* mesher_;
QRectF bounds_;
QPen pen_{QColor(200, 0, 0), 0};
};
class ClustersGraphicsItem : public CGAL::Qt::GraphicsItem
{
public:
ClustersGraphicsItem(const Mesher* mesher)
: mesher_(mesher)
, bounds_(-100.0, -100.0, 200.0, 200.0)
, last_vertex_(DT::Vertex_handle()) {
setAcceptHoverEvents(true);
rebuild();
}
void setMesher(const Mesher* mesher) {
mesher_ = mesher;
rebuild();
update();
}
void setVisible(bool b) {
if(b) {
rebuild();
}
QGraphicsItem::setVisible(b);
}
void rebuild() {
dt_.clear();
if(mesher_ == nullptr) {
return;
}
for(auto it : clusters_vertices_range(mesher_->clusters())) {
dt_.insert(it->point());
}
}
void updateBounds(const QRectF& rect) {
bounds_ = rect;
prepareGeometryChange();
update();
}
QRectF boundingRect() const override { return bounds_; }
void paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) override {
if(mesher_ == nullptr) {
return;
}
CGAL::Qt::Converter<K> convert;
painter->setPen(pen_);
painter->setBrush(brush_);
for(auto it : clusters_vertices_range(mesher_->clusters())) {
QPointF p = convert(it->point());
painter->drawEllipse(p, 2.0, 2.0);
}
if(highlight_segments_.empty()) {
return;
}
QPen reduced_pen(QColor(0, 120, 255), 0);
QPen regular_pen(QColor(200, 0, 0), 0);
for(const auto& entry : highlight_segments_) {
painter->setPen(entry.is_reduced ? reduced_pen : regular_pen);
painter->drawLine(convert(entry.segment));
}
}
void modelChanged() override {
// do nothing
}
void setPen(const QPen& pen) {
pen_ = pen;
update();
}
QPen pen() const { return pen_; }
void setBrush(const QBrush& brush) {
brush_ = brush;
update();
}
QBrush brush() const { return brush_; }
protected:
void hoverMoveEvent(QGraphicsSceneHoverEvent* event) override {
if(mesher_ == nullptr || dt_.number_of_vertices() == 0) {
clearHighlight();
update();
return;
}
CGAL::Qt::Converter<K> convert;
Point_2 p = convert(event->scenePos());
DT::Vertex_handle nearest = dt_.nearest_vertex(p);
if(nearest == DT::Vertex_handle()) {
clearHighlight();
update();
return;
}
if(nearest == last_vertex_) {
return;
}
last_vertex_ = nearest;
highlight_segments_.clear();
typename Mesher::Triangulation::Locate_type lt;
int i = 0;
typename Mesher::Triangulation::Face_handle fh = mesher_->triangulation().locate(nearest->point(), lt, i);
if(lt != Mesher::Triangulation::VERTEX) {
update();
return;
}
typename Mesher::Triangulation::Vertex_handle v2 = fh->vertex(i);
int n = mesher_->clusters().number_of_clusters_at_vertex(v2);
for(int j = 0; j < n; ++j) {
auto seq = mesher_->clusters().vertices_in_cluster_sequence(v2, j);
typename Mesher::Clusters::Cluster cluster;
typename Mesher::Clusters::const_iterator dummy_it;
mesher_->clusters().get_cluster(v2, *(seq.first), cluster, dummy_it);
for(auto it = seq.first; it != seq.second; ++it) {
HighlightSegment entry{Segment_2(v2->point(), (*it)->point()), cluster.is_reduced()};
highlight_segments_.push_back(entry);
}
}
update();
}
void hoverLeaveEvent(QGraphicsSceneHoverEvent*) override {
clearHighlight();
update();
}
private:
struct HighlightSegment
{
Segment_2 segment;
bool is_reduced;
};
void clearHighlight() {
highlight_segments_.clear();
last_vertex_ = DT::Vertex_handle();
}
private:
using DT = CGAL::Delaunay_triangulation_2<K>;
const Mesher* mesher_;
DT dt_;
QRectF bounds_;
DT::Vertex_handle last_vertex_;
std::vector<HighlightSegment> highlight_segments_;
QPen pen_{QColor(0, 0, 0), 0};
QBrush brush_{QColor(0, 0, 255, 160)};
};
class LocalSizeRefiner : public CGAL::Qt::GraphicsViewInput
{
public:
LocalSizeRefiner(QGraphicsScene*, CDT* cdt, Criteria* criteria, Mesher** mesher, QObject* parent)
: CGAL::Qt::GraphicsViewInput(parent)
, cdt_(cdt)
, criteria_(criteria)
, mesher_(mesher)
, has_previous_(false) {}
void reset() { has_previous_ = false; }
protected:
bool eventFilter(QObject* obj, QEvent* event) override {
if(event->type() == QEvent::GraphicsSceneMouseMove) {
QGraphicsSceneMouseEvent* mouseEvent = static_cast<QGraphicsSceneMouseEvent*>(event);
mouseMoveEvent(mouseEvent);
return false;
}
return QObject::eventFilter(obj, event);
}
void mouseMoveEvent(QGraphicsSceneMouseEvent* event) {
if(cdt_ == nullptr || criteria_ == nullptr || mesher_ == nullptr) {
return;
}
if(*mesher_ == nullptr) {
return;
}
if(cdt_->dimension() != 2) {
return;
}
CGAL::Qt::Converter<K> convert;
Point_2 current_point = convert(event->scenePos());
Point_2 previous_point = current_point;
if(has_previous_) {
previous_point = previous_point_;
}
Face_handle fh = cdt_->locate(current_point);
if(cdt_->is_infinite(fh)) {
previous_point_ = current_point;
has_previous_ = true;
return;
}
std::vector<Face_handle> faces_to_check;
Segment_2 segment(previous_point, current_point);
if(has_previous_) {
auto fc = cdt_->line_walk(current_point, previous_point, fh);
do {
faces_to_check.push_back(fc);
++fc;
} while(!cdt_->is_infinite(fc) && cdt_->triangle(fc).has_on_unbounded_side(previous_point));
} else {
faces_to_check.push_back(fh);
}
criteria_->set_local_size(true);
criteria_->set_segment(segment);
std::vector<Face_handle> bad_faces;
for(const auto& face : faces_to_check) {
if((face != nullptr) && (!cdt_->is_infinite(face)) && face->is_in_domain()) {
Criteria::Quality q;
if(criteria_->is_bad_object().operator()(face, q) != CGAL::Mesh_2::NOT_BAD) {
bad_faces.push_back(face);
}
}
}
(*mesher_)->set_criteria(*criteria_, false);
(*mesher_)->set_bad_faces(bad_faces.begin(), bad_faces.end());
while((*mesher_)->step_by_step_refine_mesh()) {
}
previous_point_ = current_point;
has_previous_ = true;
}
private:
CDT* cdt_;
Criteria* criteria_;
Mesher** mesher_;
Point_2 previous_point_;
bool has_previous_;
};
class CdtPointInputWithConflictZone : public CGAL::Qt::GraphicsViewInput
{
public:
CdtPointInputWithConflictZone(QGraphicsScene* scene, CDT* cdt, QObject* parent)
: CGAL::Qt::GraphicsViewInput(parent)
, scene_(scene)
, cdt_(cdt)
, active_(false) {}
protected:
bool eventFilter(QObject* obj, QEvent* event) override {
if(event->type() == QEvent::GraphicsSceneMousePress) {
QGraphicsSceneMouseEvent* mouseEvent = static_cast<QGraphicsSceneMouseEvent*>(event);
mousePressEvent(mouseEvent);
return true;
}
if(event->type() == QEvent::GraphicsSceneMouseMove) {
QGraphicsSceneMouseEvent* mouseEvent = static_cast<QGraphicsSceneMouseEvent*>(event);
mouseMoveEvent(mouseEvent);
return false;
}
if(event->type() == QEvent::GraphicsSceneMouseRelease) {
QGraphicsSceneMouseEvent* mouseEvent = static_cast<QGraphicsSceneMouseEvent*>(event);
mouseReleaseEvent(mouseEvent);
return true;
}
return QObject::eventFilter(obj, event);
}
void mousePressEvent(QGraphicsSceneMouseEvent* event) {
if(event->modifiers() != 0 || event->button() != ::Qt::LeftButton) {
return;
}
if(cdt_ == nullptr || cdt_->number_of_vertices() == 0) {
return;
}
active_ = true;
last_point_ = convert_(event->scenePos());
updateConflictZone(event->scenePos());
}
void mouseMoveEvent(QGraphicsSceneMouseEvent* event) {
if(!active_) {
return;
}
last_point_ = convert_(event->scenePos());
updateConflictZone(event->scenePos());
}
void mouseReleaseEvent(QGraphicsSceneMouseEvent*) {
if(!active_) {
return;
}
clearItems();
active_ = false;
Q_EMIT(generate(CGAL::make_object(last_point_)));
}
private:
void updateConflictZone(const QPointF& scene_pos) {
clearItems();
if(cdt_ == nullptr) {
return;
}
Point_2 p = convert_(scene_pos);
hint_ = cdt_->locate(p, hint_);
std::list<CDT::Edge> edges;
cdt_->find_conflicts(p, edges, hint_);
QPen pen(QColor(0, 80, 200, 180), 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
for(const auto& edge : edges) {
Segment_2 s = cdt_->segment(edge);
QLineF line = convert_(s);
auto item = new QGraphicsLineItem(line);
item->setPen(pen);
item->setZValue(5.0);
scene_->addItem(item);
conflict_items_.push_back(item);
}
}
void clearItems() {
for(auto item : conflict_items_) {
scene_->removeItem(item);
delete item;
}
conflict_items_.clear();
}
private:
QGraphicsScene* scene_;
CDT* cdt_;
CGAL::Qt::Converter<K> convert_;
CDT::Face_handle hint_;
std::vector<QGraphicsLineItem*> conflict_items_;
Point_2 last_point_;
bool active_;
};
class DisplaySettingsWidget : public QWidget
{
Q_OBJECT
public:
DisplaySettingsWidget(QWidget* parent)
: QWidget(parent) {
QFormLayout* layout = new QFormLayout(this);
addColorAndWidthRow(layout, "Edges", show_edges_, edge_color_, edge_width_);
addColorAndWidthRow(layout, "Constraints", show_constraints_, constraint_color_, constraint_width_);
addColorAndWidthRow(layout, "Vertices", show_vertices_, vertex_color_, vertex_width_);
addColorOnlyRow(layout, "Faces in domain", show_faces_, faces_color_);
addColorAndWidthRow(layout, "Seeds", show_seeds_, seeds_color_, seeds_width_);
addColorOnlyRow(layout, "Bad faces", show_bad_faces_, bad_faces_color_);
addColorAndWidthRow(layout, "Encroached edges", show_encroached_edges_, encroached_edges_color_, encroached_edges_width_);
addColorAndWidthRow(layout, "Clusters", show_clusters_, clusters_color_, clusters_width_);
setLayout(layout);
}
QColor edgeColor() const { return buttonColor(edge_color_); }
QColor constraintColor() const { return buttonColor(constraint_color_); }
QColor vertexColor() const { return buttonColor(vertex_color_); }
QColor facesColor() const { return buttonColor(faces_color_); }
QColor seedsColor() const { return buttonColor(seeds_color_); }
QColor badFacesColor() const { return buttonColor(bad_faces_color_); }
QColor encroachedEdgesColor() const { return buttonColor(encroached_edges_color_); }
QColor clustersColor() const { return buttonColor(clusters_color_); }
int edgeWidth() const { return edge_width_->value(); }
int constraintWidth() const { return constraint_width_->value(); }
int vertexWidth() const { return vertex_width_->value(); }
int seedsWidth() const { return seeds_width_->value(); }
int encroachedEdgesWidth() const { return encroached_edges_width_->value(); }
int clustersWidth() const { return clusters_width_->value(); }
bool showEdges() const { return show_edges_->isChecked(); }
bool showConstraints() const { return show_constraints_->isChecked(); }
bool showVertices() const { return show_vertices_->isChecked(); }
bool showFaces() const { return show_faces_->isChecked(); }
bool showSeeds() const { return show_seeds_->isChecked(); }
bool showBadFaces() const { return show_bad_faces_->isChecked(); }
bool showEncroachedEdges() const { return show_encroached_edges_->isChecked(); }
bool showClusters() const { return show_clusters_->isChecked(); }
void saveSettings() const {
QSettings settings("GeometryFactory", "Mesh_2_demo");
settings.beginGroup("DisplaySettings");
settings.setValue("edgeColor", edgeColor());
settings.setValue("edgeWidth", edgeWidth());
settings.setValue("constraintColor", constraintColor());
settings.setValue("constraintWidth", constraintWidth());
settings.setValue("vertexColor", vertexColor());
settings.setValue("vertexWidth", vertexWidth());
settings.setValue("facesColor", facesColor());
settings.setValue("seedsColor", seedsColor());
settings.setValue("seedsWidth", seedsWidth());
settings.setValue("badFacesColor", badFacesColor());
settings.setValue("encroachedEdgesColor", encroachedEdgesColor());
settings.setValue("encroachedEdgesWidth", encroachedEdgesWidth());
settings.setValue("clustersColor", clustersColor());
settings.setValue("clustersWidth", clustersWidth());
settings.endGroup();
}
void restoreSettings() {
QSettings settings("GeometryFactory", "Mesh_2_demo");
settings.beginGroup("DisplaySettings");
setButtonColor(edge_color_, settings.value("edgeColor", QColor(Qt::blue)).value<QColor>());
edge_width_->setValue(settings.value("edgeWidth", 0).toInt());
setButtonColor(constraint_color_, settings.value("constraintColor", QColor(Qt::red)).value<QColor>());
constraint_width_->setValue(settings.value("constraintWidth", 0).toInt());
setButtonColor(vertex_color_, settings.value("vertexColor", QColor(Qt::black)).value<QColor>());
vertex_width_->setValue(settings.value("vertexWidth", 2).toInt());
setButtonColor(faces_color_, settings.value("facesColor", QColor(Qt::green)).value<QColor>());
setButtonColor(seeds_color_, settings.value("seedsColor", QColor(Qt::darkBlue)).value<QColor>());
seeds_width_->setValue(settings.value("seedsWidth", 0).toInt());
setButtonColor(bad_faces_color_, settings.value("badFacesColor", QColor(0, 160, 0, 128)).value<QColor>());
setButtonColor(encroached_edges_color_, settings.value("encroachedEdgesColor", QColor(Qt::red)).value<QColor>());
encroached_edges_width_->setValue(settings.value("encroachedEdgesWidth", 0).toInt());
setButtonColor(clusters_color_, settings.value("clustersColor", QColor(Qt::black)).value<QColor>());
clusters_width_->setValue(settings.value("clustersWidth", 0).toInt());
settings.endGroup();
}
Q_SIGNALS:
void displayChanged();
private:
void addColorAndWidthRow(QFormLayout* layout, const QString& label, QCheckBox*& checkbox, QPushButton*& colorButton, QSpinBox*& widthSpinBox) {
QHBoxLayout* rowLayout = new QHBoxLayout();
checkbox = new QCheckBox(this);
checkbox->setChecked(true);
connect(checkbox, &QCheckBox::toggled, this, &DisplaySettingsWidget::displayChanged);
colorButton = new QPushButton(this);
colorButton->setAutoFillBackground(true);
colorButton->setMinimumWidth(80);
connect(colorButton, &QPushButton::clicked, this, &DisplaySettingsWidget::selectColor);
widthSpinBox = new QSpinBox(this);
widthSpinBox->setRange(0, 100);
widthSpinBox->setValue(0);
widthSpinBox->setPrefix("Width: ");
connect(widthSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), this, &DisplaySettingsWidget::displayChanged);
rowLayout->addWidget(checkbox);
rowLayout->addWidget(colorButton);
rowLayout->addWidget(widthSpinBox);
rowLayout->addStretch();
layout->addRow(label, rowLayout);
}
void addColorOnlyRow(QFormLayout* layout, const QString& label, QCheckBox*& checkbox, QPushButton*& colorButton) {
QHBoxLayout* rowLayout = new QHBoxLayout();
checkbox = new QCheckBox(this);
checkbox->setChecked(true);
connect(checkbox, &QCheckBox::toggled, this, &DisplaySettingsWidget::displayChanged);
colorButton = new QPushButton(this);
colorButton->setAutoFillBackground(true);
colorButton->setMinimumWidth(80);
connect(colorButton, &QPushButton::clicked, this, &DisplaySettingsWidget::selectColor);
rowLayout->addWidget(checkbox);
rowLayout->addWidget(colorButton);
rowLayout->addStretch();
layout->addRow(label, rowLayout);
}
void setButtonColor(QPushButton* button, const QColor& color) {
std::cerr << "Setting button color to " << color.name().toStdString() << std::endl;
QPalette pal = button->palette();
pal.setColor(QPalette::Button, color);
button->setPalette(pal);
button->setText(color.name());
button->update();
}
QColor buttonColor(QPushButton* button) const { return button->palette().color(QPalette::Button); }
private Q_SLOTS:
void selectColor() {
QPushButton* button = qobject_cast<QPushButton*>(sender());
if(button == nullptr) {
return;
}
QColor color = QColorDialog::getColor(buttonColor(button), this);
if(color.isValid()) {
setButtonColor(button, color);
Q_EMIT displayChanged();
}
}
private:
QCheckBox* show_edges_;
QPushButton* edge_color_;
QSpinBox* edge_width_;
QCheckBox* show_constraints_;
QPushButton* constraint_color_;
QSpinBox* constraint_width_;
QCheckBox* show_vertices_;
QPushButton* vertex_color_;
QSpinBox* vertex_width_;
QCheckBox* show_faces_;
QPushButton* faces_color_;
QCheckBox* show_seeds_;
QPushButton* seeds_color_;
QSpinBox* seeds_width_;
QCheckBox* show_bad_faces_;
QPushButton* bad_faces_color_;
QCheckBox* show_encroached_edges_;
QPushButton* encroached_edges_color_;
QSpinBox* encroached_edges_width_;
QCheckBox* show_clusters_;
QPushButton* clusters_color_;
QSpinBox* clusters_width_;
};
class MainWindow : public CGAL::Qt::DemosMainWindow
{
Q_OBJECT
public:
MainWindow();
void openTriangulation(const QString& filename);
private Q_SLOTS:
void onActionInsertPolylineToggled(bool checked);
void onActionInsertPointsToggled(bool checked);
void onActionInsertSeedsToggled(bool checked);
void onActionShowCircumcircleToggled(bool checked);
void onActionInsertBoundingBox();
void onActionRefineMesh();
void onActionConformMesh();
void onActionRefineStep();
void onActionAutoStepToggled(bool checked);
void onActionClear();
void onActionRecenter();
void onActionOpen();
void onActionSave();
void processInput(CGAL::Object o);
void updateStepLength(int value);
void updateTimerInterval(int value);
void onUnderMouseToggled(bool checked);
void applyDisplaySettings();
void updateCriteria();
private:
void setupUi();
void setupScene();
void setupActions();
void setupDock();
void updateCounts();
void updateMesherState();
void updateDomain();
void updateBounds();
void restoreVisibilitySettings();
void saveVisibilitySettings();
void saveTriangulation(const QString& filename);
void loadPolyConstraints(const QString& filename);
void loadEdgConstraints(const QString& filename);
void loadDataConstraints(const QString& filename);
void loadObjConstraints(const QString& filename);
Mesher* createMesher();
private:
CDT cdt_;
Criteria criteria_;
Mesher* mesher_ = nullptr;
std::list<Point_2> seeds_;
QGraphicsScene scene_;
QGraphicsView* view_ = nullptr;
CGAL::Qt::DelaunayMeshTriangulationGraphicsItem<CDT>* dgi_ = nullptr;
CGAL::Qt::GraphicsViewPolylineInput<K>* polyline_input_ = nullptr;
CdtPointInputWithConflictZone* point_input_ = nullptr;
CGAL::Qt::DelaunayMeshInsertSeeds<CDT>* seed_input_ = nullptr;
CGAL::Qt::TriangulationCircumcircle<CDT>* circumcircle_ = nullptr;
LocalSizeRefiner* local_refiner_ = nullptr;
BadFacesGraphicsItem* bad_faces_item_ = nullptr;
EncroachedEdgesGraphicsItem* encroached_edges_item_ = nullptr;
ClustersGraphicsItem* clusters_item_ = nullptr;
QAction* action_show_vertices_ = nullptr;
QAction* action_show_edges_ = nullptr;
QAction* action_show_constraints_ = nullptr;
QAction* action_show_faces_ = nullptr;
QAction* action_show_seeds_ = nullptr;
QAction* action_show_bad_faces_ = nullptr;
QAction* action_show_encroached_edges_ = nullptr;
QAction* action_show_clusters_ = nullptr;
QAction* action_auto_step_ = nullptr;
QLabel* nb_points_label_ = nullptr;
QLabel* nb_clusters_label_ = nullptr;
QLabel* init_status_label_ = nullptr;
QDoubleSpinBox* angle_bound_box_ = nullptr;
QDoubleSpinBox* size_bound_box_ = nullptr;
QCheckBox* under_mouse_box_ = nullptr;
QSpinBox* step_length_box_ = nullptr;
QSpinBox* timer_interval_box_ = nullptr;
QTimer* timer_ = nullptr;
int step_length_ = 1;
int timer_interval_ = 1000;
DisplaySettingsWidget* display_settings_ = nullptr;
QDockWidget* display_dock_ = nullptr;
};
MainWindow::MainWindow() {
setupUi();
setupScene();
setupActions();
setupDock();
// Restore display settings after dock is set up
display_settings_->restoreSettings();
connect(display_settings_, &DisplaySettingsWidget::displayChanged, this, &MainWindow::applyDisplaySettings);
applyDisplaySettings();
restoreVisibilitySettings();
onActionInsertPolylineToggled(true);
dgi_->setVisibleSeeds(action_show_seeds_->isChecked(), seeds_.begin(), seeds_.end());
updateCriteria();
updateCounts();
}
void MainWindow::setupUi() {
view_ = new QGraphicsView(this);
this->view = view_;
setCentralWidget(view_);
statusBar();
setupStatusBar();
setupOptionsMenu();
addAboutDemo(":/cgal/help/about_Constrained_Delaunay_triangulation_2.html");
addAboutCGAL();
}
void MainWindow::setupScene() {
scene_.setItemIndexMethod(QGraphicsScene::NoIndex);
scene_.setSceneRect(-100.0, -100.0, 200.0, 200.0);
view_->setScene(&scene_);
view_->setMouseTracking(true);
view_->scale(1.0, -1.0);
addNavigation(view_);
bad_faces_item_ = new BadFacesGraphicsItem(&cdt_, mesher_);
bad_faces_item_->setVisible(false);
scene_.addItem(bad_faces_item_);
encroached_edges_item_ = new EncroachedEdgesGraphicsItem(&cdt_, mesher_);
encroached_edges_item_->setVisible(false);
scene_.addItem(encroached_edges_item_);
clusters_item_ = new ClustersGraphicsItem(mesher_);
clusters_item_->setVisible(false);
scene_.addItem(clusters_item_);
dgi_ = new CGAL::Qt::DelaunayMeshTriangulationGraphicsItem<CDT>(&cdt_);
QColor faces_color(Qt::green);
faces_color.setAlpha(150);
dgi_->setFacesInDomainBrush(faces_color);
dgi_->setVerticesPen(pen(Qt::black, 2));
dgi_->setConstraintsPen(pen(Qt::red));
dgi_->setEdgesPen(pen(Qt::blue));
dgi_->setSeedsPen(pen(Qt::darkBlue));
dgi_->setZValue(-1);
scene_.addItem(dgi_);
polyline_input_ = new CGAL::Qt::GraphicsViewPolylineInput<K>(this, &scene_, 0, true);
connect(polyline_input_, &CGAL::Qt::GraphicsViewInput::generate, this, &MainWindow::processInput);
point_input_ = new CdtPointInputWithConflictZone(&scene_, &cdt_, this);
connect(point_input_, &CGAL::Qt::GraphicsViewInput::generate, this, &MainWindow::processInput);
seed_input_ = new CGAL::Qt::DelaunayMeshInsertSeeds<CDT>(&scene_, &cdt_, this);
connect(seed_input_, &CGAL::Qt::GraphicsViewInput::generate, this, &MainWindow::processInput);
circumcircle_ = new CGAL::Qt::TriangulationCircumcircle<CDT>(&scene_, &cdt_, this);
circumcircle_->setPen(pen(Qt::darkGray));
local_refiner_ = new LocalSizeRefiner(&scene_, &cdt_, &criteria_, &mesher_, this);
}
void MainWindow::setupActions() {
QMenu* file_menu = menuBar()->addMenu("&File");
QMenu* mesh_menu = menuBar()->addMenu("&Mesh");
QMenu* view_menu = menuBar()->addMenu("&View");
QMenu* input_menu = menuBar()->addMenu("&Input");
QMenu* options_menu = menuBar()->addMenu("&Options");
QAction* action_open = file_menu->addAction("Open constraints...");
QAction* action_save = file_menu->addAction("Save constraints...");
QAction* action_clear = file_menu->addAction("Clear");
QAction* action_recenter = file_menu->addAction("Recenter");
file_menu->addSeparator();
QAction* action_quit = file_menu->addAction("Quit");
QAction* action_insert_bounding_box = mesh_menu->addAction("Insert bounding box");
QAction* action_refine_mesh = mesh_menu->addAction("Refine mesh");
QAction* action_conform_mesh = mesh_menu->addAction("Conform mesh");
QAction* action_refine_step = mesh_menu->addAction("Refine one step");
action_auto_step_ = mesh_menu->addAction("Auto step");
action_auto_step_->setCheckable(true);
QAction* action_auto_step = action_auto_step_;
action_show_vertices_ = view_menu->addAction("Show vertices");
action_show_edges_ = view_menu->addAction("Show edges");
action_show_constraints_ = view_menu->addAction("Show constrained edges");
action_show_faces_ = view_menu->addAction("Show faces in domain");
action_show_seeds_ = view_menu->addAction("Show seeds");
action_show_bad_faces_ = view_menu->addAction("Show bad faces");
action_show_encroached_edges_ = view_menu->addAction("Show encroached edges");
action_show_clusters_ = view_menu->addAction("Show clusters");
action_show_vertices_->setCheckable(true);
action_show_edges_->setCheckable(true);
action_show_constraints_->setCheckable(true);
action_show_faces_->setCheckable(true);
action_show_seeds_->setCheckable(true);
action_show_bad_faces_->setCheckable(true);
action_show_encroached_edges_->setCheckable(true);
action_show_clusters_->setCheckable(true);
action_show_vertices_->setChecked(true);
action_show_edges_->setChecked(true);
action_show_constraints_->setChecked(true);
action_show_faces_->setChecked(true);
action_show_seeds_->setChecked(true);
QAction* action_insert_polyline = input_menu->addAction("Insert polyline");
QAction* action_insert_points = input_menu->addAction("Insert points");
QAction* action_insert_seeds = input_menu->addAction("Insert seeds");
QAction* action_show_circumcircle = input_menu->addAction("Show circumcircle");
action_insert_polyline->setCheckable(true);
action_insert_points->setCheckable(true);
action_insert_seeds->setCheckable(true);
action_show_circumcircle->setCheckable(true);
QActionGroup* input_group = new QActionGroup(this);
input_group->addAction(action_insert_polyline);
input_group->addAction(action_insert_points);
input_group->addAction(action_insert_seeds);
action_insert_polyline->setChecked(true);
QAction* action_display_settings = options_menu->addAction("Display settings...");
QToolBar* toolbar = addToolBar("Mesh actions");
toolbar->addAction(action_open);
toolbar->addAction(action_save);
toolbar->addAction(action_refine_mesh);
toolbar->addAction(action_conform_mesh);
toolbar->addAction(action_refine_step);
toolbar->addAction(action_auto_step);
QToolBar* input_toolbar = addToolBar("Input");
input_toolbar->addAction(action_insert_polyline);
input_toolbar->addAction(action_insert_points);
input_toolbar->addAction(action_insert_seeds);
input_toolbar->addAction(action_show_circumcircle);
connect(action_open, &QAction::triggered, this, &MainWindow::onActionOpen);
connect(action_save, &QAction::triggered, this, &MainWindow::onActionSave);
connect(action_clear, &QAction::triggered, this, &MainWindow::onActionClear);
connect(action_recenter, &QAction::triggered, this, &MainWindow::onActionRecenter);
connect(action_quit, &QAction::triggered, this, &MainWindow::close);
connect(action_insert_bounding_box, &QAction::triggered, this, &MainWindow::onActionInsertBoundingBox);
connect(action_refine_mesh, &QAction::triggered, this, &MainWindow::onActionRefineMesh);
connect(action_conform_mesh, &QAction::triggered, this, &MainWindow::onActionConformMesh);
connect(action_refine_step, &QAction::triggered, this, &MainWindow::onActionRefineStep);
connect(action_auto_step_, &QAction::toggled, this, &MainWindow::onActionAutoStepToggled);
using DtGraphicsItem = CGAL::Qt::DelaunayMeshTriangulationGraphicsItem<CDT>;
connect(action_show_vertices_, &QAction::toggled, dgi_, &DtGraphicsItem::setVisibleVertices);
connect(action_show_edges_, &QAction::toggled, dgi_, &DtGraphicsItem::setVisibleEdges);
connect(action_show_constraints_, &QAction::toggled, dgi_, &DtGraphicsItem::setVisibleConstraints);
connect(action_show_faces_, &QAction::toggled, dgi_, &DtGraphicsItem::setVisibleFacesInDomain);
connect(action_show_seeds_, &QAction::toggled,
[this](bool checked) { dgi_->setVisibleSeeds(checked, seeds_.begin(), seeds_.end()); });
using CGAL_graphicsItem = CGAL::Qt::GraphicsItem;
connect(action_show_bad_faces_, &QAction::toggled, bad_faces_item_, &CGAL_graphicsItem::setVisible);
connect(action_show_encroached_edges_, &QAction::toggled, encroached_edges_item_, &CGAL_graphicsItem::setVisible);
connect(action_show_clusters_, &QAction::toggled, clusters_item_, &CGAL_graphicsItem::setVisible);
connect(action_show_vertices_, &QAction::toggled, this, [this](bool) { saveVisibilitySettings(); });
connect(action_show_edges_, &QAction::toggled, this, [this](bool) { saveVisibilitySettings(); });
connect(action_show_constraints_, &QAction::toggled, this, [this](bool) { saveVisibilitySettings(); });
connect(action_show_faces_, &QAction::toggled, this, [this](bool) { saveVisibilitySettings(); });
connect(action_show_seeds_, &QAction::toggled, this, [this](bool) { saveVisibilitySettings(); });
connect(action_show_bad_faces_, &QAction::toggled, this, [this](bool) { saveVisibilitySettings(); });
connect(action_show_encroached_edges_, &QAction::toggled, this, [this](bool) { saveVisibilitySettings(); });
connect(action_show_clusters_, &QAction::toggled, this, [this](bool) { saveVisibilitySettings(); });
connect(action_insert_polyline, &QAction::toggled, this, &MainWindow::onActionInsertPolylineToggled);
connect(action_insert_points, &QAction::toggled, this, &MainWindow::onActionInsertPointsToggled);
connect(action_insert_seeds, &QAction::toggled, this, &MainWindow::onActionInsertSeedsToggled);
connect(action_show_circumcircle, &QAction::toggled, this, &MainWindow::onActionShowCircumcircleToggled);
connect(action_display_settings, &QAction::triggered, [this]() {
display_dock_->setVisible(!display_dock_->isVisible());
});
}
void MainWindow::setupDock() {
QDockWidget* dock = new QDockWidget("Mesh settings", this);
QWidget* dock_widget = new QWidget(dock);
QVBoxLayout* layout = new QVBoxLayout(dock_widget);
QGroupBox* info_box = new QGroupBox("Info", dock_widget);
QFormLayout* info_layout = new QFormLayout(info_box);
nb_points_label_ = new QLabel("0", info_box);
nb_clusters_label_ = new QLabel("0", info_box);
init_status_label_ = new QLabel("no", info_box);
info_layout->addRow("Number of points", nb_points_label_);
info_layout->addRow("Number of clusters", nb_clusters_label_);
info_layout->addRow("Initialized", init_status_label_);
QGroupBox* criteria_box = new QGroupBox("Criteria", dock_widget);
QFormLayout* criteria_layout = new QFormLayout(criteria_box);
angle_bound_box_ = new QDoubleSpinBox(criteria_box);
angle_bound_box_->setDecimals(4);
angle_bound_box_->setRange(0.0, 1000.0);
angle_bound_box_->setValue(0.125);
size_bound_box_ = new QDoubleSpinBox(criteria_box);
size_bound_box_->setDecimals(6);
size_bound_box_->setRange(0.0, std::numeric_limits<double>::max());
size_bound_box_->setValue(0.0);
criteria_layout->addRow("Angle bound", angle_bound_box_);
criteria_layout->addRow("Size bound", size_bound_box_);
under_mouse_box_ = new QCheckBox("Under mouse only", criteria_box);
criteria_layout->addRow(under_mouse_box_);
QGroupBox* step_box = new QGroupBox("Step controls", dock_widget);
QFormLayout* step_layout = new QFormLayout(step_box);
step_length_box_ = new QSpinBox(step_box);
step_length_box_->setRange(1, std::numeric_limits<int>::max());
step_length_box_->setValue(step_length_);
timer_interval_box_ = new QSpinBox(step_box);
timer_interval_box_->setRange(1, std::numeric_limits<int>::max());
timer_interval_box_->setValue(timer_interval_);
timer_interval_box_->setSuffix(" ms");
step_layout->addRow("Step length", step_length_box_);
step_layout->addRow("Timer interval", timer_interval_box_);
layout->addWidget(info_box);
layout->addWidget(criteria_box);
layout->addWidget(step_box);
layout->addStretch(1);
dock_widget->setLayout(layout);
dock->setWidget(dock_widget);
addDockWidget(Qt::RightDockWidgetArea, dock);
connect(angle_bound_box_, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &MainWindow::updateCriteria);
connect(size_bound_box_, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &MainWindow::updateCriteria);
connect(under_mouse_box_, &QCheckBox::toggled, this, &MainWindow::onUnderMouseToggled);
connect(step_length_box_, QOverload<int>::of(&QSpinBox::valueChanged), this, &MainWindow::updateStepLength);
connect(timer_interval_box_, QOverload<int>::of(&QSpinBox::valueChanged), this, &MainWindow::updateTimerInterval);
// Create display settings dock widget
display_dock_ = new QDockWidget("Display settings", this);
display_settings_ = new DisplaySettingsWidget(display_dock_);
display_dock_->setWidget(display_settings_);
addDockWidget(Qt::RightDockWidgetArea, display_dock_);
timer_ = new QTimer(this);
connect(timer_, &QTimer::timeout, this, &MainWindow::onActionRefineStep);
}
void MainWindow::processInput(CGAL::Object o) {
std::list<Point_2> points;
if(CGAL::assign(points, o)) {
if(points.size() == 1) {
cdt_.insert(points.front());
} else {
Point_2 p, q;
CDT::Vertex_handle vh, wh;
auto it = points.begin();
vh = cdt_.insert(*it);
p = *it;
++it;
for(; it != points.end(); ++it) {
q = *it;
if(p != q) {
wh = cdt_.insert(*it);
cdt_.insert_constraint(vh, wh);
vh = wh;
p = q;
}
}
}
} else {
Point_2 p;
if(CGAL::assign(p, o)) {
seeds_.push_back(p);
if(action_show_seeds_->isChecked()) {
dgi_->setVisibleSeeds(true, seeds_.begin(), seeds_.end());
}
}
}
updateDomain();
updateCounts();
updateBounds();
dgi_->modelChanged();
}
void MainWindow::updateDomain() {
Mesher::mark_facets(cdt_, seeds_.begin(), seeds_.end());
}
void MainWindow::updateCounts() {
nb_points_label_->setText(QString::number(static_cast<int>(cdt_.number_of_vertices())));
if(mesher_ != nullptr) {
nb_clusters_label_->setText(QString::number(mesher_->clusters().size()));
init_status_label_->setText("yes");
} else {
nb_clusters_label_->setText("0");
init_status_label_->setText("no");
}
}
void MainWindow::updateMesherState() {
bad_faces_item_->setMesher(mesher_);
encroached_edges_item_->setMesher(mesher_);
clusters_item_->setMesher(mesher_);
}
void MainWindow::updateBounds() {
QRectF rect = dgi_->boundingRect();
if(!rect.isValid()) {
rect = QRectF(-100.0, -100.0, 200.0, 200.0);
}
bad_faces_item_->updateBounds(rect);
encroached_edges_item_->updateBounds(rect);
clusters_item_->updateBounds(rect);
}
void MainWindow::restoreVisibilitySettings() {
QSettings settings("GeometryFactory", "Mesh_2_demo");
std::cerr << "Restoring visibility settings from file \"" << settings.fileName().toStdString() << "\"...\n";
settings.beginGroup("DisplayVisibility");
action_show_vertices_->setChecked(settings.value("showVertices", true).toBool());
action_show_edges_->setChecked(settings.value("showEdges", true).toBool());
action_show_constraints_->setChecked(settings.value("showConstraints", true).toBool());
action_show_faces_->setChecked(settings.value("showFaces", true).toBool());
action_show_seeds_->setChecked(settings.value("showSeeds", true).toBool());
action_show_bad_faces_->setChecked(settings.value("showBadFaces", false).toBool());
action_show_encroached_edges_->setChecked(settings.value("showEncroachedEdges", false).toBool());
action_show_clusters_->setChecked(settings.value("showClusters", false).toBool());
settings.endGroup();
}
void MainWindow::saveVisibilitySettings() {
QSettings settings("GeometryFactory", "Mesh_2_demo");
std::cerr << "Saving visibility settings to file \"" << settings.fileName().toStdString() << "\"...\n";
settings.beginGroup("DisplayVisibility");
settings.setValue("showVertices", action_show_vertices_->isChecked());
settings.setValue("showEdges", action_show_edges_->isChecked());
settings.setValue("showConstraints", action_show_constraints_->isChecked());
settings.setValue("showFaces", action_show_faces_->isChecked());
settings.setValue("showSeeds", action_show_seeds_->isChecked());
settings.setValue("showBadFaces", action_show_bad_faces_->isChecked());
settings.setValue("showEncroachedEdges", action_show_encroached_edges_->isChecked());
settings.setValue("showClusters", action_show_clusters_->isChecked());
settings.endGroup();
}
Mesher* MainWindow::createMesher() {
Mesher* mesher = new Mesher(cdt_, criteria_);
mesher->set_seeds(seeds_.begin(), seeds_.end());
return mesher;
}
void MainWindow::onActionInsertPolylineToggled(bool checked) {
if(checked) {
scene_.installEventFilter(polyline_input_);
} else {
scene_.removeEventFilter(polyline_input_);
}
}
void MainWindow::onActionInsertPointsToggled(bool checked) {
if(checked) {
scene_.installEventFilter(point_input_);
} else {
scene_.removeEventFilter(point_input_);
}
}
void MainWindow::onActionInsertSeedsToggled(bool checked) {
if(checked) {
scene_.installEventFilter(seed_input_);
} else {
scene_.removeEventFilter(seed_input_);
}
}
void MainWindow::onActionShowCircumcircleToggled(bool checked) {
if(checked) {
scene_.installEventFilter(circumcircle_);
circumcircle_->show();
} else {
scene_.removeEventFilter(circumcircle_);
circumcircle_->hide();
}
}
void MainWindow::onActionInsertBoundingBox() {
if(cdt_.dimension() < 1) {
return;
}
FT xmin, xmax, ymin, ymax;
auto vi = cdt_.finite_vertices_begin();
xmin = xmax = vi->point().x();
ymin = ymax = vi->point().y();
++vi;
while(vi != cdt_.finite_vertices_end()) {
xmin = (std::min)(xmin, vi->point().x());
xmax = (std::max)(xmax, vi->point().x());
ymin = (std::min)(ymin, vi->point().y());
ymax = (std::max)(ymax, vi->point().y());
++vi;
}
FT xcenter = (xmin + xmax) / 2.;
FT ycenter = (ymin + ymax) / 2.;
FT xspan = (xmax - xmin) / 2.;
FT yspan = (ymax - ymin) / 2.;
Point_2 bb1(xcenter - 1.5 * xspan, ycenter - 1.5 * yspan);
Point_2 bb2(xcenter + 1.5 * xspan, ycenter - 1.5 * yspan);
Point_2 bb3(xcenter + 1.5 * xspan, ycenter + 1.5 * yspan);
Point_2 bb4(xcenter - 1.5 * xspan, ycenter + 1.5 * yspan);
cdt_.insert(bb1);
cdt_.insert(bb2);
cdt_.insert(bb3);
cdt_.insert(bb4);
cdt_.insert_constraint(bb1, bb2);
cdt_.insert_constraint(bb2, bb3);
cdt_.insert_constraint(bb3, bb4);
cdt_.insert_constraint(bb4, bb1);
updateDomain();
updateCounts();
dgi_->modelChanged();
}
void MainWindow::onActionRefineMesh() {
criteria_.set_bound(angle_bound_box_->value());
criteria_.set_size_bound(size_bound_box_->value());
criteria_.set_local_size(false);
if(mesher_ == nullptr) {
mesher_ = createMesher();
}
mesher_->set_criteria(criteria_);
mesher_->refine_mesh();
updateMesherState();
updateDomain();
updateCounts();
dgi_->modelChanged();
}
void MainWindow::onActionConformMesh() {
CGAL::make_conforming_Gabriel_2(cdt_);
updateDomain();
if(mesher_ != nullptr) {
delete mesher_;
mesher_ = nullptr;
}
updateMesherState();
updateCounts();
dgi_->modelChanged();
}
void MainWindow::onActionRefineStep() {
criteria_.set_bound(angle_bound_box_->value());
criteria_.set_size_bound(size_bound_box_->value());
if(mesher_ == nullptr) {
mesher_ = createMesher();
mesher_->init();
updateMesherState();
} else {
int counter = step_length_;
while(counter > 0) {
--counter;
if(!mesher_->try_one_step_refine_mesh()) {
action_auto_step_->setChecked(false);
break;
}
}
}
updateDomain();
updateCounts();
dgi_->modelChanged();
}
void MainWindow::onActionAutoStepToggled(bool checked) {
if(checked) {
timer_->start(timer_interval_);
} else {
timer_->stop();
}
}
void MainWindow::onActionClear() {
cdt_.clear();
seeds_.clear();
if(mesher_ != nullptr) {
delete mesher_;
mesher_ = nullptr;
}
local_refiner_->reset();
updateMesherState();
updateCounts();
dgi_->modelChanged();
}
void MainWindow::onActionRecenter() {
QRectF rect = dgi_->boundingRect();
if(rect.isValid()) {
view_->setSceneRect(rect);
view_->fitInView(rect, Qt::KeepAspectRatio);
}
}
void MainWindow::onActionOpen() {
QString filename = QFileDialog::getOpenFileName(this, "Open constraint file", ".",
"Edge files (*.edg);;"
"Poly files (*.poly);;"
"Data files (*.data);;"
"Obj files (*.obj);;"
"All files (*)");
if(filename.isEmpty()) {
return;
}
openTriangulation(filename);
}
void MainWindow::onActionSave() {
QString filename = QFileDialog::getSaveFileName(this, "Save constraints", "filename.edg",
"Edge files (*.edg);;"
"Poly files (*.poly);;"
"All files (*)");
if(filename.isEmpty()) {
return;
}
saveTriangulation(filename);
}
void MainWindow::openTriangulation(const QString& filename) {
if(filename.endsWith(".poly")) {
loadPolyConstraints(filename);
} else if(filename.endsWith(".data")) {
loadDataConstraints(filename);
} else if(filename.endsWith(".obj")) {
loadObjConstraints(filename);
} else {
loadEdgConstraints(filename);
}
updateDomain();
updateCounts();
dgi_->modelChanged();
onActionRecenter();
}
void MainWindow::saveTriangulation(const QString& filename) {
std::ofstream of(qPrintable(filename));
if(!of) {
return;
}
if(filename.endsWith(".poly")) {
CGAL::IO::write_triangle_poly_file(cdt_, of, seeds_.begin(), seeds_.end());
} else {
write_constraints(cdt_, of);
}
}
void MainWindow::loadPolyConstraints(const QString& filename) {
std::ifstream ifs(qPrintable(filename));
if(!ifs) {
return;
}
cdt_.clear();
seeds_.clear();
CGAL::IO::read_triangle_poly_file(cdt_, ifs, std::back_inserter(seeds_));
}
void MainWindow::loadEdgConstraints(const QString& filename) {
std::ifstream ifs(qPrintable(filename));
if(!ifs) {
return;
}
cdt_.clear();
seeds_.clear();
read_constraints(cdt_, ifs);
}
void MainWindow::loadObjConstraints(const QString& filename) {
std::ifstream ifs(qPrintable(filename));
if(!ifs) {
return;
}
cdt_.clear();
seeds_.clear();
std::vector<Point_2> points;
std::vector<std::vector<std::size_t>> polylines;
std::vector<std::vector<std::size_t>> unused_polygons;
if(!CGAL::IO::internal::read_OBJ(ifs, points, polylines, unused_polygons)) {
QMessageBox::warning(this, "Error", "Failed to read OBJ file");
return;
}
// Insert constraints from polylines
for(const std::vector<std::size_t>& polyline : polylines) {
for(std::size_t i = 1; i < polyline.size(); ++i) {
cdt_.insert_constraint(points[polyline[i - 1]], points[polyline[i]]);
}
}
}
void MainWindow::loadDataConstraints(const QString& filename) {
std::ifstream ins(qPrintable(filename));
if(!ins) {
return;
}
int nx, ny, niso, use_threshold;
float threshold;
ins >> nx >> ny >> niso >> use_threshold >> threshold;
for(int c = 0; c < niso; ++c) {
float f;
ins >> f;
}
std::vector<Point_2> points(nx * ny);
double xmin, xmax, ymin, ymax;
ins >> xmin >> xmax >> ymin >> ymax;
double dx = (xmax - xmin) / (nx - 1);
double dy = (ymax - ymin) / (ny - 1);
std::size_t k2 = 0;
for(int i2 = 0; i2 < nx; ++i2) {
for(int j = 0; j < ny; ++j) {
points[k2] = Point_2(xmin + i2 * dx, ymin + j * dy);
++k2;
}
}
std::random_device rd;
std::mt19937 gen(rd());
std::shuffle(points.begin(), points.end(), gen);
cdt_.clear();
QString fault_filename = filename;
fault_filename.replace(QRegularExpression("\\.data$"), "_fault.data");
std::ifstream ins2(qPrintable(fault_filename));
if(!ins2) {
return;
}
int num_lines = 0;
ins2 >> num_lines;
std::vector<int> num_vertex_per_line(num_lines);
for(int n = 0; n < num_lines; ++n) {
ins2 >> num_vertex_per_line[n];
}
CGAL::Bbox_2 b;
for(int i = 0; i < num_lines; ++i) {
Point_2 p, q;
ins2 >> p;
if(i == 0) {
b = p.bbox();
} else {
b = b + p.bbox();
}
for(int j = 1; j < num_vertex_per_line[i]; ++j) {
ins2 >> q;
cdt_.insert_constraint(p, q);
p = q;
b = b + p.bbox();
}
}
for(const auto& point : points) {
if(CGAL::do_overlap(b, point.bbox())) {
cdt_.insert(point);
}
}
xmax = b.xmax();
xmin = b.xmin();
ymax = b.ymax();
ymin = b.ymin();
dx = (xmax - xmin) / 20.0;
dy = (ymax - ymin) / 20.0;
xmin -= dx;
ymin -= dy;
xmax += dx;
ymax += dy;
Point_2 bl(xmin, ymin);
Point_2 br(xmax, ymin);
Point_2 tl(xmin, ymax);
Point_2 tr(xmax, ymax);
cdt_.insert_constraint(bl, br);
cdt_.insert_constraint(br, tr);
cdt_.insert_constraint(tr, tl);
cdt_.insert_constraint(tl, bl);
}
void MainWindow::updateStepLength(int value) {
step_length_ = value;
}
void MainWindow::updateTimerInterval(int value) {
timer_interval_ = value;
if(timer_->isActive()) {
timer_->start(timer_interval_);
}
}
void MainWindow::updateCriteria() {
criteria_.set_bound(angle_bound_box_->value());
criteria_.set_size_bound(size_bound_box_->value());
criteria_.set_local_size(under_mouse_box_->isChecked());
if(mesher_ != nullptr) {
mesher_->set_criteria(criteria_);
}
}
void MainWindow::onUnderMouseToggled(bool checked) {
if(checked) {
if(mesher_ == nullptr) {
mesher_ = createMesher();
mesher_->init();
updateMesherState();
}
criteria_.set_local_size(true);
mesher_->set_criteria(criteria_);
scene_.installEventFilter(local_refiner_);
} else {
criteria_.set_local_size(false);
if(mesher_ != nullptr) {
mesher_->set_criteria(criteria_);
}
scene_.removeEventFilter(local_refiner_);
local_refiner_->reset();
}
}
void MainWindow::applyDisplaySettings() {
dgi_->setEdgesPen(pen(display_settings_->edgeColor(), display_settings_->edgeWidth()));
dgi_->setConstraintsPen(pen(display_settings_->constraintColor(), display_settings_->constraintWidth()));
dgi_->setVerticesPen(pen(display_settings_->vertexColor(), display_settings_->vertexWidth()));
dgi_->setFacesInDomainBrush(QBrush(display_settings_->facesColor()));
dgi_->setSeedsPen(pen(display_settings_->seedsColor(), display_settings_->seedsWidth()));
bad_faces_item_->setBrush(QBrush(display_settings_->badFacesColor()));
encroached_edges_item_->setPen(
pen(display_settings_->encroachedEdgesColor(), display_settings_->encroachedEdgesWidth()));
clusters_item_->setPen(pen(display_settings_->clustersColor(), display_settings_->clustersWidth()));
clusters_item_->setBrush(QBrush(display_settings_->clustersColor()));
action_show_edges_->setChecked(display_settings_->showEdges());
action_show_constraints_->setChecked(display_settings_->showConstraints());
action_show_vertices_->setChecked(display_settings_->showVertices());
action_show_faces_->setChecked(display_settings_->showFaces());
action_show_seeds_->setChecked(display_settings_->showSeeds());
action_show_bad_faces_->setChecked(display_settings_->showBadFaces());
action_show_encroached_edges_->setChecked(display_settings_->showEncroachedEdges());
action_show_clusters_->setChecked(display_settings_->showClusters());
display_settings_->saveSettings();
dgi_->modelChanged();
}
int main(int argc, char** argv) {
QApplication app(argc, argv);
app.setOrganizationDomain("geometryfactory.com");
app.setOrganizationName("GeometryFactory");
app.setApplicationName("Mesh_2 demo");
CGAL_QT_INIT_RESOURCES;
MainWindow window;
window.show();
for(const auto& arg : app.arguments().sliced(1)) {
window.openTriangulation(arg);
}
return app.exec();
}
#include "mesh_2_demo.moc"