1746 lines
54 KiB
C++
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"
|