mirror of https://github.com/CGAL/cgal
578 lines
18 KiB
C++
578 lines
18 KiB
C++
#include "config.h"
|
|
#include "config_mesh_3.h"
|
|
|
|
#ifdef CGAL_POLYHEDRON_DEMO_USE_SURFACE_MESHER
|
|
#include <CGAL/Three/Polyhedron_demo_plugin_interface.h>
|
|
#include "Messages_interface.h"
|
|
|
|
#include <QObject>
|
|
#include <QAction>
|
|
#include <QMainWindow>
|
|
#include <QApplication>
|
|
#include <QtPlugin>
|
|
#include "Scene_c3t3_item.h"
|
|
#include <QInputDialog>
|
|
#include <QFileDialog>
|
|
#include <QMessageBox>
|
|
#include <fstream>
|
|
|
|
#include "Scene_polyhedron_item.h"
|
|
#include "Scene_polylines_item.h"
|
|
|
|
#ifdef CGAL_MESH_3_DEMO_ACTIVATE_IMPLICIT_FUNCTIONS
|
|
#include "Scene_implicit_function_item.h"
|
|
#endif
|
|
#ifdef CGAL_MESH_3_DEMO_ACTIVATE_SEGMENTED_IMAGES
|
|
#include "Scene_image_item.h"
|
|
#endif
|
|
|
|
#include "Meshing_thread.h"
|
|
|
|
#include "ui_Meshing_dialog.h"
|
|
|
|
using namespace CGAL::Three;
|
|
|
|
// Constants
|
|
const QColor default_mesh_color(45,169,70);
|
|
|
|
#include "Mesh_3_plugin_cgal_code.h" // declare functions `cgal_code_mesh_3`
|
|
#include "split_polylines.h"
|
|
|
|
class Mesh_3_plugin :
|
|
public QObject,
|
|
protected Polyhedron_demo_plugin_interface
|
|
{
|
|
Q_OBJECT
|
|
Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface)
|
|
Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0")
|
|
|
|
public:
|
|
void init(QMainWindow* mainWindow,
|
|
CGAL::Three::Scene_interface* scene_interface,
|
|
Messages_interface* msg_interface)
|
|
{
|
|
this->scene = scene_interface;
|
|
this->mw = mainWindow;
|
|
|
|
actionMesh_3 = new QAction("Create a Tetrahedral Mesh", mw);
|
|
if(actionMesh_3) {
|
|
actionMesh_3->setProperty("subMenuName", "Tetrahedral Mesh Generation");
|
|
connect(actionMesh_3, SIGNAL(triggered()),
|
|
this, SLOT(mesh_3_volume()));
|
|
}
|
|
|
|
actionMesh_3_surface = new QAction("Create a Surface Triangle Mesh", mw);
|
|
if (actionMesh_3_surface){
|
|
actionMesh_3_surface->setProperty("subMenuName", "Tetrahedral Mesh Generation");
|
|
connect(actionMesh_3_surface, SIGNAL(triggered()),
|
|
this, SLOT(mesh_3_surface()));
|
|
}
|
|
|
|
actionSplitPolylines = new QAction("Split polylines in a graph", mw);
|
|
actionSplitPolylines->setProperty("subMenuName",
|
|
"Tetrahedral Mesh Generation");
|
|
connect(actionSplitPolylines, &QAction::triggered,
|
|
this, &Mesh_3_plugin::splitPolylines);
|
|
|
|
this->msg = msg_interface;
|
|
}
|
|
|
|
QList<QAction*> actions() const {
|
|
return QList<QAction*>()
|
|
<< actionMesh_3
|
|
<< actionMesh_3_surface
|
|
<< actionSplitPolylines;
|
|
}
|
|
|
|
bool applicable(QAction* a) const {
|
|
if(actionSplitPolylines) {
|
|
return qobject_cast<Scene_polylines_item*>
|
|
(scene->item(scene->mainSelectionIndex())) != 0;
|
|
}
|
|
#ifdef CGAL_MESH_3_DEMO_ACTIVATE_IMPLICIT_FUNCTIONS
|
|
if(qobject_cast<Scene_implicit_function_item*>(scene->item(scene->mainSelectionIndex())) != NULL
|
|
&& a == actionMesh_3)
|
|
return true;
|
|
#endif
|
|
#ifdef CGAL_MESH_3_DEMO_ACTIVATE_SEGMENTED_IMAGES
|
|
Q_FOREACH(int ind, scene->selectionIndices()){
|
|
if( qobject_cast<Scene_image_item*>(scene->item(ind)))
|
|
return true;
|
|
}
|
|
#endif
|
|
Q_FOREACH(int ind, scene->selectionIndices()){
|
|
Scene_polyhedron_item* poly_item
|
|
= qobject_cast<Scene_polyhedron_item*>(scene->item(ind));
|
|
if (NULL == poly_item)
|
|
continue;
|
|
if (a == actionMesh_3) return poly_item->polyhedron()->is_closed();
|
|
else return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public Q_SLOTS:
|
|
void mesh_3_volume();
|
|
void mesh_3_surface();
|
|
void splitPolylines();
|
|
void meshing_done(Meshing_thread* t);
|
|
void status_report(QString str);
|
|
|
|
private:
|
|
void mesh_3(const bool surface_only);
|
|
void launch_thread(Meshing_thread* mesh_thread);
|
|
void treat_result(Scene_item& source_item, Scene_c3t3_item& result_item) const;
|
|
|
|
private:
|
|
QAction* actionMesh_3;
|
|
QAction* actionMesh_3_surface;
|
|
QAction* actionSplitPolylines;
|
|
Messages_interface* msg;
|
|
QMessageBox* message_box_;
|
|
Scene_item* source_item_;
|
|
CGAL::Three::Scene_interface* scene;
|
|
QMainWindow* mw;
|
|
}; // end class Mesh_3_plugin
|
|
|
|
double
|
|
get_approximate(double d, int precision, int& decimals)
|
|
{
|
|
if ( d<0 ) { return 0; }
|
|
|
|
double i = std::pow(10.,precision-1);
|
|
|
|
decimals = 0;
|
|
while ( d > i*10 ) { d = d/10.; ++decimals; }
|
|
while ( d < i ) { d = d*10.; --decimals; }
|
|
|
|
return std::floor(d)*std::pow(10.,decimals);
|
|
}
|
|
|
|
void Mesh_3_plugin::splitPolylines() {
|
|
Scene_item* main_item = scene->item(scene->mainSelectionIndex());
|
|
Scene_polylines_item* polylines_item =
|
|
qobject_cast<Scene_polylines_item*>(main_item);
|
|
if(polylines_item == 0) return;
|
|
|
|
Scene_polylines_item* new_item = new Scene_polylines_item;
|
|
auto new_polylines = split_polylines(polylines_item->polylines);
|
|
new_item->polylines =
|
|
std::move(Polylines_container{new_polylines.begin(), new_polylines.end()});
|
|
new_item->setName(tr("%1 (split)").arg(polylines_item->name()));
|
|
scene->addItem(new_item);
|
|
}
|
|
|
|
void Mesh_3_plugin::mesh_3_surface()
|
|
{
|
|
mesh_3(true);
|
|
}
|
|
void Mesh_3_plugin::mesh_3_volume()
|
|
{
|
|
mesh_3(false);
|
|
}
|
|
|
|
void Mesh_3_plugin::mesh_3(const bool surface_only)
|
|
{
|
|
Scene_polyhedron_item* poly_item = NULL;
|
|
Scene_implicit_function_item* function_item = NULL;
|
|
Scene_image_item* image_item = NULL;
|
|
Scene_polylines_item* polylines_item = NULL;
|
|
|
|
Q_FOREACH(int ind, scene->selectionIndices()) {
|
|
|
|
if(poly_item == NULL){
|
|
poly_item = qobject_cast<Scene_polyhedron_item*>(scene->item(ind));
|
|
}
|
|
#ifdef CGAL_MESH_3_DEMO_ACTIVATE_IMPLICIT_FUNCTIONS
|
|
if(function_item == NULL){
|
|
function_item = qobject_cast<Scene_implicit_function_item*>(scene->item(ind));
|
|
}
|
|
#endif
|
|
#ifdef CGAL_MESH_3_DEMO_ACTIVATE_SEGMENTED_IMAGES
|
|
if(image_item == NULL){
|
|
image_item = qobject_cast<Scene_image_item*>(scene->item(ind));
|
|
}
|
|
#endif
|
|
if(polylines_item == NULL){
|
|
polylines_item = qobject_cast<Scene_polylines_item*>(scene->item(ind));
|
|
}
|
|
}
|
|
Scene_item* item = NULL;
|
|
bool features_protection_available = false;
|
|
if(NULL != poly_item)
|
|
{
|
|
if (!poly_item->polyhedron()->is_pure_triangle())
|
|
{
|
|
QMessageBox::warning(mw, tr(""),
|
|
tr("Selected Scene_polyhedron_item is not triangulated."));
|
|
return;
|
|
}
|
|
item = poly_item;
|
|
features_protection_available = true;
|
|
}
|
|
#ifdef CGAL_MESH_3_DEMO_ACTIVATE_IMPLICIT_FUNCTIONS
|
|
else if (NULL != function_item) { item = function_item; }
|
|
#endif
|
|
#ifdef CGAL_MESH_3_DEMO_ACTIVATE_SEGMENTED_IMAGES
|
|
else if (NULL != image_item)
|
|
{
|
|
item = image_item;
|
|
features_protection_available = (polylines_item != NULL) || !image_item->isGray();
|
|
|
|
bool fit_wrdtp = true;
|
|
std::size_t img_wdim = image_item->image()->image()->wdim;
|
|
WORD_KIND img_wordKind = image_item->image()->image()->wordKind;
|
|
//check if the word type fits the hardcoded values in the plugin
|
|
if(image_item->isGray())
|
|
{
|
|
if(img_wordKind != WK_FLOAT)
|
|
fit_wrdtp = false;
|
|
else
|
|
if(img_wdim != 4)
|
|
fit_wrdtp = false;
|
|
}
|
|
else
|
|
{
|
|
if(img_wordKind != WK_FIXED)
|
|
fit_wrdtp = false;
|
|
else
|
|
if(img_wdim != 1)
|
|
fit_wrdtp = false;
|
|
}
|
|
if(!fit_wrdtp)
|
|
{
|
|
QMessageBox::warning(mw, tr(""),
|
|
tr("Selected object can't be meshed because the image's word type is not supported by this plugin."));
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (NULL == item)
|
|
{
|
|
QMessageBox::warning(mw, tr(""),
|
|
tr("Selected object can't be meshed"));
|
|
return;
|
|
}
|
|
// -----------------------------------
|
|
// Create Mesh dialog
|
|
// -----------------------------------
|
|
QDialog dialog(mw);
|
|
Ui::Meshing_dialog ui;
|
|
ui.setupUi(&dialog);
|
|
dialog.setWindowFlags(Qt::Dialog|Qt::CustomizeWindowHint|Qt::WindowCloseButtonHint);
|
|
connect(ui.buttonBox, SIGNAL(accepted()),
|
|
&dialog, SLOT(accept()));
|
|
connect(ui.buttonBox, SIGNAL(rejected()),
|
|
&dialog, SLOT(reject()));
|
|
|
|
// Connect checkboxes to spinboxes
|
|
connect(ui.noApprox, SIGNAL(toggled(bool)),
|
|
ui.approx, SLOT(setEnabled(bool)));
|
|
|
|
connect(ui.noFacetSizing, SIGNAL(toggled(bool)),
|
|
ui.facetSizing, SLOT(setEnabled(bool)));
|
|
|
|
connect(ui.noAngle, SIGNAL(toggled(bool)),
|
|
ui.facetAngle, SLOT(setEnabled(bool)));
|
|
|
|
connect(ui.noTetSizing, SIGNAL(toggled(bool)),
|
|
ui.tetSizing, SLOT(setEnabled(bool)));
|
|
|
|
connect(ui.noTetShape, SIGNAL(toggled(bool)),
|
|
ui.tetShape, SLOT(setEnabled(bool)));
|
|
|
|
connect(ui.protect, SIGNAL(toggled(bool)),
|
|
ui.noEdgeSizing, SLOT(setEnabled(bool)));
|
|
|
|
connect(ui.protect, SIGNAL(toggled(bool)),
|
|
ui.noEdgeSizing, SLOT(setChecked(bool)));
|
|
|
|
connect(ui.noEdgeSizing, SIGNAL(toggled(bool)),
|
|
ui.edgeLabel, SLOT(setEnabled(bool)));
|
|
|
|
connect(ui.noEdgeSizing, SIGNAL(toggled(bool)),
|
|
ui.edgeSizing, SLOT(setEnabled(bool)));
|
|
|
|
// Set default parameters
|
|
CGAL::Three::Scene_interface::Bbox bbox = item->bbox();
|
|
ui.objectName->setText(item->name());
|
|
ui.objectNameSize->setText(tr("Object bbox size (w,h,d): <b>%1</b>, <b>%2</b>, <b>%3</b>")
|
|
.arg(bbox.xmax() - bbox.xmin(),0,'g',3)
|
|
.arg(bbox.ymax() - bbox.ymin(),0,'g',3)
|
|
.arg(bbox.zmax() - bbox.zmin(),0,'g',3) );
|
|
|
|
double diag = CGAL::sqrt((bbox.xmax()-bbox.xmin())*(bbox.xmax()-bbox.xmin()) + (bbox.ymax()-bbox.ymin())*(bbox.ymax()-bbox.ymin()) + (bbox.zmax()-bbox.zmin())*(bbox.zmax()-bbox.zmin()));
|
|
int decimals = 0;
|
|
double sizing_default = get_approximate(diag * 0.05, 2, decimals);
|
|
ui.facetSizing->setDecimals(-decimals+2);
|
|
ui.facetSizing->setSingleStep(std::pow(10.,decimals));
|
|
ui.facetSizing->setRange(diag * 10e-6, // min
|
|
diag); // max
|
|
ui.facetSizing->setValue(sizing_default); // default value
|
|
ui.edgeSizing->setValue(sizing_default);
|
|
|
|
ui.tetSizing->setDecimals(-decimals+2);
|
|
ui.tetSizing->setSingleStep(std::pow(10.,decimals));
|
|
ui.tetSizing->setRange(diag * 10e-6, // min
|
|
diag); // max
|
|
ui.tetSizing->setValue(sizing_default); // default value
|
|
|
|
double approx_default = get_approximate(diag * 0.005, 2, decimals);
|
|
ui.approx->setDecimals(-decimals+2);
|
|
ui.approx->setSingleStep(std::pow(10.,decimals));
|
|
ui.approx->setRange(diag * 10e-7, // min
|
|
diag); // max
|
|
ui.approx->setValue(approx_default);
|
|
|
|
ui.protect->setEnabled(features_protection_available);
|
|
ui.protect->setChecked(features_protection_available);
|
|
|
|
ui.initializationGroup->setVisible(image_item != NULL && !image_item->isGray());
|
|
ui.grayImgGroup->setVisible(image_item != NULL && image_item->isGray());
|
|
if (poly_item != NULL)
|
|
ui.volumeGroup->setVisible(!surface_only && poly_item->polyhedron()->is_closed());
|
|
else
|
|
ui.volumeGroup->setVisible(!surface_only);
|
|
ui.noEdgeSizing->setChecked(ui.protect->isChecked());
|
|
ui.edgeLabel->setEnabled(ui.noEdgeSizing->isChecked());
|
|
ui.edgeSizing->setEnabled(ui.noEdgeSizing->isChecked());
|
|
|
|
// -----------------------------------
|
|
// Get values
|
|
// -----------------------------------
|
|
|
|
//reset cursor from the code for the scripts
|
|
QApplication::restoreOverrideCursor();
|
|
int i = dialog.exec();
|
|
if( i == QDialog::Rejected ) { return; }
|
|
|
|
// 0 means parameter is not considered
|
|
const double angle = !ui.noAngle->isChecked() ? 0 : ui.facetAngle->value();
|
|
const double approx = !ui.noApprox->isChecked() ? 0 : ui.approx->value();
|
|
const double facet_sizing = !ui.noFacetSizing->isChecked() ? 0 : ui.facetSizing->value();
|
|
const double radius_edge = !ui.noTetShape->isChecked() ? 0 : ui.tetShape->value();
|
|
const double tet_sizing = !ui.noTetSizing->isChecked() ? 0 : ui.tetSizing->value();
|
|
const double edge_size = !ui.noEdgeSizing->isChecked() ? DBL_MAX : ui.edgeSizing->value();
|
|
const bool protect_features = ui.protect->isChecked();
|
|
const bool detect_connected_components = ui.detectComponents->isChecked();
|
|
const int manifold = ui.manifoldCheckBox->isChecked() ? 1 : 0;
|
|
const float iso_value = ui.iso_value_spinBox->value();
|
|
const float value_outside = ui.value_outside_spinBox->value();
|
|
const float inside_is_less = ui.inside_is_less_checkBox->isChecked();
|
|
|
|
|
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
|
|
|
Meshing_thread* thread = NULL;
|
|
|
|
// Polyhedron
|
|
if ( NULL != poly_item )
|
|
{
|
|
Polyhedron* pMesh = poly_item->polyhedron();
|
|
if (NULL == pMesh)
|
|
{
|
|
QMessageBox::critical(mw, tr(""), tr("ERROR: no data in selected item"));
|
|
return;
|
|
}
|
|
|
|
Scene_polylines_item::Polylines_container plc;
|
|
|
|
thread = cgal_code_mesh_3(pMesh,
|
|
(polylines_item == NULL)?plc:polylines_item->polylines,
|
|
item->name(),
|
|
angle,
|
|
facet_sizing,
|
|
approx,
|
|
tet_sizing,
|
|
edge_size,
|
|
radius_edge,
|
|
protect_features,
|
|
manifold,
|
|
surface_only,
|
|
scene);
|
|
}
|
|
// Image
|
|
#ifdef CGAL_MESH_3_DEMO_ACTIVATE_IMPLICIT_FUNCTIONS
|
|
else if (NULL != function_item)
|
|
{
|
|
const Implicit_function_interface* pFunction = function_item->function();
|
|
if (NULL == pFunction)
|
|
{
|
|
QMessageBox::critical(mw, tr(""), tr("ERROR: no data in selected item"));
|
|
return;
|
|
}
|
|
|
|
thread = cgal_code_mesh_3(pFunction,
|
|
angle,
|
|
facet_sizing,
|
|
approx,
|
|
tet_sizing,
|
|
edge_size,
|
|
radius_edge,
|
|
manifold,
|
|
scene);
|
|
}
|
|
#endif
|
|
#ifdef CGAL_MESH_3_DEMO_ACTIVATE_SEGMENTED_IMAGES
|
|
else if (NULL != image_item)
|
|
{
|
|
const Image* pImage = image_item->image();
|
|
if (NULL == pImage)
|
|
{
|
|
QMessageBox::critical(mw, tr(""), tr("ERROR: no data in selected item"));
|
|
return;
|
|
}
|
|
|
|
Scene_polylines_item::Polylines_container plc;
|
|
|
|
thread = cgal_code_mesh_3(pImage,
|
|
(polylines_item == NULL)?plc:polylines_item->polylines,
|
|
angle,
|
|
facet_sizing,
|
|
approx,
|
|
tet_sizing,
|
|
edge_size,
|
|
radius_edge,
|
|
protect_features,
|
|
manifold,
|
|
scene,
|
|
detect_connected_components,
|
|
image_item->isGray(),
|
|
iso_value,
|
|
value_outside,
|
|
inside_is_less);
|
|
}
|
|
#endif
|
|
|
|
if ( NULL == thread )
|
|
{
|
|
QMessageBox::critical(mw,tr(""),tr("ERROR: no thread created"));
|
|
return;
|
|
}
|
|
|
|
// Launch thread
|
|
source_item_ = item;
|
|
launch_thread(thread);
|
|
|
|
QApplication::restoreOverrideCursor();
|
|
}
|
|
|
|
void
|
|
Mesh_3_plugin::
|
|
launch_thread(Meshing_thread* mesh_thread)
|
|
{
|
|
// -----------------------------------
|
|
// Create message box with stop button
|
|
// -----------------------------------
|
|
message_box_ = new QMessageBox(QMessageBox::NoIcon,
|
|
"Meshing",
|
|
"Mesh generation in progress...",
|
|
QMessageBox::Cancel,
|
|
mw);
|
|
|
|
message_box_->setDefaultButton(QMessageBox::Cancel);
|
|
QAbstractButton* cancelButton = message_box_->button(QMessageBox::Cancel);
|
|
cancelButton->setText(tr("Stop"));
|
|
|
|
QObject::connect(cancelButton, SIGNAL(clicked()),
|
|
mesh_thread, SLOT(stop()));
|
|
|
|
message_box_->show();
|
|
|
|
// -----------------------------------
|
|
// Connect main thread to meshing thread
|
|
// -----------------------------------
|
|
QObject::connect(mesh_thread, SIGNAL(done(Meshing_thread*)),
|
|
this, SLOT(meshing_done(Meshing_thread*)));
|
|
|
|
QObject::connect(mesh_thread, SIGNAL(status_report(QString)),
|
|
this, SLOT(status_report(QString)));
|
|
|
|
// -----------------------------------
|
|
// Launch mesher
|
|
// -----------------------------------
|
|
mesh_thread->start();
|
|
}
|
|
|
|
|
|
void
|
|
Mesh_3_plugin::
|
|
status_report(QString str)
|
|
{
|
|
if ( NULL == message_box_ ) { return; }
|
|
|
|
message_box_->setInformativeText(str);
|
|
}
|
|
|
|
|
|
void
|
|
Mesh_3_plugin::
|
|
meshing_done(Meshing_thread* thread)
|
|
{
|
|
// Print message in console
|
|
QString str = QString("Meshing of \"%1\" done in %2s<br>")
|
|
.arg(source_item_->name())
|
|
.arg(thread->time());
|
|
|
|
Q_FOREACH( QString param, thread->parameters_log() )
|
|
{
|
|
str.append(QString("( %1 )<br>").arg(param));
|
|
}
|
|
|
|
Scene_c3t3_item* result_item = thread->item();
|
|
const Scene_item::Bbox& bbox = result_item->bbox();
|
|
str.append(QString("BBox (x,y,z): [ %1, %2 ], [ %3, %4 ], [ %5, %6 ], <br>")
|
|
.arg(bbox.xmin())
|
|
.arg(bbox.xmax())
|
|
.arg(bbox.ymin())
|
|
.arg(bbox.ymax())
|
|
.arg(bbox.zmin())
|
|
.arg(bbox.zmax()));
|
|
|
|
msg->information(qPrintable(str));
|
|
|
|
// Treat new c3t3 item
|
|
treat_result(*source_item_, *result_item);
|
|
|
|
// close message box
|
|
message_box_->close();
|
|
message_box_ = NULL;
|
|
|
|
// free memory
|
|
// TODO: maybe there is another way to do that
|
|
delete thread;
|
|
}
|
|
|
|
|
|
void
|
|
Mesh_3_plugin::
|
|
treat_result(Scene_item& source_item,
|
|
Scene_c3t3_item& result_item) const
|
|
{
|
|
result_item.setName(tr("%1 [3D Mesh]").arg(source_item.name()));
|
|
|
|
result_item.c3t3_changed();
|
|
|
|
const Scene_item::Bbox& bbox = result_item.bbox();
|
|
result_item.setPosition((bbox.xmin() + bbox.xmax())/2.f,
|
|
(bbox.ymin() + bbox.ymax())/2.f,
|
|
(bbox.zmin() + bbox.zmax())/2.f);
|
|
|
|
result_item.setColor(default_mesh_color);
|
|
result_item.setRenderingMode(source_item.renderingMode());
|
|
result_item.set_data_item(&source_item);
|
|
|
|
source_item.setVisible(false);
|
|
|
|
const Scene_interface::Item_id index = scene->mainSelectionIndex();
|
|
scene->itemChanged(index);
|
|
scene->setSelectedItem(-1);
|
|
Scene_interface::Item_id new_item_id = scene->addItem(&result_item);
|
|
scene->setSelectedItem(new_item_id);
|
|
}
|
|
|
|
#include "Mesh_3_plugin.moc"
|
|
|
|
#endif // CGAL_POLYHEDRON_DEMO_USE_SURFACE_MESHER
|