mirror of https://github.com/CGAL/cgal
Mesh plane detection plugin
This commit is contained in:
parent
42e3d15972
commit
30fca3ba84
|
|
@ -27,7 +27,7 @@ polyhedron_demo_plugin(mesh_segmentation_plugin Mesh_segmentation_plugin ${segme
|
|||
target_link_libraries(mesh_segmentation_plugin scene_polyhedron_item scene_surface_mesh_item)
|
||||
|
||||
qt5_wrap_ui( mesh_plane_detectionUI_FILES Mesh_plane_detection_dialog.ui)
|
||||
polyhedron_demo_plugin(mesh_plane_detection_plugin Mesh_plane_detection_plugin Mesh_plane_detection_impl ${mesh_plane_detectionUI_FILES})
|
||||
polyhedron_demo_plugin(mesh_plane_detection_plugin Mesh_plane_detection_plugin ${mesh_plane_detectionUI_FILES})
|
||||
target_link_libraries(mesh_plane_detection_plugin scene_polyhedron_item)
|
||||
|
||||
qt5_wrap_ui( mesh_simplificationUI_FILES Mesh_simplification_dialog.ui)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,130 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Mesh_plane_detection_dialog</class>
|
||||
<widget class="QDialog" name="Mesh_plane_detection_dialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>349</width>
|
||||
<height>117</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Mesh Plane Detection</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="minimumAreaLabel">
|
||||
<property name="text">
|
||||
<string>Minimum area</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QDoubleSpinBox" name="minimumAreaDoubleSpinBox">
|
||||
<property name="decimals">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>0.000010000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>1000000.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.010000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>0.010000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="maximumDeviationFromNormalLabel">
|
||||
<property name="text">
|
||||
<string>Maximum deviation from normal</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="maximumDeviationFromNormalSpinBox">
|
||||
<property name="suffix">
|
||||
<string>°</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>90</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>30</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>13</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>Mesh_plane_detection_dialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>Mesh_plane_detection_dialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
|
|
@ -0,0 +1,260 @@
|
|||
#include <CGAL/Three/Polyhedron_demo_plugin_helper.h>
|
||||
#include <CGAL/Three/Polyhedron_demo_plugin_interface.h>
|
||||
|
||||
#include "Scene_polyhedron_item.h"
|
||||
#include "Polyhedron_type.h"
|
||||
#include "Scene.h"
|
||||
#include "Color_map.h"
|
||||
|
||||
#include <CGAL/mesh_segmentation.h>
|
||||
#include <QApplication>
|
||||
#include <QMainWindow>
|
||||
#include <QInputDialog>
|
||||
#include <QTime>
|
||||
#include <QAction>
|
||||
#include <QDebug>
|
||||
#include <QObject>
|
||||
#include <QDockWidget>
|
||||
//#include <QtConcurrentRun>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <CGAL/property_map.h>
|
||||
#include <CGAL/Polygon_mesh_processing/measure.h>
|
||||
#include <CGAL/Polygon_mesh_processing/compute_normal.h>
|
||||
|
||||
#include "ui_Mesh_plane_detection_dialog.h"
|
||||
|
||||
namespace PMP = CGAL::Polygon_mesh_processing;
|
||||
|
||||
using namespace CGAL::Three;
|
||||
class Polyhedron_demo_mesh_plane_detection_plugin :
|
||||
public QObject,
|
||||
public Polyhedron_demo_plugin_helper
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface)
|
||||
Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0")
|
||||
|
||||
public:
|
||||
|
||||
QList<QAction*> actions() const {
|
||||
return QList<QAction*>() << actionPlaneDetection;
|
||||
}
|
||||
|
||||
bool applicable(QAction*) const {
|
||||
return
|
||||
qobject_cast<Scene_polyhedron_item*>(scene->item(scene->mainSelectionIndex()));
|
||||
}
|
||||
|
||||
void init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* scene_interface, Messages_interface*) {
|
||||
this->scene = scene_interface;
|
||||
this->mw = mainWindow;
|
||||
actionPlaneDetection = new QAction("Mesh Plane Detection", mw);
|
||||
actionPlaneDetection->setProperty("subMenuName", "Triangulated Surface Mesh Segmentation");
|
||||
actionPlaneDetection->setObjectName("actionPlaneDetection");
|
||||
|
||||
// adding slot for itemAboutToBeDestroyed signal, aim is removing item from item-functor map.
|
||||
if( Scene* scene = dynamic_cast<Scene*>(scene_interface) ) {
|
||||
connect(scene, SIGNAL(itemAboutToBeDestroyed(CGAL::Three::Scene_item*)), this, SLOT(itemAboutToBeDestroyed(CGAL::Three::Scene_item*)));
|
||||
}
|
||||
|
||||
autoConnectActions();
|
||||
}
|
||||
virtual void closure()
|
||||
{
|
||||
}
|
||||
|
||||
template<class SegmentPropertyMap>
|
||||
void colorize_segmentation(Scene_polyhedron_item* item, SegmentPropertyMap& segment_ids, std::vector<QColor>& color_vector);
|
||||
|
||||
void check_and_set_ids(Polyhedron* polyhedron);
|
||||
|
||||
public Q_SLOTS:
|
||||
void on_actionPlaneDetection_triggered();
|
||||
void itemAboutToBeDestroyed(CGAL::Three::Scene_item*);
|
||||
private:
|
||||
QAction* actionPlaneDetection;
|
||||
|
||||
|
||||
template <typename OutputIterator>
|
||||
void detect_planes_in_mesh (Polyhedron& mesh, const double area_min,
|
||||
const double angle_max,
|
||||
OutputIterator output)
|
||||
{
|
||||
std::cerr << "Detecting planes with:" << std::endl
|
||||
<< " * Area min = " << area_min << std::endl
|
||||
<< " * Min cos angle = " << angle_max << std::endl;
|
||||
std::vector<std::size_t> label_region(mesh.size_of_facets(), 0);
|
||||
int class_index = 0;
|
||||
|
||||
std::vector<typename Polyhedron::Facet_handle> facets (mesh.size_of_facets());
|
||||
for (typename Polyhedron::Facet_iterator f = mesh.facets_begin(); f != mesh.facets_end(); ++f)
|
||||
facets[f->id()] = f;
|
||||
|
||||
for (typename Polyhedron::Facet_iterator f = mesh.facets_begin(); f != mesh.facets_end(); ++f)
|
||||
{
|
||||
if (label_region[f->id()] != 0)
|
||||
continue;
|
||||
class_index++;
|
||||
label_region[f->id()] = class_index;
|
||||
double area = PMP::face_area (f, mesh, PMP::parameters::geom_traits(Kernel()));
|
||||
|
||||
//characteristics of the seed
|
||||
Kernel::Vector_3 normal_seed = PMP::compute_face_normal (f, mesh, PMP::parameters::geom_traits(Kernel()));
|
||||
Kernel::Point_3 pt_seed = f->halfedge()->vertex()->point();
|
||||
Kernel::Plane_3 optimal_plane(pt_seed, normal_seed);
|
||||
// Kernel::Plane_3 optimal_plane = f->plane();
|
||||
|
||||
//initialization containers
|
||||
std::vector<std::size_t> index_container (1, f->id());
|
||||
std::vector<std::size_t> index_container_former_ring (1, f->id());
|
||||
std::list<std::size_t> index_container_current_ring;
|
||||
|
||||
//propagation
|
||||
bool propagation = true;
|
||||
do{
|
||||
|
||||
propagation = false;
|
||||
|
||||
for (std::size_t k = 0; k < index_container_former_ring.size(); k++)
|
||||
{
|
||||
typename Polyhedron::Halfedge_around_facet_circulator
|
||||
circ = facets[index_container_former_ring[k]]->facet_begin(), start = circ;
|
||||
|
||||
do
|
||||
{
|
||||
if (circ->is_border_edge())
|
||||
continue;
|
||||
|
||||
typename Polyhedron::Facet_handle
|
||||
neighbor = circ->opposite()->facet();
|
||||
std::size_t neighbor_index = neighbor->id();
|
||||
if (label_region[neighbor_index] == 0)
|
||||
{
|
||||
Kernel::Vector_3 normal
|
||||
= PMP::compute_face_normal (neighbor, mesh, PMP::parameters::geom_traits(Kernel()));
|
||||
|
||||
if (std::fabs(normal * optimal_plane.orthogonal_vector()) > angle_max)
|
||||
{
|
||||
label_region[neighbor_index] = class_index;
|
||||
propagation = true;
|
||||
index_container_current_ring.push_back(neighbor_index);
|
||||
area += PMP::face_area (neighbor, mesh, PMP::parameters::geom_traits(Kernel()));
|
||||
}
|
||||
}
|
||||
}
|
||||
while (++ circ != start);
|
||||
}
|
||||
|
||||
//update containers
|
||||
index_container_former_ring.clear();
|
||||
for (std::list<std::size_t>::iterator it = index_container_current_ring.begin();
|
||||
it != index_container_current_ring.end(); ++it)
|
||||
{
|
||||
index_container_former_ring.push_back(*it);
|
||||
index_container.push_back(*it);
|
||||
}
|
||||
index_container_current_ring.clear();
|
||||
|
||||
} while (propagation);
|
||||
|
||||
//Test the number of inliers -> reject if inferior to Nmin
|
||||
if (area < area_min)
|
||||
{
|
||||
class_index--;
|
||||
label_region[f->id()] = 0;
|
||||
for (std::size_t k = 0; k < index_container.size(); k++)
|
||||
label_region[index_container[k]] = 0;
|
||||
}
|
||||
}
|
||||
std::cerr << class_index << " planes detected" << std::endl;
|
||||
|
||||
std::copy (label_region.begin(), label_region.end(), output);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
void Polyhedron_demo_mesh_plane_detection_plugin::itemAboutToBeDestroyed(CGAL::Three::Scene_item*)
|
||||
{
|
||||
}
|
||||
|
||||
void Polyhedron_demo_mesh_plane_detection_plugin::on_actionPlaneDetection_triggered()
|
||||
{
|
||||
const CGAL::Three::Scene_interface::Item_id index = scene->mainSelectionIndex();
|
||||
|
||||
Scene_polyhedron_item* poly_item =
|
||||
qobject_cast<Scene_polyhedron_item*>(scene->item(index));
|
||||
|
||||
if (poly_item)
|
||||
{
|
||||
Polyhedron& pmesh =*poly_item->polyhedron();
|
||||
|
||||
QDialog dialog(mw);
|
||||
Ui::Mesh_plane_detection_dialog ui;
|
||||
ui.setupUi(&dialog);
|
||||
|
||||
// check user cancellation
|
||||
if(dialog.exec() == QDialog::Rejected)
|
||||
return;
|
||||
|
||||
QTime time;
|
||||
time.start();
|
||||
std::cerr << "Detecting planes... ";
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
QApplication::processEvents();
|
||||
|
||||
check_and_set_ids (&pmesh);
|
||||
std::vector<int> indices;
|
||||
detect_planes_in_mesh (pmesh,
|
||||
ui.minimumAreaDoubleSpinBox->value(),
|
||||
std::fabs(std::cos (CGAL_PI * ui.maximumDeviationFromNormalSpinBox->value() / 180.)),
|
||||
std::back_inserter (indices));
|
||||
|
||||
poly_item->set_color_vector_read_only(true);
|
||||
colorize_segmentation (poly_item, indices, poly_item->color_vector());
|
||||
|
||||
std::cerr << "ok (" << time.elapsed() << " ms, "
|
||||
<< pmesh.size_of_halfedges() / 2 << " edges)" << std::endl;
|
||||
|
||||
poly_item->invalidateOpenGLBuffers();
|
||||
scene->itemChanged(index);
|
||||
QApplication::restoreOverrideCursor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Polyhedron_demo_mesh_plane_detection_plugin::check_and_set_ids(Polyhedron* polyhedron)
|
||||
{
|
||||
std::size_t facet_id = 0;
|
||||
for(Polyhedron::Facet_iterator facet_it = polyhedron->facets_begin();
|
||||
facet_it != polyhedron->facets_end(); ++facet_it, ++facet_id)
|
||||
facet_it->id() = facet_id;
|
||||
}
|
||||
|
||||
template<class SegmentPropertyMap>
|
||||
void Polyhedron_demo_mesh_plane_detection_plugin::colorize_segmentation(
|
||||
Scene_polyhedron_item* item,
|
||||
SegmentPropertyMap& segment_ids,
|
||||
std::vector<QColor>& color_vector)
|
||||
{
|
||||
item->setItemIsMulticolor(true);
|
||||
Polyhedron* polyhedron = item->polyhedron();
|
||||
color_vector.clear();
|
||||
std::size_t max_segment = 0;
|
||||
for(Polyhedron::Facet_iterator facet_it = polyhedron->facets_begin();
|
||||
facet_it != polyhedron->facets_end(); ++facet_it)
|
||||
{
|
||||
std::size_t segment_id = segment_ids[facet_it->id()];
|
||||
facet_it->set_patch_id(static_cast<int>(segment_id));
|
||||
max_segment = (std::max)(max_segment, segment_id);
|
||||
}
|
||||
color_vector.push_back(QColor(0, 0, 0));
|
||||
for(std::size_t i = 1; i <= max_segment; ++i)
|
||||
color_vector.push_back (QColor (CGAL::get_default_random().get_int (41, 255),
|
||||
CGAL::get_default_random().get_int (35, 238),
|
||||
CGAL::get_default_random().get_int (35, 255)));
|
||||
}
|
||||
|
||||
#include "Mesh_plane_detection_plugin.moc"
|
||||
Loading…
Reference in New Issue