fix an issue with the offset meshing plugin

This commit is contained in:
Laurent Rineau 2025-03-10 17:21:08 +01:00
parent 299a89f212
commit a3e64cffbc
5 changed files with 12 additions and 713 deletions

View File

@ -63,30 +63,20 @@ add_dependencies(CGALlab_compile_all_plugins CGALlab_all_plugins)
STRING(TOLOWER "${plugin_implementation_base_name}.json" base_name)
SET(filename "${CMAKE_CURRENT_BINARY_DIR}/${base_name}")
LIST(LENGTH ARG_KEYWORDS size)
SET(keywords "")
if(${size} GREATER 0)
SET(keywords )
FILE(WRITE ${filename} "{ \"Keywords\" : [")
foreach(keyword ${ARG_KEYWORDS})
LIST(APPEND keywords "\"${keyword}\", ")
SET(keywords "${keywords}\"${keyword}\", ")
if(NOT TARGET ${keyword})
add_custom_target(${keyword})
endif()
add_dependencies( ${keyword} ${plugin_name})
endforeach()
LIST(LENGTH keywords size)
math(EXPR size "${size} - 1")
LIST(GET keywords -1 last_element)
LIST(REMOVE_AT keywords ${size})
STRING(LENGTH ${last_element} size)
math(EXPR size "${size} - 2")
STRING(SUBSTRING ${last_element} 0 ${size} last_element)
LIST(APPEND keywords ${last_element})
foreach(keyword ${keywords})
file(APPEND ${filename} ${keyword})
endforeach()
file(APPEND ${filename} "], \n")
string(TIMESTAMP VERSION "%Y-%m-%d %H:%M")
file(APPEND ${filename} "\"ConfigDate\" : \"${VERSION}\" }")
# Remove the last comma and space
string(REGEX REPLACE ", $" "" keywords "${keywords}")
endif()
file(WRITE ${filename} "{\n \"Keywords\" : [ ${keywords} ],\n")
string(TIMESTAMP VERSION "%Y-%m-%d %H:%M")
file(APPEND ${filename} " \"ConfigDate\" : \"${VERSION}\"\n}\n")
CGAL_install_hooks()
endmacro(cgal_lab_plugin)

View File

@ -565,7 +565,7 @@ bool MainWindow::load_plugin(QString fileName, bool blacklisted)
}
QDebug qdebug = qDebug();
if(verbose)
qdebug << "### Loading \"" << fileName.toUtf8().data() << "\"... ";
qdebug << "### Loading" << fileName << "... ";
QPluginLoader loader;
loader.setFileName(fileinfo.absoluteFilePath());
QJsonArray keywords = loader.metaData().value("MetaData").toObject().value("Keywords").toArray();
@ -612,6 +612,8 @@ bool MainWindow::load_plugin(QString fileName, bool blacklisted)
}
else{
pluginsStatus_map[name] = loader.errorString();
if(verbose)
qdebug << "\n#### Error: " << loader.errorString();
}
PathNames_map[name].push_back(fileinfo.absoluteDir().absolutePath());

View File

@ -2,6 +2,8 @@ include(CGALlab_macros)
remove_definitions(-DQT_STATICPLUGIN)
set(CMAKE_AUTOMOC ON)
qt6_wrap_cpp(VOLUME_MOC_OUTFILES
${CMAKE_CURRENT_SOURCE_DIR}/Volume_plane_thread.h)
qt6_wrap_cpp(VOLUME_MOC_OUTFILES

View File

@ -1,7 +1,5 @@
#include "config.h"
#ifdef CGAL_LAB_DEMO_USE_SURFACE_MESHER
#include <CGAL/Three/CGAL_Lab_plugin_interface.h>
#include "ui_Offset_meshing_dialog.h"
@ -761,5 +759,3 @@ inflate_mesh()
}
#include "Offset_meshing_plugin.moc"
#endif // CGAL_LAB_DEMO_USE_SURFACE_MESHER

View File

@ -1,691 +0,0 @@
#include "config.h"
#ifdef CGAL_LAB_DEMO_USE_SURFACE_MESHER
#include <CGAL/Three/CGAL_Lab_plugin_interface.h>
#include "ui_Remeshing_dialog.h"
#include <QObject>
#include <QAction>
#include <QMainWindow>
#include <QMenu>
#include <QApplication>
#include <QtPlugin>
#include <QThread>
#include "Scene_surface_mesh_item.h"
#include "Scene_polygon_soup_item.h"
#include "Scene_polylines_item.h"
#include <QInputDialog>
#include <QStringList>
#include <QMessageBox>
#include <QAbstractButton>
#include "C3t3_type.h"
#include <CGAL/AABB_tree.h>
#include <CGAL/AABB_traits_3.h>
#include <CGAL/AABB_face_graph_triangle_primitive.h>
#include <CGAL/Side_of_triangle_mesh.h>
#include <CGAL/Polygon_mesh_processing/bbox.h>
#include <CGAL/Polygon_mesh_processing/orientation.h>
#include <CGAL/Timer.h>
#include <CGAL/make_mesh_3.h>
#include <CGAL/Labeled_mesh_domain_3.h>
#include <CGAL/Mesh_domain_with_polyline_features_3.h>
#include <CGAL/Mesh_criteria_3.h>
#include <CGAL/Three/Three.h>
#include <CGAL/facets_in_complex_3_to_triangle_mesh.h>
#include <memory> // std::shared_ptr
namespace CGAL{
template <class TriangleMesh, class GeomTraits>
class Offset_function
{
typedef AABB_face_graph_triangle_primitive<TriangleMesh> Primitive;
typedef AABB_traits_3<GeomTraits, Primitive> Traits;
typedef AABB_tree<Traits> Tree;
typedef Side_of_triangle_mesh<TriangleMesh, GeomTraits> Side_of;
public:
Offset_function(TriangleMesh& tm, double offset_distance)
: m_tree_ptr(new Tree(std::begin(faces(tm)),
std::end(faces(tm)),
tm) )
, m_side_of_ptr( new Side_of(*m_tree_ptr) )
, m_offset_distance(offset_distance)
, m_is_closed( is_closed(tm) )
{
CGAL_assertion(!m_tree_ptr->empty());
}
double operator()(const typename GeomTraits::Point_3& p) const
{
using CGAL::sqrt;
Bounded_side side = m_is_closed?m_side_of_ptr->operator()(p):ON_UNBOUNDED_SIDE;
if (side==ON_BOUNDARY) return m_offset_distance;
typename GeomTraits::Point_3 closest_point = m_tree_ptr->closest_point(p);
double distance = sqrt(squared_distance(p, closest_point));
return (side == ON_UNBOUNDED_SIDE ? -distance : distance) + m_offset_distance;
}
private:
std::shared_ptr<Tree> m_tree_ptr;
std::shared_ptr<Side_of> m_side_of_ptr;
double m_offset_distance;
bool m_is_closed;
};
class Polygon_soup_offset_function {
typedef Scene_polygon_soup_item::Points Points;
typedef Scene_polygon_soup_item::Polygons Polygons;
typedef Polygons::const_iterator Polygon_iterator;
class Polygon_soup_point_property_map {
const Points* points_vector_ptr;
public:
typedef Polygon_iterator key_type;
typedef EPICK::Point_3 value_type;
typedef const value_type& reference;
typedef boost::readable_property_map_tag category;
Polygon_soup_point_property_map() = default;
Polygon_soup_point_property_map(const Points* ptr)
: points_vector_ptr(ptr)
{}
friend reference get(Polygon_soup_point_property_map map,
key_type polygon_it)
{
return (*map.points_vector_ptr)[*polygon_it->begin()];
}
};
class Polygon_soup_triangle_property_map {
const Points* points_vector_ptr;
public:
typedef Polygon_iterator key_type;
typedef EPICK::Triangle_3 value_type;
typedef value_type reference;
typedef boost::readable_property_map_tag category;
Polygon_soup_triangle_property_map() = default;
Polygon_soup_triangle_property_map(const Points* ptr)
: points_vector_ptr(ptr)
{}
friend value_type get(Polygon_soup_triangle_property_map map,
key_type polygon_it)
{
auto it = polygon_it->begin();
CGAL_assertion(it != polygon_it->end());
const auto id0 = *it++;
CGAL_assertion(it != polygon_it->end());
const auto id1 = *it++;
CGAL_assertion(it != polygon_it->end());
const auto id2 = *it++;
CGAL_assertion(it == polygon_it->end());
return value_type( (*map.points_vector_ptr)[id0],
(*map.points_vector_ptr)[id1],
(*map.points_vector_ptr)[id2] );
}
};
struct AABB_primitive :
public CGAL::AABB_primitive<Polygon_iterator,
Polygon_soup_triangle_property_map,
Polygon_soup_point_property_map,
CGAL::Tag_true,
CGAL::Tag_false>
{
typedef CGAL::AABB_primitive<Polygon_iterator,
Polygon_soup_triangle_property_map,
Polygon_soup_point_property_map,
CGAL::Tag_true,
CGAL::Tag_false> Base;
typedef Polygon_iterator Id;
template <typename ObjectPmap, typename PointPmap>
AABB_primitive(Id id, ObjectPmap&& opmap, PointPmap&& ppmap)
: Base(id, std::forward<ObjectPmap>(opmap), std::forward<PointPmap>(ppmap))
{}
template <typename Iterator, typename ObjectPmap, typename PointPmap>
AABB_primitive(Iterator it, ObjectPmap&& opmap, PointPmap&& ppmap)
: Base(*it, std::forward<ObjectPmap>(opmap), std::forward<PointPmap>(ppmap))
{}
}; // end struct template AABB_primitive
typedef CGAL::AABB_traits_3<EPICK, AABB_primitive> AABB_traits;
typedef CGAL::AABB_tree<AABB_traits> AABB_tree;
std::shared_ptr<AABB_tree> m_tree_ptr;
double m_offset_distance;
typedef Polygon_soup_triangle_property_map ObjectPmap;
typedef Polygon_soup_point_property_map PointPmap;
public:
Polygon_soup_offset_function(const Scene_polygon_soup_item* soup,
const double offset_distance)
: m_tree_ptr
(std::make_shared<AABB_tree>(begin(soup->polygons()),
end(soup->polygons()),
ObjectPmap(&soup->points()),
PointPmap(&soup->points()))
)
, m_offset_distance(offset_distance)
{
CGAL_assertion(! m_tree_ptr->empty() );
}
double operator()(const EPICK::Point_3& p) const
{
using CGAL::sqrt;
EPICK::Point_3 closest_point = m_tree_ptr->closest_point(p);
double distance = sqrt(squared_distance(p, closest_point));
return m_offset_distance - distance;
}
}; // end class Polygon_soup_offset_function
} //end of CGAL namespace
Scene_surface_mesh_item* make_item(SMesh* sm)
{
return new Scene_surface_mesh_item(sm);
}
CGAL::Offset_function<SMesh, EPICK>
offset_function(SMesh* surface_mesh_ptr, double offset_value) {
return { *surface_mesh_ptr, offset_value };
}
CGAL::Polygon_soup_offset_function
offset_function(Scene_polygon_soup_item* item, double offset_value) {
return { item, offset_value };
}
template <typename T>
struct Result_type {
typedef T type;
};
template <>
struct Result_type<Scene_polygon_soup_item> {
typedef SMesh type;
};
template <typename Mesh>
CGAL::Bbox_3 bbox(Mesh* mesh_ptr) {
return CGAL::Polygon_mesh_processing::bbox(*mesh_ptr);
}
CGAL::Bbox_3 bbox(Scene_polygon_soup_item* item) {
return item->bbox();
}
class MeshGuard{
SMesh* mesh;
bool done;
public:
MeshGuard(SMesh* mesh):mesh(mesh), done(false){}
void setDone(){done = true;}
~MeshGuard(){
if(!done)
delete mesh;
}
};
// declare the CGAL function
template<class Mesh>
SMesh* cgal_off_meshing(QWidget*,
Mesh* tm_ptr,
Scene_polylines_item* polylines_item,
const double offset_value,
const double angle,
const double sizing,
const double approx,
const double edge_size,
int tag)
{
typedef EPICK GT;
typedef CGAL::Labeled_mesh_domain_3<GT, int, int> Mesh_domain_base;
typedef CGAL::Mesh_domain_with_polyline_features_3<Mesh_domain_base> Mesh_domain;
typedef C3t3::Triangulation Tr;
typedef CGAL::Mesh_criteria_3<Tr> Mesh_criteria;
typedef GT::Sphere_3 Sphere_3;
CGAL::Bbox_3 bbox = ::bbox(tm_ptr);
GT::Point_3 center((bbox.xmax()+bbox.xmin())/2,
(bbox.ymax()+bbox.ymin())/2,
(bbox.zmax()+bbox.zmin())/2);
double sqrad = 0.6 * std::sqrt( CGAL::square(bbox.xmax()-bbox.xmin())+
CGAL::square(bbox.ymax()-bbox.ymin())+
CGAL::square(bbox.zmax()-bbox.zmin()) )
+ offset_value;
sqrad=CGAL::square(sqrad);
CGAL::Timer timer;
timer.start();
namespace p = CGAL::parameters;
Mesh_domain domain =
Mesh_domain::create_implicit_mesh_domain
(p::function = offset_function(tm_ptr, offset_value),
p::bounding_object = Sphere_3(center, sqrad),
p::relative_error_bound = 1e-7,
p::construct_surface_patch_index = [](int i, int j) { return (i * 1000 + j); });
const CGAL::Mesh_facet_topology topology = CGAL::FACET_VERTICES_ON_SAME_SURFACE_PATCH;
auto manifold_option = p::non_manifold();
if(tag == 1) manifold_option = p::manifold_with_boundary();
if(tag == 2) manifold_option = p::manifold();
Mesh_criteria criteria(p::facet_angle = angle,
p::facet_size = sizing,
p::facet_distance = approx,
p::facet_topology = topology,
p::edge_size = edge_size);
if (polylines_item!=nullptr)
{
typedef std::vector<Mesh_domain::Surface_patch_index> Surface_patch_ids;
std::vector<Mesh_domain::Surface_patch_index> surface_patch_ids;
domain.add_features_and_incidences(polylines_item->polylines.begin(),
polylines_item->polylines.end(),
CGAL::Identity_property_map<Scene_polylines_item::Polyline>(),
CGAL::Constant_property_map<Scene_polylines_item::Polyline, Surface_patch_ids>(
surface_patch_ids));
}
C3t3 c3t3 = CGAL::make_mesh_3<C3t3>(domain, criteria,
p::no_perturb(),
p::no_exude(),
manifold_option);
const Tr& tr = c3t3.triangulation();
timer.stop();
std::cerr << "done (" << timer.time() << " ms, " << tr.number_of_vertices() << " vertices)" << std::endl;
if(tr.number_of_vertices() > 0)
{
typedef typename Result_type<Mesh>::type Result_mesh;
// add remesh as new polyhedron
Result_mesh *pRemesh = new Result_mesh;
//if the thread is interrupted before the mesh is returned, delete it.
MeshGuard guard(pRemesh);
CGAL::facets_in_complex_3_to_triangle_mesh(c3t3, *pRemesh);
guard.setDone();
if(CGAL::is_closed(*pRemesh)
&& ! CGAL::Polygon_mesh_processing::is_outward_oriented(*pRemesh))
{
CGAL::Polygon_mesh_processing::reverse_face_orientations(*pRemesh);
}
return pRemesh;
}
else
return nullptr;
}
struct Mesher_thread:public QThread{
Q_OBJECT
private:
SMesh* sMesh;
Scene_polygon_soup_item* soup_item;
Scene_polylines_item* polylines_item;
const double offset_value;
const double angle;
const double sizing;
const double approx;
const double edge_size;
int tag_index;
public:
Mesher_thread( SMesh* tm_ptr,
Scene_polygon_soup_item* soup_item,
Scene_polylines_item* polylines_item,
const double offset_value,
const double angle,
const double sizing,
const double approx,
const double edge_size,
int tag)
:sMesh(tm_ptr), soup_item(soup_item), polylines_item(polylines_item),
offset_value(offset_value), angle(angle),
sizing(sizing), approx(approx), edge_size(edge_size), tag_index(tag){
}
void run() override {
SMesh* new_mesh= nullptr;
if(soup_item)
new_mesh = cgal_off_meshing(CGAL::Three::Three::mainWindow(),
soup_item,
polylines_item,
offset_value,
angle,
sizing,
approx,
edge_size,
tag_index);
else
new_mesh = cgal_off_meshing(CGAL::Three::Three::mainWindow(),
sMesh,
polylines_item,
offset_value,
angle,
sizing,
approx,
edge_size,
tag_index);
CGAL::Three::Three::getMutex()->lock();
CGAL::Three::Three::getWaitCondition()->wakeAll();
CGAL::Three::Three::getMutex()->unlock();
Q_EMIT resultReady(new_mesh);
}
Q_SIGNALS:
void resultReady(SMesh *new_mesh);
};
using namespace CGAL::Three;
class CGAL_Lab_offset_meshing_plugin :
public QObject,
protected CGAL_Lab_plugin_interface
{
Q_OBJECT
Q_INTERFACES(CGAL::Three::CGAL_Lab_plugin_interface)
Q_PLUGIN_METADATA(IID "com.geometryfactory.CGALLab.PluginInterface/1.0")
public:
void init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* scene_interface, Messages_interface*) {
this->scene = scene_interface;
this->mw = mainWindow;
actionOffsetMeshing = new QAction(tr("Offset Meshing"), mw);
actionOffsetMeshing->setProperty("subMenuName", "3D Surface Mesh Generation");
if(actionOffsetMeshing) {
connect(actionOffsetMeshing, SIGNAL(triggered()),
this, SLOT(offset_meshing()));
}
actionInflateMesh= new QAction(tr("Inflate Mesh"), mw);
actionInflateMesh->setProperty("subMenuName", "Operations on Polyhedra");
if(actionInflateMesh) {
connect(actionInflateMesh, SIGNAL(triggered()),
this, SLOT(inflate_mesh()));
}
}
bool applicable(QAction*) const {
if ( scene->selectionIndices().size() != 1 &&
scene->selectionIndices().size() != 2 )
{
return false;
}
for(CGAL::Three::Scene_interface::Item_id index : scene->selectionIndices())
{
if ( qobject_cast<Scene_surface_mesh_item*>(scene->item(index)) ||
qobject_cast<Scene_polygon_soup_item*>(scene->item(index)) )
return true;
}
return false;
}
QList<QAction*> actions() const {
return QList<QAction*>() << actionOffsetMeshing
<< actionInflateMesh;
}
public Q_SLOTS:
void offset_meshing();
void inflate_mesh();
private:
QAction* actionOffsetMeshing;
QAction* actionInflateMesh;
Scene_interface *scene;
QMainWindow *mw;
}; // end class CGAL_Lab_offset_meshing_plugin
void CGAL_Lab_offset_meshing_plugin::inflate_mesh()
{
const CGAL::Three::Scene_interface::Item_id index = scene->mainSelectionIndex();
Scene_item* item = scene->item(index);
if(item == nullptr){
return;
}
Scene_surface_mesh_item* sm_item =
qobject_cast<Scene_surface_mesh_item*>(item);
if(sm_item == nullptr){
return;
}
SMesh* sMesh = sm_item->face_graph();
if(sMesh == nullptr){
return;
}
double diag = sm_item->diagonalBbox();
double offset_value = QInputDialog::getDouble(mw,
QString("Choose Inflate Distance"),
QString("Inflate Distance (use negative number for deflate)"),
0.1*diag,
-(std::numeric_limits<double>::max)(),
(std::numeric_limits<double>::max)(), 10);
auto vpm = get(CGAL::vertex_point,*sMesh);
auto vnm =
sMesh->property_map<vertex_descriptor, EPICK::Vector_3 >("v:normal").value();
for(const auto& v : vertices(*sMesh))
{
Point_3 p = get(vpm, v);
EPICK::Vector_3 n = get(vnm, v);
n/=(CGAL::sqrt(n.squared_length()));
put(vpm, v, p + offset_value*n);
}
sm_item->invalidateOpenGLBuffers();
}
void CGAL_Lab_offset_meshing_plugin::offset_meshing()
{
Scene_surface_mesh_item* sm_item = nullptr;
Scene_polygon_soup_item* soup_item = nullptr;
Scene_polylines_item* polylines_item = nullptr;
Scene_item* item = nullptr;
bool mesh_or_soup_item_found = false;
for(CGAL::Three::Scene_interface::Item_id index : scene->selectionIndices())
{
if (!mesh_or_soup_item_found)
{
sm_item = qobject_cast<Scene_surface_mesh_item*>(scene->item(index));
if (sm_item == nullptr)
{
soup_item = qobject_cast<Scene_polygon_soup_item*>(item);
if (soup_item != nullptr)
{
item=scene->item(index);
mesh_or_soup_item_found = true;
continue;
}
}
else
{
item=scene->item(index);
mesh_or_soup_item_found = true;
continue;
}
}
polylines_item = qobject_cast<Scene_polylines_item*>(scene->item(index));
}
SMesh* sMesh = nullptr;
double diag = 0;
Scene_item::Bbox box;
if(sm_item)
{
sMesh = sm_item->face_graph();
if(!sMesh)
return;
box = bbox(sMesh);
}
else if(soup_item != nullptr)
{
box = bbox(soup_item);
}
else if(soup_item == nullptr)
return;
double X=(box.max)(0)-(box.min)(0),
Y = (box.max)(1)-(box.min)(1),
Z = (box.max)(2)-(box.min)(2);
diag = CGAL::sqrt(X*X+Y*Y+Z*Z);
double offset_value = QInputDialog::getDouble(mw,
QString("Choose Offset Value"),
QString("Offset Value (use negative number for inset)"),
0.1*diag,
-(std::numeric_limits<double>::max)(),
(std::numeric_limits<double>::max)(), 10);
QDialog dialog(mw);
Ui::Remeshing_dialog ui;
ui.setupUi(&dialog);
ui.angle->setRange(1.0, 30.0);
connect(ui.buttonBox, SIGNAL(accepted()),
&dialog, SLOT(accept()));
connect(ui.buttonBox, SIGNAL(rejected()),
&dialog, SLOT(reject()));
ui.sizing->setRange(diag * 10e-6, // min
diag); // max
ui.sizing->setValue(diag * 0.05); // default value
ui.approx->setRange(diag * 10e-7, // min
diag); // max
ui.approx->setValue(diag * 0.005);
if (polylines_item!=nullptr)
{
ui.edge_sizing->setRange(diag * 10e-6, // min
diag); // max
ui.edge_sizing->setValue(diag * 0.05); // default value
}
else
ui.edge_sizing->setEnabled(false);
int i = dialog.exec();
if(i == QDialog::Rejected)
return;
const double angle = ui.angle->value();
const double approx = ui.approx->value();
const double sizing = ui.sizing->value();
const double edge_size=polylines_item!=nullptr?ui.edge_sizing->value():0;
const int tag_index = ui.tags->currentIndex();
if(tag_index < 0) return;
QApplication::setOverrideCursor(Qt::WaitCursor);
std::cerr << "mesh with:"
<< "\n angle=" << angle
<< "\n sizing=" << sizing
<< "\n approx=" << approx
<< "\n tag=" << tag_index
<< std::boolalpha
<< std::endl;
Mesher_thread* worker = nullptr;
if(soup_item)
worker = new Mesher_thread(nullptr,
soup_item,
polylines_item,
offset_value,
angle,
sizing,
approx,
edge_size,
tag_index);
else
worker = new Mesher_thread(sMesh,
nullptr,
polylines_item,
offset_value,
angle,
sizing,
approx,
edge_size,
tag_index);
connect(worker, &QThread::finished, worker, &QObject::deleteLater);
connect(worker, &Mesher_thread::resultReady, this,
[item, angle, sizing, approx, offset_value/* , index */]
(SMesh *new_mesh){
QApplication::restoreOverrideCursor();
if(!new_mesh){
CGAL::Three::Three::getMutex()->lock();
CGAL::Three::Three::isLocked() = false;
CGAL::Three::Three::getMutex()->unlock();
return;
}
Scene_surface_mesh_item* new_item = new Scene_surface_mesh_item(new_mesh);
new_item->setName(tr("%1 offset %5 (%2 %3 %4)")
.arg(item->name())
.arg(angle)
.arg(sizing)
.arg(approx)
.arg(offset_value));
new_item->setColor(Qt::magenta);
new_item->setWireframeMode();
CGAL::Three::Three::scene()->addItem(new_item);
// CGAL::Three::Three::scene()->itemChanged(index);
QApplication::restoreOverrideCursor();
CGAL::Three::Three::getMutex()->lock();
CGAL::Three::Three::isLocked() = false;
CGAL::Three::Three::getMutex()->unlock();
});
QMessageBox* message_box = new QMessageBox(QMessageBox::NoIcon,
"Meshing",
"Offset meshing in progress...",
QMessageBox::Cancel,
mw);
message_box->setDefaultButton(QMessageBox::Cancel);
QAbstractButton* cancelButton = message_box->button(QMessageBox::Cancel);
cancelButton->setText(tr("Stop"));
connect(cancelButton, &QAbstractButton::clicked,
this, [worker](){
worker->terminate();
QApplication::restoreOverrideCursor();//waitcursor
QApplication::restoreOverrideCursor();//busycursor
});
connect(worker, &Mesher_thread::finished,
message_box, &QMessageBox::close);
message_box->open();
QApplication::setOverrideCursor(Qt::BusyCursor);
CGAL::Three::Three::getMutex()->lock();
CGAL::Three::Three::isLocked() = true;
CGAL::Three::Three::getMutex()->unlock();
worker->start();
}
#include "Offset_meshing_plugin.moc"
#endif // CGAL_LAB_DEMO_USE_SURFACE_MESHER