Merge branch 'gsoc13-CurveSkeleton-new_package-xgao'

Conflicts:
	Installation/changes.html
This commit is contained in:
Sébastien Loriot 2015-07-09 17:30:24 +02:00
commit e7563d3e05
56 changed files with 72047 additions and 5 deletions

View File

@ -977,7 +977,7 @@ collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor v0v1,
lP_Erased = true ;
}
CGAL_assertion(is_valid(g));
CGAL_expensive_assertion(is_valid(g));
return lP_Erased ? q : p ;
}

View File

@ -104,7 +104,7 @@ option(BUILD_DOC "Build the doxygen documentation" OFF)
if (BUILD_DOC)
find_package(Doxygen)
find_package(PythonInterp 2.6.8)
find_package(PythonInterp 2.6.7)
if(DOXYGEN_FOUND)
# set up the directories and variables

View File

@ -151827,6 +151827,17 @@ pages = {1539--1556},
year = {2008},
}
@article{tagliasacchi2012mean,
title={Mean curvature skeletons},
author={Tagliasacchi, Andrea and Alhashim, Ibraheem and Olson, Matt and Zhang, Hao},
journal={Computer Graphics Forum (Proceedings of the Symposium on Geometry Processing)},
volume={31},
number={5},
pages={1735--1744},
year={2012},
publisher={The Eurographics Association and Blackwell}
}
@article{Chen2009SegmentationBenchmark,
author = "Xiaobai Chen and Aleksey Golovinskiy and Thomas Funkhouser",
title = "A Benchmark for {3D} Mesh Segmentation",

View File

@ -111,6 +111,7 @@ IMAGE_PATH = ${CMAKE_SOURCE_DIR}/Documentation/doc/Documentation/fig \
${CMAKE_SOURCE_DIR}/Straight_skeleton_2/doc/Straight_skeleton_2/fig \
${CMAKE_SOURCE_DIR}/Voronoi_diagram_2/doc/Voronoi_diagram_2/fig \
${CMAKE_SOURCE_DIR}/Surface_mesh_simplification/doc/Surface_mesh_simplification/fig \
${CMAKE_SOURCE_DIR}/Surface_mesh_skeletonization/doc/Surface_mesh_skeletonization/fig \
${CMAKE_SOURCE_DIR}/Surface_mesh_segmentation/doc/Surface_mesh_segmentation/fig \
${CMAKE_SOURCE_DIR}/Subdivision_method_3/doc/Subdivision_method_3/fig \
${CMAKE_SOURCE_DIR}/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/fig \

View File

@ -47,6 +47,7 @@ Algebraic_kernel_d
Kernel_d
Point_set_2
SearchStructures
Surface_mesh_skeletonization
Polytope_distance_d
Polyhedron
Polynomial

View File

@ -105,11 +105,13 @@ h1 {
\package_listing{Surface_modeling}
\package_listing{Surface_mesh_parameterization}
\package_listing{Surface_mesh_shortest_path}
\package_listing{Surface_mesh_skeletonization}
\package_listing{Ridges_3}
\package_listing{Jet_fitting_3}
\package_listing{Point_set_processing_3}
\package_listing{Stream_lines_2}
\section PartSearchStructures Spatial Searching and Sorting
\package_listing{Point_set_2}

View File

@ -153,6 +153,17 @@ and <code>src/</code> directories).
<!-- gsoc13-CurveSkeleton-new_package-xgao begin -->
<h3>Triangulated Surface Mesh Skeletonization (new package)</h3>
<ul>
<li>
This package provides a (1D) curve skeleton extraction algorithm for
a triangulated polygonal mesh without borders based on the mean curvature flow.
The particularity of this skeleton is that it captures the topology of the input.
For each skeleton vertex one can obtain its location and its corresponding vertices from the input mesh.
The code is generic and works with any model of the `FaceListGraph` concept.
</li>
</ul>
<!-- gsoc13-CurveSkeleton-new_package-xgao end -->
<!-- Point_set_shape_detection_3-cjamin begin -->

View File

@ -96,6 +96,7 @@ if(CGAL_Qt4_FOUND AND QT4_FOUND AND OPENGL_FOUND AND QGLVIEWER_FOUND)
qt4_wrap_ui( FileLoaderDialogUI_files FileLoaderDialog.ui )
qt4_wrap_ui( Show_point_dialogUI_FILES Show_point_dialog.ui )
qt4_wrap_ui( remeshingUI_FILES Remeshing_dialog.ui)
qt4_wrap_ui( Mean_curvature_flow_skeleton_pluginUI_FILES Mean_curvature_flow_skeleton_plugin.ui)
qt4_wrap_ui( meshingUI_FILES Meshing_dialog.ui )
qt4_wrap_ui( cameraUI_FILES Camera_positions_list.ui )
qt4_wrap_ui( PreferencesUI_FILES Preferences.ui )
@ -430,6 +431,13 @@ if(CGAL_Qt4_FOUND AND QT4_FOUND AND OPENGL_FOUND AND QGLVIEWER_FOUND)
polyhedron_demo_plugin(mesh_segmentation_plugin Polyhedron_demo_mesh_segmentation_plugin ${segmentationUI_FILES})
target_link_libraries(mesh_segmentation_plugin scene_polyhedron_item)
polyhedron_demo_plugin(mean_curvature_flow_skeleton_plugin Polyhedron_demo_mean_curvature_flow_skeleton_plugin ${Mean_curvature_flow_skeleton_pluginUI_FILES})
target_link_libraries(mean_curvature_flow_skeleton_plugin
scene_polyhedron_item
scene_points_with_normal_item
scene_polylines_item
demo_framework)
polyhedron_demo_plugin(nef_plugin Polyhedron_demo_nef_plugin)
target_link_libraries(nef_plugin scene_nef_polyhedron_item)

View File

@ -0,0 +1,188 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Mean_curvature_flow_skeleton_plugin</class>
<widget class="QDockWidget" name="Mean_curvature_flow_skeleton_plugin">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>744</width>
<height>468</height>
</rect>
</property>
<property name="windowTitle">
<string>DockWidget</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<widget class="QWidget" name="">
<property name="geometry">
<rect>
<x>30</x>
<y>20</y>
<width>468</width>
<height>235</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QCheckBox" name="is_medially_centered">
<property name="text">
<string>Is medially centered</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_omega_H">
<property name="text">
<string>quality_speed_tradeoff</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="omega_H"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_omega_P">
<property name="text">
<string>medially_centered_speed_tradeoff</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="omega_P"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_min_edge_length">
<property name="text">
<string>min_edge_length</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="min_edge_length"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_delta_area">
<property name="text">
<string>area_variation_factor </string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="delta_area"/>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="pushButton_run">
<property name="text">
<string>Run one iteration</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_converge">
<property name="text">
<string>Run to convergence</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_skeletonize">
<property name="text">
<string>Skeletonize</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_segment">
<property name="text">
<string>Segment</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="pushButton_contract">
<property name="text">
<string>Contract</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_collapse">
<property name="text">
<string>Collapse</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_split">
<property name="text">
<string>Split</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_degeneracy">
<property name="text">
<string>Degeneracy</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<zorder>omega_H</zorder>
<zorder>min_edge_length</zorder>
<zorder>label_omega_H</zorder>
<zorder>label_min_edge_length</zorder>
<zorder>pushButton_contract</zorder>
<zorder>pushButton_collapse</zorder>
<zorder>pushButton_split</zorder>
<zorder>pushButton_degeneracy</zorder>
<zorder>pushButton_run</zorder>
<zorder>pushButton_skeletonize</zorder>
<zorder>pushButton_converge</zorder>
<zorder>delta_area</zorder>
<zorder>label_delta_area</zorder>
<zorder>label_omega_P</zorder>
<zorder>omega_P</zorder>
<zorder>is_medially_centered</zorder>
<zorder>pushButton_segment</zorder>
</widget>
</widget>
<resources/>
<connections/>
<slots>
<slot>contract()</slot>
</slots>
</ui>

View File

@ -0,0 +1,906 @@
#ifdef CGAL_GLEW_ENABLED
#include <GL/glew.h> // tmp hack to make sure gl.his included before glew.h
#endif
#include "Polyhedron_demo_plugin_helper.h"
#include "Polyhedron_demo_plugin_interface.h"
#include "ui_Mean_curvature_flow_skeleton_plugin.h"
#include "Scene_polyhedron_item.h"
#include "Scene_points_with_normal_item.h"
#include "Scene_polylines_item.h"
#include "Scene.h"
#include "Polyhedron_type.h"
#include "MainWindow.h"
#include "ui_MainWindow.h"
#include <QApplication>
#include <QMainWindow>
#include <QInputDialog>
#include <QTime>
#include <QMessageBox>
#include <Eigen/Sparse>
#include <boost/property_map/property_map.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Eigen_solver_traits.h>
#include <CGAL/extract_mean_curvature_flow_skeleton.h>
#include <CGAL/boost/graph/graph_traits_Polyhedron_3.h>
#include <CGAL/iterator.h>
#include <CGAL/internal/corefinement/Polyhedron_subset_extraction.h>
#include <CGAL/boost/graph/split_graph_into_polylines.h>
#include <CGAL/mesh_segmentation.h>
#include <CGAL/Polyhedron_copy_3.h>
#include <queue>
typedef boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
typedef boost::graph_traits<Polyhedron>::vertex_iterator vertex_iterator;
typedef boost::graph_traits<Polyhedron>::halfedge_descriptor halfedge_descriptor;
typedef Polyhedron::Facet_iterator Facet_iterator;
typedef CGAL::Mean_curvature_flow_skeletonization<Polyhedron> Mean_curvature_skeleton;
typedef Mean_curvature_skeleton::Skeleton Skeleton;
typedef Polyhedron::Traits Kernel;
typedef Kernel::Point_3 Point;
struct Polyline_visitor
{
typedef std::vector<Point> Polyline;
typedef std::vector<std::size_t> Polyline_of_ids;
std::list<Polyline>& polylines;
Skeleton& skeleton;
Polyline_visitor(std::list<Polyline>& lines, Skeleton& skeleton)
: polylines(lines),
skeleton(skeleton)
{}
void start_new_polyline()
{
Polyline V;
polylines.push_back(V);
}
void add_node(boost::graph_traits<Skeleton>::vertex_descriptor vd)
{
Polyline& polyline = polylines.back();
polyline.push_back(skeleton[vd].point);
}
};
template<class ValueType>
struct Facet_with_id_pmap
: public boost::put_get_helper<ValueType&,
Facet_with_id_pmap<ValueType> >
{
typedef Polyhedron::Face_handle key_type;
typedef ValueType value_type;
typedef value_type& reference;
typedef boost::lvalue_property_map_tag category;
Facet_with_id_pmap(
std::vector<ValueType>& internal_vector
) : internal_vector(internal_vector) { }
reference operator[](key_type key) const
{ return internal_vector[key->id()]; }
private:
std::vector<ValueType>& internal_vector;
};
class Polyhedron_demo_mean_curvature_flow_skeleton_plugin :
public QObject,
public Polyhedron_demo_plugin_helper
{
Q_OBJECT
Q_INTERFACES(Polyhedron_demo_plugin_interface)
QAction* actionMCFSkeleton;
QAction* actionConvert_to_medial_skeleton;
public:
// used by Polyhedron_demo_plugin_helper
QStringList actionsNames() const {
return QStringList() << "actionMCFSkeleton" << "actionConvert_to_medial_skeleton";
}
void init(QMainWindow* mainWindow, Scene_interface* scene_interface) {
mcs = NULL;
dockWidget = NULL;
ui = NULL;
actionMCFSkeleton = new QAction(tr("Mean Curvature Skeleton"), mainWindow);
actionMCFSkeleton->setObjectName("actionMCFSkeleton");
actionConvert_to_medial_skeleton = new QAction(tr("Extract Medial Skeleton"), mainWindow);
actionConvert_to_medial_skeleton->setObjectName("actionConvert_to_medial_skeleton");
Polyhedron_demo_plugin_helper::init(mainWindow, scene_interface);
}
QList<QAction*> actions() const {
return QList<QAction*>() << actionMCFSkeleton << actionConvert_to_medial_skeleton;
}
bool applicable(QAction*) const {
return qobject_cast<Scene_polyhedron_item*>(scene->item(scene->mainSelectionIndex()));
}
void init_ui(double diag) {
ui->omega_H->setValue(0.1);
ui->omega_H->setSingleStep(0.1);
ui->omega_H->setDecimals(3);
ui->omega_P->setValue(0.2);
ui->omega_P->setSingleStep(0.1);
ui->omega_P->setDecimals(3);
ui->min_edge_length->setDecimals(7);
ui->min_edge_length->setValue(0.002 * diag);
ui->min_edge_length->setSingleStep(0.0000001);
ui->delta_area->setDecimals(7);
ui->delta_area->setValue(1e-4);
ui->delta_area->setSingleStep(1e-5);
ui->is_medially_centered->setChecked(false);
ui->label_omega_H->setToolTip(QString("omega_H controls the velocity of movement and approximation quality"));
ui->label_omega_P->setToolTip(QString("omega_P controls the smoothness of the medial approximation"));
ui->pushButton_contract->setToolTip(QString("contract mesh based on mean curvature flow"));
ui->pushButton_collapse->setToolTip(QString("collapse short edges"));
ui->pushButton_split->setToolTip(QString("split obtuse triangles"));
ui->pushButton_degeneracy->setToolTip(QString("fix degenerate points"));
ui->pushButton_skeletonize->setToolTip(QString("Turn mesh to a skeleton curve"));
ui->pushButton_run->setToolTip(QString("run one iteration of contract, collapse, split, detect degeneracy"));
ui->pushButton_converge->setToolTip(QString("iteratively contract the mesh until convergence"));
}
bool check_item_index(int index) {
if (index < 0)
{
QMessageBox msgBox;
msgBox.setText("Please select an item first");
msgBox.exec();
return false;
}
return true;
}
/// \todo move this function into an include
bool is_mesh_valid(Polyhedron *pMesh) {
if (!pMesh->is_closed())
{
QMessageBox msgBox;
msgBox.setText("The mesh is not closed.");
msgBox.exec();
return false;
}
if (!pMesh->is_pure_triangle())
{
QMessageBox msgBox;
msgBox.setText("The mesh is not a pure triangle mesh.");
msgBox.exec();
return false;
}
// the algorithm is only applicable on a mesh
// that has only one connected component
std::size_t num_component;
CGAL::Counting_output_iterator output_it(&num_component);
CGAL::internal::extract_connected_components(*pMesh, output_it);
++output_it;
if (num_component != 1)
{
QMessageBox msgBox;
QString str = QString("The mesh is not a single closed mesh.\n It has %1 components.").arg(num_component);
msgBox.setText(str);
msgBox.exec();
return false;
}
return true;
}
/// \todo remove duplicated code
// check if the Mean_curvature_skeleton exists
// or has the same polyheron item
// check if the mesh is a watertight triangle mesh
bool check_mesh(Scene_polyhedron_item* item) {
double omega_H = ui->omega_H->value();
double omega_P = ui->omega_P->value();
double min_edge_length = ui->min_edge_length->value();
double delta_area = ui->delta_area->value();
bool is_medially_centered = ui->is_medially_centered->isChecked();
Polyhedron *pMesh = item->polyhedron();
if (mcs == NULL)
{
if (!is_mesh_valid(pMesh))
{
return false;
}
mcs = new Mean_curvature_skeleton(*pMesh);
meso_skeleton = new Polyhedron(*pMesh);
input_triangle_mesh = pMesh;
//set algorithm parameters
mcs->set_quality_speed_tradeoff(omega_H);
mcs->set_medially_centered_speed_tradeoff(omega_P);
mcs->set_min_edge_length(min_edge_length);
mcs->set_is_medially_centered(is_medially_centered);
mcs->set_area_variation_factor(delta_area);
Scene_polyhedron_item* contracted_item = new Scene_polyhedron_item( meso_skeleton );
contracted_item->setName(QString("contracted mesh of %1").arg(item->name()));
InputMeshItemIndex = scene->mainSelectionIndex();
contractedItemIndex = scene->addItem(contracted_item);
item->setVisible(false);
fixedPointsItemIndex = -1;
nonFixedPointsItemIndex = -1;
poleLinesItemIndex = -1;
}
else
{
if (input_triangle_mesh != pMesh)
{
if (!is_mesh_valid(pMesh))
{
return false;
}
delete mcs;
mcs = new Mean_curvature_skeleton(*pMesh);
meso_skeleton = new Polyhedron(*pMesh);
input_triangle_mesh = pMesh;
//set algorithm parameters
mcs->set_quality_speed_tradeoff(omega_H);
mcs->set_medially_centered_speed_tradeoff(omega_P);
mcs->set_min_edge_length(min_edge_length);
mcs->set_is_medially_centered(is_medially_centered);
mcs->set_area_variation_factor(delta_area);
Scene_polyhedron_item* contracted_item = new Scene_polyhedron_item(meso_skeleton);
contracted_item->setName(QString("contracted mesh of %1").arg(item->name()));
InputMeshItemIndex = scene->mainSelectionIndex();
contractedItemIndex = scene->addItem(contracted_item);
item->setVisible(false);
fixedPointsItemIndex = -1;
nonFixedPointsItemIndex = -1;
poleLinesItemIndex = -1;
}
else
{
mcs->set_quality_speed_tradeoff(omega_H);
mcs->set_medially_centered_speed_tradeoff(omega_P);
mcs->set_min_edge_length(min_edge_length);
mcs->set_area_variation_factor(delta_area);
mcs->set_is_medially_centered(is_medially_centered);
}
}
return true;
}
void update_meso_skeleton()
{
CGAL::Polyhedron_copy_3<Mean_curvature_skeleton::Meso_skeleton, Polyhedron::HalfedgeDS> modifier(mcs->meso_skeleton());
meso_skeleton->delegate(modifier);
scene->itemChanged(contractedItemIndex);
}
void update_parameters(Mean_curvature_skeleton* mcs)
{
double omega_H = ui->omega_H->value();
double omega_P = ui->omega_P->value();
double min_edge_length = ui->min_edge_length->value();
double delta_area = ui->delta_area->value();
bool is_medially_centered = ui->is_medially_centered->isChecked();
mcs->set_quality_speed_tradeoff(omega_H);
mcs->set_medially_centered_speed_tradeoff(omega_P);
mcs->set_min_edge_length(min_edge_length);
mcs->set_area_variation_factor(delta_area);
mcs->set_is_medially_centered(is_medially_centered);
}
public Q_SLOTS:
void on_actionMCFSkeleton_triggered();
void on_actionConvert_to_medial_skeleton_triggered();
void on_actionContract();
void on_actionCollapse();
void on_actionSplit();
void on_actionDegeneracy();
void on_actionRun();
void on_actionSkeletonize();
void on_actionConverge();
void on_actionUpdateBBox();
void on_actionSegment();
void on_actionItemAboutToBeDestroyed(Scene_item*);
private:
Mean_curvature_skeleton* mcs;
Polyhedron* meso_skeleton; // a copy of the meso_skeleton that is displayed
Polyhedron* input_triangle_mesh;
QDockWidget* dockWidget;
Ui::Mean_curvature_flow_skeleton_plugin* ui;
int fixedPointsItemIndex;
int nonFixedPointsItemIndex;
int poleLinesItemIndex;
int contractedItemIndex;
int InputMeshItemIndex;
Skeleton skeleton_curve;
}; // end Polyhedron_demo_mean_curvature_flow_skeleton_plugin
void Polyhedron_demo_mean_curvature_flow_skeleton_plugin::on_actionMCFSkeleton_triggered()
{
const Scene_interface::Item_id index = scene->mainSelectionIndex();
Scene_polyhedron_item* item =
qobject_cast<Scene_polyhedron_item*>(scene->item(index));
if(item)
{
Polyhedron* pMesh = item->polyhedron();
if(!pMesh) return;
dockWidget = new QDockWidget(mw);
ui = new Ui::Mean_curvature_flow_skeleton_plugin();
ui->setupUi(dockWidget);
dockWidget->setFeatures(QDockWidget::DockWidgetMovable
| QDockWidget::DockWidgetFloatable
| QDockWidget::DockWidgetClosable);
dockWidget->setWindowTitle("Mean Curvature Flow Skeleton");
mw->addDockWidget(Qt::LeftDockWidgetArea, dockWidget);
dockWidget->show();
dockWidget->raise();
connect(ui->pushButton_contract, SIGNAL(clicked()),
this, SLOT(on_actionContract()));
connect(ui->pushButton_collapse, SIGNAL(clicked()),
this, SLOT(on_actionCollapse()));
connect(ui->pushButton_split, SIGNAL(clicked()),
this, SLOT(on_actionSplit()));
connect(ui->pushButton_degeneracy, SIGNAL(clicked()),
this, SLOT(on_actionDegeneracy()));
connect(ui->pushButton_run, SIGNAL(clicked()),
this, SLOT(on_actionRun()));
connect(ui->pushButton_skeletonize, SIGNAL(clicked()),
this, SLOT(on_actionSkeletonize()));
connect(ui->pushButton_converge, SIGNAL(clicked()),
this, SLOT(on_actionConverge()));
connect(dynamic_cast<Scene*>(scene), SIGNAL(updated_bbox()),
this, SLOT(on_actionUpdateBBox()));
connect(ui->pushButton_segment, SIGNAL(clicked()),
this, SLOT(on_actionSegment()));
double diag = scene->len_diagonal();
init_ui(diag);
fixedPointsItemIndex = -1;
nonFixedPointsItemIndex = -1;
poleLinesItemIndex = -1;
contractedItemIndex = -1;
InputMeshItemIndex = -1;
}
}
void Polyhedron_demo_mean_curvature_flow_skeleton_plugin::on_actionUpdateBBox()
{
double diag = scene->len_diagonal();
ui->min_edge_length->setValue(0.002 * diag);
}
void Polyhedron_demo_mean_curvature_flow_skeleton_plugin::on_actionSegment()
{
if (num_vertices(skeleton_curve)==0 ) on_actionSkeletonize();
if (num_vertices(skeleton_curve)==0 ) return;
QTime time;
time.start();
QApplication::setOverrideCursor(Qt::WaitCursor);
// init the polyhedron simplex indices
CGAL::set_halfedgeds_items_id(*input_triangle_mesh);
//for each input vertex compute its distance to the skeleton
std::vector<double> distances(num_vertices(*input_triangle_mesh));
BOOST_FOREACH(boost::graph_traits<Skeleton>::vertex_descriptor v, vertices(skeleton_curve) )
{
const Point& skel_pt = skeleton_curve[v].point;
BOOST_FOREACH(vertex_descriptor mesh_v, skeleton_curve[v].vertices)
{
const Point& mesh_pt = mesh_v->point();
distances[mesh_v->id()] = std::sqrt(CGAL::squared_distance(skel_pt, mesh_pt));
}
}
// create a property-map for sdf values
std::vector<double> sdf_values( num_faces(*input_triangle_mesh) );
Facet_with_id_pmap<double> sdf_property_map(sdf_values);
// compute sdf values with skeleton
BOOST_FOREACH(Polyhedron::Face_handle f, faces(*input_triangle_mesh))
{
double dist = 0;
BOOST_FOREACH(Polyhedron::Halfedge_handle hd, halfedges_around_face(halfedge(f, *input_triangle_mesh), *input_triangle_mesh))
dist+=distances[target(hd, *input_triangle_mesh)->id()];
sdf_property_map[f] = dist / 3.;
}
// post-process the sdf values
CGAL::sdf_values_postprocessing(*input_triangle_mesh, sdf_property_map);
// create a property-map for segment-ids (it is an adaptor for this case)
std::vector<std::size_t> segment_ids( num_faces(*input_triangle_mesh) );
Facet_with_id_pmap<std::size_t> segment_property_map(segment_ids);
// segment the mesh using default parameters
std::cout << "Number of segments: "
<< CGAL::segmentation_from_sdf_values(*input_triangle_mesh, sdf_property_map, segment_property_map) <<"\n";
Polyhedron* segmented_polyhedron = new Polyhedron(*input_triangle_mesh);
int i=0;
BOOST_FOREACH(Polyhedron::Face_handle fd, faces(*segmented_polyhedron))
{
fd->set_patch_id( static_cast<int>(segment_ids[i++] ));
}
scene->item(InputMeshItemIndex)->setVisible(false);
Scene_polyhedron_item* item_segmentation = new Scene_polyhedron_item(segmented_polyhedron);
scene->addItem(item_segmentation);
item_segmentation->setName(QString("segmentation"));
QApplication::restoreOverrideCursor();
}
void Polyhedron_demo_mean_curvature_flow_skeleton_plugin::on_actionConvert_to_medial_skeleton_triggered()
{
const Scene_interface::Item_id index = scene->mainSelectionIndex();
Scene_polyhedron_item* item =
qobject_cast<Scene_polyhedron_item*>(scene->item(index));
if(item)
{
Polyhedron* pMesh = item->polyhedron();
if ( !is_mesh_valid(pMesh) ) return;
QTime time;
time.start();
QApplication::setOverrideCursor(Qt::WaitCursor);
Skeleton skeleton;
CGAL::extract_mean_curvature_flow_skeleton(*pMesh, skeleton);
std::cout << "ok (" << time.elapsed() << " ms, " << ")" << std::endl;
//create the polylines representing the skeleton
Scene_polylines_item* skeleton_item = new Scene_polylines_item();
skeleton_item->setColor(QColor(175, 0, 255));
Polyline_visitor polyline_visitor(skeleton_item->polylines, skeleton);
CGAL::split_graph_into_polylines( skeleton,
polyline_visitor,
CGAL::IsTerminalDefault() );
skeleton_item->setName(QString("Medial skeleton curve of %1").arg(item->name()));
scene->addItem(skeleton_item);
item->setPointsMode();
QApplication::restoreOverrideCursor();
}
}
void Polyhedron_demo_mean_curvature_flow_skeleton_plugin::on_actionContract()
{
const Scene_interface::Item_id index = scene->mainSelectionIndex();
if (!check_item_index(index))
{
return;
}
Scene_polyhedron_item* item =
qobject_cast<Scene_polyhedron_item*>(scene->item(index));
if (!check_mesh(item))
{
return;
}
QTime time;
time.start();
std::cout << "Contract...\n";
QApplication::setOverrideCursor(Qt::WaitCursor);
update_parameters(mcs);
mcs->contract_geometry();
std::cout << "ok (" << time.elapsed() << " ms, " << ")" << std::endl;
update_meso_skeleton();
QApplication::restoreOverrideCursor();
}
void Polyhedron_demo_mean_curvature_flow_skeleton_plugin::on_actionCollapse()
{
const Scene_interface::Item_id index = scene->mainSelectionIndex();
if (!check_item_index(index))
{
return;
}
Scene_polyhedron_item* item =
qobject_cast<Scene_polyhedron_item*>(scene->item(index));
if (!check_mesh(item))
{
return;
}
QTime time;
time.start();
std::cout << "Collapse...\n";
QApplication::setOverrideCursor(Qt::WaitCursor);
update_parameters(mcs);
std::size_t num_collapses = mcs->collapse_edges();
std::cout << "collapsed " << num_collapses << " edges.\n";
std::cout << "ok (" << time.elapsed() << " ms, " << ")" << std::endl;
update_meso_skeleton();
QApplication::restoreOverrideCursor();
}
void Polyhedron_demo_mean_curvature_flow_skeleton_plugin::on_actionSplit()
{
const Scene_interface::Item_id index = scene->mainSelectionIndex();
if (!check_item_index(index))
{
return;
}
Scene_polyhedron_item* item =
qobject_cast<Scene_polyhedron_item*>(scene->item(index));
if (!check_mesh(item))
{
return;
}
QTime time;
time.start();
std::cout << "Split...\n";
QApplication::setOverrideCursor(Qt::WaitCursor);
update_parameters(mcs);
std::size_t num_split = mcs->split_faces();
std::cout << "split " << num_split << " triangles.\n";
std::cout << "ok (" << time.elapsed() << " ms, " << ")" << std::endl;
update_meso_skeleton();
QApplication::restoreOverrideCursor();
}
void Polyhedron_demo_mean_curvature_flow_skeleton_plugin::on_actionDegeneracy()
{
const Scene_interface::Item_id index = scene->mainSelectionIndex();
if (!check_item_index(index))
{
return;
}
Scene_polyhedron_item* item =
qobject_cast<Scene_polyhedron_item*>(scene->item(index));
if (!check_mesh(item))
{
return;
}
QTime time;
time.start();
std::cout << "Detect degeneracy...\n";
QApplication::setOverrideCursor(Qt::WaitCursor);
update_parameters(mcs);
mcs->detect_degeneracies();
std::cout << "ok (" << time.elapsed() << " ms, " << ")" << std::endl;
Scene_points_with_normal_item* fixedPointsItem = new Scene_points_with_normal_item;
fixedPointsItem->setName(QString("fixed points of %1").arg(item->name()));
std::vector<Point> fixedPoints;
mcs->fixed_points(fixedPoints);
Point_set *ps = fixedPointsItem->point_set();
for (size_t i = 0; i < fixedPoints.size(); ++i)
{
UI_point_3<Kernel> point(fixedPoints[i].x(), fixedPoints[i].y(), fixedPoints[i].z());
ps->select(&point);
ps->push_back(point);
}
if (fixedPointsItemIndex == -1)
{
fixedPointsItemIndex = scene->addItem(fixedPointsItem);
}
else
{
Scene_item* temp = scene->replaceItem(fixedPointsItemIndex, fixedPointsItem, false);
delete temp;
}
// update scene
update_meso_skeleton();
scene->itemChanged(fixedPointsItemIndex);
scene->setSelectedItem(index);
QApplication::restoreOverrideCursor();
}
void Polyhedron_demo_mean_curvature_flow_skeleton_plugin::on_actionRun()
{
const Scene_interface::Item_id index = scene->mainSelectionIndex();
if (!check_item_index(index))
{
return;
}
Scene_polyhedron_item* item =
qobject_cast<Scene_polyhedron_item*>(scene->item(index));
if (!check_mesh(item))
{
return;
}
QTime time;
time.start();
QApplication::setOverrideCursor(Qt::WaitCursor);
std::cout << "Run one iteration...\n";
update_parameters(mcs);
mcs->contract();
std::cout << "ok (" << time.elapsed() << " ms, " << ")" << std::endl;
Scene_interface::Item_id contracted_item_index = scene->mainSelectionIndex();
Scene_polyhedron_item* contracted_item =
qobject_cast<Scene_polyhedron_item*>(scene->item(contracted_item_index));
// update scene
Scene_points_with_normal_item* fixedPointsItem = new Scene_points_with_normal_item;
fixedPointsItem->setName(QString("fixed points of %1").arg(contracted_item->name()));
std::vector<Point> fixedPoints;
mcs->fixed_points(fixedPoints);
Point_set *ps = fixedPointsItem->point_set();
for (size_t i = 0; i < fixedPoints.size(); ++i)
{
UI_point_3<Kernel> point(fixedPoints[i].x(), fixedPoints[i].y(), fixedPoints[i].z());
ps->select(&point);
ps->push_back(point);
}
if (fixedPointsItemIndex == -1)
{
fixedPointsItemIndex = scene->addItem(fixedPointsItem);
}
else
{
Scene_item* temp = scene->replaceItem(fixedPointsItemIndex, fixedPointsItem, false);
delete temp;
}
QObject* scene_object = dynamic_cast<QObject*>(scene);
connect(scene_object, SIGNAL(itemAboutToBeDestroyed(Scene_item*)),
this, SLOT(on_actionItemAboutToBeDestroyed(Scene_item*)));
//#define DRAW_NON_FIXED_POINTS
#ifdef DRAW_NON_FIXED_POINTS
// draw non-fixed points
Scene_points_with_normal_item* nonFixedPointsItem = new Scene_points_with_normal_item;
nonFixedPointsItem->setName("non-fixed points");
nonFixedPointsItem->setColor(QColor(0, 255, 0));
std::vector<Point> nonFixedPoints;
mcs->non_fixed_points(nonFixedPoints);
ps = nonFixedPointsItem->point_set();
for (size_t i = 0; i < nonFixedPoints.size(); ++i)
{
UI_point_3<Kernel> point(nonFixedPoints[i].x(), nonFixedPoints[i].y(), nonFixedPoints[i].z());
ps->push_back(point);
}
if (nonFixedPointsItemIndex == -1)
{
nonFixedPointsItemIndex = scene->addItem(nonFixedPointsItem);
}
else
{
scene->replaceItem(nonFixedPointsItemIndex, nonFixedPointsItem, false);
}
scene->itemChanged(nonFixedPointsItemIndex);
#endif
//#define DRAW_POLE_LINE
#ifdef DRAW_POLE_LINE
// draw lines connecting surface points and their correspondent poles
Scene_polylines_item* poleLinesItem = new Scene_polylines_item();
Polyhedron* pMesh = item->polyhedron();
std::vector<Point> pole_points;
mcs->poles(pole_points);
vertex_iterator vb, ve;
int id = 0;
for (boost::tie(vb, ve) = vertices(*pMesh); vb != ve; ++vb)
{
std::vector<Point> line;
line.clear();
vertex_descriptor v = *vb;
Point s = v->point();
Point t = pole_points[id++];
line.push_back(s);
line.push_back(t);
poleLinesItem->polylines.push_back(line);
}
if (poleLinesItemIndex == -1)
{
poleLinesItemIndex = scene->addItem(poleLinesItem);
}
else
{
scene->replaceItem(poleLinesItemIndex, poleLinesItem, false);
}
#endif
update_meso_skeleton();
scene->setSelectedItem(index);
QApplication::restoreOverrideCursor();
}
void Polyhedron_demo_mean_curvature_flow_skeleton_plugin::on_actionSkeletonize()
{
const Scene_interface::Item_id index = scene->mainSelectionIndex();
if (!check_item_index(index))
{
return;
}
Scene_polyhedron_item* item =
qobject_cast<Scene_polyhedron_item*>(scene->item(index));
if (!check_mesh(item))
{
return;
}
QTime time;
time.start();
QApplication::setOverrideCursor(Qt::WaitCursor);
update_parameters(mcs);
mcs->convert_to_skeleton(skeleton_curve);
std::cout << "ok (" << time.elapsed() << " ms, " << ")" << std::endl;
//create the polylines representing the skeleton
Scene_polylines_item* skeleton = new Scene_polylines_item();
skeleton->setColor(QColor(175, 0, 255));
Polyline_visitor polyline_visitor(skeleton->polylines, skeleton_curve);
CGAL::split_graph_into_polylines( skeleton_curve,
polyline_visitor,
CGAL::IsTerminalDefault() );
skeleton->setName(QString("skeleton curve of %1").arg(item->name()));
scene->addItem(skeleton);
// set the fixed points and contracted mesh as invisible
if (fixedPointsItemIndex >= 0)
{
scene->item(fixedPointsItemIndex)->setVisible(false);
}
scene->item(contractedItemIndex)->setVisible(false);
// display the original mesh in transparent mode
item->setVisible(false);
if (InputMeshItemIndex >= 0)
{
scene->item(InputMeshItemIndex)->setVisible(true);
scene->item(InputMeshItemIndex)->setPointsMode();
}
// update scene
QApplication::restoreOverrideCursor();
}
void Polyhedron_demo_mean_curvature_flow_skeleton_plugin::on_actionConverge()
{
const Scene_interface::Item_id index = scene->mainSelectionIndex();
if (!check_item_index(index))
{
return;
}
Scene_polyhedron_item* item =
qobject_cast<Scene_polyhedron_item*>(scene->item(index));
if (!check_mesh(item))
{
return;
}
QTime time;
time.start();
QApplication::setOverrideCursor(Qt::WaitCursor);
mcs->contract_until_convergence();
std::cout << "ok (" << time.elapsed() << " ms, " << ")" << std::endl;
// update scene
Scene_points_with_normal_item* fixedPointsItem = new Scene_points_with_normal_item;
fixedPointsItem->setName(QString("fixed points of %1").arg(item->name()));
std::vector<Point> fixedPoints;
mcs->fixed_points(fixedPoints);
Point_set *ps = fixedPointsItem->point_set();
for (size_t i = 0; i < fixedPoints.size(); ++i)
{
UI_point_3<Kernel> point(fixedPoints[i].x(), fixedPoints[i].y(), fixedPoints[i].z());
ps->select(&point);
ps->push_back(point);
}
if (fixedPointsItemIndex == -1)
{
fixedPointsItemIndex = scene->addItem(fixedPointsItem);
}
else
{
Scene_item* temp = scene->replaceItem(fixedPointsItemIndex, fixedPointsItem, false);
delete temp;
}
scene->itemChanged(fixedPointsItemIndex);
update_meso_skeleton();
scene->setSelectedItem(index);
QApplication::restoreOverrideCursor();
}
void Polyhedron_demo_mean_curvature_flow_skeleton_plugin::on_actionItemAboutToBeDestroyed(Scene_item* /* item */)
{
if (mcs != NULL)
{
delete mcs;
mcs = NULL;
}
}
Q_EXPORT_PLUGIN2(Polyhedron_demo_mean_curvature_flow_skeleton_plugin, Polyhedron_demo_mean_curvature_flow_skeleton_plugin)
#include "Polyhedron_demo_mean_curvature_flow_skeleton_plugin.moc"

View File

@ -113,6 +113,7 @@ else
jet_fitting_plugin \
join_polyhedra_plugin \
kernel_plugin \
mean_curvature_flow_skeleton_plugin \
mesh_3_plugin \
mesh_segmentation_plugin \
mesh_simplification_plugin \

View File

@ -0,0 +1,92 @@
// Copyright (c) 2014 GeometryFactory (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
// 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.
//
// $URL$
// $Id$
//
// Author(s) : Sebastien Loriot
//
#ifndef CGAL_FACEGRAPH_POLYHEDRON_3_H
#define CGAL_FACEGRAPH_POLYHEDRON_3_H 1
#include <CGAL/basic.h>
#include <CGAL/Modifier_base.h>
#include <CGAL/Polyhedron_incremental_builder_3.h>
#include <CGAL/Cartesian_converter.h>
#include <CGAL/Kernel_traits.h>
#include <boost/graph/graph_traits.hpp>
#include <boost/foreach.hpp>
namespace CGAL {
template < class FaceGraph, class PointPMap, class HDS, bool clear_target_before = true >
class FaceGraph_to_Polyhedron_3 : public Modifier_base<HDS> {
FaceGraph& fg;
PointPMap ppmap;
public:
typedef HDS Halfedge_data_structure;
FaceGraph_to_Polyhedron_3(const FaceGraph& src, PointPMap map)
: fg(const_cast<FaceGraph&>(src))
, ppmap(map)
{}
void operator()( HDS& target);
};
template < class FaceGraph, class PointPMap, class HDS, bool clear_target_before>
void
FaceGraph_to_Polyhedron_3<FaceGraph, PointPMap, HDS, clear_target_before>::
operator()(HDS& tgt)
{
typedef typename boost::graph_traits<FaceGraph>::vertex_descriptor vertex_descriptor;
typedef typename boost::graph_traits<FaceGraph>::face_descriptor face_descriptor;
typedef typename boost::graph_traits<FaceGraph>::halfedge_descriptor halfedge_descriptor;
Cartesian_converter<
typename Kernel_traits<typename boost::property_traits<PointPMap>::value_type>::Kernel,
typename Kernel_traits<typename HDS::Vertex::Point>::Kernel
> convert;
if ( clear_target_before )
tgt.clear();
Polyhedron_incremental_builder_3<HDS> B(tgt);
B.begin_surface( num_vertices(fg),
num_faces(fg),
num_halfedges(fg));
std::map<vertex_descriptor, std::size_t> indices;
std::size_t i=0;
BOOST_FOREACH(vertex_descriptor vd, vertices(fg) )
{
B.add_vertex( convert( get(ppmap, vd) ) );
indices[vd]=i++;
}
BOOST_FOREACH(face_descriptor fd, faces(fg))
{
B.begin_facet();
halfedge_descriptor hd=halfedge(fd, fg), first=hd;
do {
B.add_vertex_to_facet( indices[target(edge(hd,fg), fg)] );
hd=next(hd,fg);;
} while( hd != first);
B.end_facet();
}
B.end_surface();
}
} //namespace CGAL
#endif // CGAL_FACEGRAPH_POLYHEDRON_3_H //
// EOF //

View File

@ -128,6 +128,31 @@ public:
X = solver().solve(B);
return solver().info() == Eigen::Success;
}
// Solving the normal equation "At*A*X = At*B".
// --
bool normal_equation_factor(const Matrix& A)
{
typename Matrix::EigenType At = A.eigen_object().transpose();
m_mat = &A.eigen_object();
solver().compute(At * A.eigen_object());
return solver().info() == Eigen::Success;
}
bool normal_equation_solver(const Vector& B, Vector& X)
{
CGAL_precondition(m_mat!=NULL); //non_symmetric_factor should have been called first
typename Vector::EigenType AtB = m_mat->transpose() * B.eigen_object();
X = solver().solve(AtB);
return solver().info() == Eigen::Success;
}
bool normal_equation_solver(const Matrix& A, const Vector& B, Vector& X)
{
if (!normal_equation_factor(A)) return false;
return normal_equation_solver(B, X);
}
// --
protected:
const typename Matrix::EigenType* m_mat;
boost::shared_ptr<EigenSolverT> m_solver_sptr;

View File

@ -6,3 +6,5 @@ Circulator
Stream_support
Polyhedron
Kernel_d
Surface_modeling
Surface_mesh_skeletonization

View File

@ -7,7 +7,7 @@ namespace CGAL {
The class `Eigen_solver_traits` provides an interface to the sparse solvers of \ref thirdpartyEigen "Eigen".
The version 3.1 (or greater) of \ref thirdpartyEigen "Eigen" must be available on the system.
\cgalModels `SparseLinearAlgebraTraitsWithFactor_d`
\cgalModels `#SparseLinearAlgebraTraitsWithFactor_d` and `#NormalEquationSparseLinearAlgebraTraits_d`
Parameters
--------------

View File

@ -0,0 +1,8 @@
Handling the case when solver fails.
More tests for correctness.
More benchmark.
Add a command line tool.
Improve the reference and user manual.
Add an option to filter small parts of the skeleton (cf knot2.off)
Resample the skeleton.
Compute the distance between two skeletons.

View File

@ -0,0 +1,74 @@
# Created by the script cgal_create_cmake_script_with_options
# This is the CMake script for compiling a set of CGAL applications.
project( Mean_curvature_skeleton )
#SET(CMAKE_BUILD_TYPE "Debug")
#SET(GCC_COVERAGE_COMPILE_FLAGS "-fprofile-arcs -ftest-coverage")
#SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS}" )
cmake_minimum_required(VERSION 2.6.2)
if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_GREATER 2.6)
if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}" VERSION_GREATER 2.8.3)
cmake_policy(VERSION 2.8.4)
else()
cmake_policy(VERSION 2.6)
endif()
endif()
set( CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS true )
if ( COMMAND cmake_policy )
cmake_policy( SET CMP0003 NEW )
endif()
# CGAL and its components
find_package( CGAL QUIET COMPONENTS )
if ( NOT CGAL_FOUND )
message(STATUS "This project requires the CGAL library, and will not be compiled.")
return()
endif()
# include helper file
include( ${CGAL_USE_FILE} )
# Boost and its components
find_package( Boost REQUIRED )
if ( NOT Boost_FOUND )
message(STATUS "This project requires the Boost library, and will not be compiled.")
return()
endif()
# include for local directory
# include for local package
include_directories( BEFORE ../../include )
find_package(Eigen3 3.2.0) #(requires 3.2.0 or greater)
if(NOT EIGEN3_FOUND)
message(STATUS "NOTICE: Eigen 3.2 (or greater) is not found.")
else()
include( ${EIGEN3_USE_FILE} )
endif(NOT EIGEN3_FOUND)
# Creating entries for all .cpp/.C files with "main" routine
# ##########################################################
include( CGAL_CreateSingleSourceCGALProgram )
create_single_source_cgal_program( "solver_benchmark.cpp" )

View File

@ -0,0 +1,119 @@
#include <CGAL/Polyhedron_3.h>
#include <CGAL/Polyhedron_items_with_id_3.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Eigen_solver_traits.h>
#include <CGAL/Mean_curvature_flow_skeletonization.h>
#include <CGAL/iterator.h>
#include <CGAL/internal/corefinement/Polyhedron_subset_extraction.h>
#include <CGAL/IO/Polyhedron_iostream.h>
#include <CGAL/Bbox_3.h>
#include <boost/property_map/property_map.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/iterator/transform_iterator.hpp>
#include <Eigen/SparseLU>
#include <Eigen/Sparse>
#include <fstream>
#include <map>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point;
typedef CGAL::Polyhedron_3<Kernel, CGAL::Polyhedron_items_with_id_3> Polyhedron;
typedef boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
typedef CGAL::Eigen_solver_traits<
Eigen::SparseLU<
CGAL::Eigen_sparse_matrix<double>::EigenType,
Eigen::COLAMDOrdering<int> > > SparseLU_solver;
typedef CGAL::Eigen_solver_traits<
Eigen::SimplicialLDLT<
CGAL::Eigen_sparse_matrix<double>::EigenType
> > SimplicialLDLT_solver;
typedef CGAL::Default D;
// The input of the skeletonization algorithm must be a pure triangular closed
// mesh and has only one component.
bool is_mesh_valid(Polyhedron& pMesh)
{
if (!pMesh.is_closed())
{
std::cerr << "The mesh is not closed.";
return false;
}
if (!pMesh.is_pure_triangle())
{
std::cerr << "The mesh is not a pure triangle mesh.";
return false;
}
// the algorithm is only applicable on a mesh
// that has only one connected component
std::size_t num_component;
CGAL::Counting_output_iterator output_it(&num_component);
CGAL::internal::extract_connected_components(pMesh, output_it);
++output_it;
if (num_component != 1)
{
std::cerr << "The mesh is not a single closed mesh. It has "
<< num_component << " components.";
return false;
}
return true;
}
int main()
{
Polyhedron mesh;
std::ifstream input("data/elephant.off");
if ( !input || !(input >> mesh) || mesh.empty() ) {
std::cerr << "Cannot open data/elephant.off" << std::endl;
return EXIT_FAILURE;
}
if (!is_mesh_valid(mesh)) {
return EXIT_FAILURE;
}
int NTEST = 10;
double sum = 0;
for (int i = 0; i < NTEST; i++)
{
typedef CGAL::Mean_curvature_flow_skeletonization<Polyhedron, D, D, SparseLU_solver> MCF_skel;
MCF_skel::Skeleton skeleton;
CGAL::Timer timer;
timer.start();
MCF_skel mcf_skel(mesh);
mcf_skel(skeleton);
timer.stop();
sum += timer.time();
}
std::cout << "Time of SparseLU: " << sum / NTEST << "\n";
sum = 0;
for (int i = 0; i < NTEST; i++)
{
typedef CGAL::Mean_curvature_flow_skeletonization<Polyhedron, D, D, SimplicialLDLT_solver> MCF_skel;
MCF_skel::Skeleton skeleton;
CGAL::Timer timer;
timer.start();
MCF_skel mcf_skel(mesh);
mcf_skel(skeleton);
timer.stop();
sum += timer.time();
}
std::cout << "Time of SimplicialLDLT: " << sum / NTEST << "\n";
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,175 @@
/*!
* \ingroup PkgMeanCurvatureSkeleton3Concepts
* \cgalConcept
*
* Traits class concept defining the requirements of the class `CGAL::Mean_curvature_flow_skeletonization`.
*
* \cgalHasModel Any \cgal `Kernel` with `double` as `%Kernel::%FT`
*
*
*/
class MeanCurvatureSkeletonizationTraits {
public:
/// \name Types
/// @{
/// The point type.
typedef unspecified_type Point_3;
/// The vector type.
typedef unspecified_type Vector_3;
/// The number type. It must be constructible from and convertible to `double`.
typedef unspecified_type FT;
/*!
* Function object type that provides
* `Point_3 operator()(FT x, FT y, FT z) const`
* returning the point with `x`, `y` and `z` as Cartesian coordinates.
*/
typedef unspecified_type Construct_point_3;
/*!
* Function object type that provides `Point_3 operator()(Point_3 p1, Point_3 p2) const`
* returning the vector `p1p2`, and `Point_3 operator()(NULL_VECTOR) const` returning the null vector.
*/
typedef unspecified_type Construct_vector_3;
/*!
* Function object type that provides
* `Vector_3 operator()(Vector_3 v, FT t) const`
* returning the vector `t * v`.
*/
typedef unspecified_type Construct_scaled_vector_3;
/*!
* Function object type that provides
* `Vector_3 operator()(Vector_3 v, FT t) const`
* returning the vector `v / t`.
*/
typedef unspecified_type Construct_divided_vector_3;
/*!
* Function object type that provides
* `Vector_3 operator()(Vector_3 v1, Vector_3 v2) const`
* returning the cross-product vector of `v1` and `v2`.
*/
typedef unspecified_type Construct_cross_product_vector_3;
/*!
* Function object type that provides
* `Vector_3 operator()(Vector_3 v1, Vector_3 v2) const`
* returning the `v1+v2`.
*/
typedef unspecified_type Construct_sum_of_vectors_3;
/*!
* Function object type that provides
* `Point_3 operator()(Point_3 p1, Point_3 p2) const`
* returning the midpoint of `p1` and `p2`.
*/
typedef unspecified_type Construct_midpoint_3;
/*!
* Function object type that provides
* `FT operator()(Point_3 p, Point_3 q) const`
* returning the squared distance between `p` and `q`.
*/
typedef unspecified_type Compute_squared_distance_3;
/*!
* Function object type that provides
* `FT operator()(Vector_3 v) const`
* returning the squared length of `v`.
*/
typedef unspecified_type Compute_squared_length_3;
/*!
* Function object type that provides
* `FT operator()(Point_3 p1,Point_3 p2,Point_3 p3) const`
* returning the area of the triangle defined by `p1`, `p2` and `p3`.
*/
typedef unspecified_type Compute_area_3;
/*!
* Function object type that provides
* `FT operator()(Vector_3 v1, Vector_3 v2) const`
* returning the scalar product of `v1` and `v2`.
*/
typedef unspecified_type Compute_scalar_product_3;
/*!
* Function object type that provides
* `FT operator()(Point_3 p)` and `FT operator()(Vector_3 v) const`
* returning the `x` coordinate of a point and a vector respectively.
*/
typedef unspecified_type Compute_x_3;
/*!
* Function object type that provides
* `FT operator()(Point_3 p)` and `FT operator()(Vector_3 v) const`
* returning the `y` coordinate of a point and a vector respectively.
*/
typedef unspecified_type Compute_y_3;
/*!
* Function object type that provides
* `FT operator()(Point_3 p)` and `FT operator()(Vector_3 v) const`
* returning the `z` coordinate of a point and a vector respectively.
*/
typedef unspecified_type Compute_z_3;
/// @}
/// \name Access to Function Objects
/// @{
Construct_point_3
construct_point_3_object();
Construct_vector_3
construct_vector_3_object();
Construct_scaled_vector_3
construct_scaled_vector_3_object();
Construct_divided_vector_3
construct_divided_vector_3_object();
Construct_cross_product_vector_3
construct_cross_product_vector_3_object();
Construct_sum_of_vectors_3
construct_sum_of_vectors_3_object();
Construct_midpoint_3
construct_midpoint_3_object();
Compute_squared_distance_3
compute_squared_distance_3_object();
Compute_squared_length_3
compute_squared_length_3_object();
Compute_area_3
compute_area_3_object();
Compute_scalar_product_3
compute_scalar_product_3_object();
Compute_x_3
compute_x_3_object();
Compute_y_3
compute_y_3_object();
Compute_z_3
compute_z_3_object();
/// @}
}; /* end DelaunayTriangulationTraits_2 */

View File

@ -0,0 +1,76 @@
/*!
\ingroup PkgMeanCurvatureSkeleton3Concepts
\cgalConcept
Concept describing the set of requirements for solving the normal equation \f$ A^t A X = A^t B \f$,
\f$ A \f$ being a matrix, \f$ At \f$ its transpose matrix, \f$ B \f$ and \f$ X \f$ being two vectors.
\sa `SparseLinearAlgebraTraits_d`
\cgalHasModel `CGAL::Eigen_solver_traits<T>`
*/
class NormalEquationSparseLinearAlgebraTraits_d {
public:
/// \name Types
/// @{
/*!
Matrix type model of `SparseLinearAlgebraTraits_d::Matrix`
*/
typedef unspecified_type Matrix;
/*!
Vector type model of `SparseLinearAlgebraTraits_d::Vector`
*/
typedef unspecified_type Vector;
/*!
Number type
*/
typedef unspecified_type NT;
/// @}
/// \name Creation
/// @{
/*!
Default constructor.
*/
NormalEquationSparseLinearAlgebraTraits_d();
/// @}
/// \name Operations
/// @{
/*!
Factorize the sparse matrix `At * A`.
This factorization is used in `normal_equation_solver()` to solve the system for different right-hand side vectors.
@return true if the factorization is successful
*/
bool normal_equation_factor(const Matrix& A);
/*!
Solve the sparse linear system `At * A * X = At * B`, with `A` being the matrix
provided in `#normal_equation_factor()`, and `At` its transpose matrix.
@return true if the solver is successful
*/
bool normal_equation_solver(const Vector& B, Vector& X);
/*!
Equivalent to a call to \link normal_equation_factor() `normal_equation_factor(A)` \endlink
followed by a call to \link normal_equation_solver `normal_equation_solver(B,X)` \endlink .
*/
bool normal_equation_solver(const Matrix& A, const Vector& B, Vector& X);
/// @}
}; /* end SparseLinearAlgebraTraitsWithFactor_d */

View File

@ -0,0 +1,10 @@
@INCLUDE = ${CGAL_DOC_PACKAGE_DEFAULTS}
PROJECT_NAME = "CGAL ${CGAL_CREATED_VERSION_NUM} - Triangulated Surface Mesh Skeletonization"
INPUT = ${CMAKE_SOURCE_DIR}/Surface_mesh_skeletonization/doc/Surface_mesh_skeletonization/ \
${CMAKE_SOURCE_DIR}/Surface_mesh_skeletonization/include
EXTRACT_ALL = false
HIDE_UNDOC_MEMBERS = true
HIDE_UNDOC_CLASSES = true
MULTILINE_CPP_IS_BRIEF = YES

View File

@ -0,0 +1,46 @@
/// \defgroup PkgMeanCurvatureSkeleton3 Triangulated Surface Mesh Skeletonization Reference
/// \defgroup PkgMeanCurvatureSkeleton3Concepts Concepts
/// \ingroup PkgMeanCurvatureSkeleton3
/*!
\addtogroup PkgMeanCurvatureSkeleton3
\cgalPkgDescriptionBegin{Triangulated Surface Mesh Skeletonization,PkgMeanCurvatureSkeleton3Summary}
\cgalPkgPicture{mcfskel-small.png}
\cgalPkgSummaryBegin
\cgalPkgAuthor{Xiang Gao, Sébastien Loriot and Andrea Tagliasacchi}
\cgalPkgDesc{This package provides a (1D) curve skeleton extraction algorithm for a triangulated polygonal mesh without borders based on the mean curvature flow.
The particularity of this skeleton is that it captures the topology of the input.
For each skeleton vertex one can obtain its location and its corresponding vertices from the input mesh.
The code is generic and works with any model of the `FaceListGraph`
concept.
}
\cgalPkgManuals{Chapter_3D_Surface_mesh_skeletonization,PkgMeanCurvatureSkeleton3}
\cgalPkgSummaryEnd
\cgalPkgShortInfoBegin
\cgalPkgSince{4.7}
\cgalPkgDependsOn{Sparse symmetric solver such as those from \ref thirdpartyEigen}
\cgalPkgBib{cgal:glt-tsms}
\cgalPkgLicense{\ref licensesGPL "GPL"}
\cgalPkgDemo{See Polyhedral Surface,polyhedron_3.zip}
\cgalPkgShortInfoEnd
\cgalPkgDescriptionEnd
\cgalClassifedRefPages
## Concepts ##
- `MeanCurvatureSkeletonizationTraits`
- `NormalEquationSparseLinearAlgebraTraits_d`
## Classes ##
- `CGAL::Mean_curvature_flow_skeletonization`
\todo code: implement the random sampling of surface using the work started by Alexandru during its gsoc to get a better approximation of poles
\todo code: expose in polygon mesh processing the function to compute the voronoi pole of a close triangle mesh
\todo code: expose in polygon mesh processing the function to remesh locally a triangle mesh with the angle and edge length parameters
\todo code: expose in polygon mesh processing the function compute the surface area of a polygon mesh
\todo code: avoid using EPEC for the triangulation
*/

View File

@ -0,0 +1,159 @@
namespace CGAL {
/*!
\mainpage User Manual
\anchor Chapter_3D_Surface_mesh_skeletonization
\cgalAutoToc
\author Xiang Gao, Sébastien Loriot and Andrea Tagliasacchi
\section MCFSkelSecMot Introduction
\cgalFigureBegin{Main_image_suggestion, main_image_suggestion.png}
Curve skeleton of a horse model.
\cgalFigureEnd
Skeletons are effective shape abstractions used in segmentation, shape
matching, reconstruction, virtual navigation, etc. As the name
implies, a curve skeleton is a graph of curvilinear structures (1D).
It is not a medial axis that for a 3D geometry is composed of surfaces (2D).
As illustrated in \cgalFigureRef{Main_image_suggestion}, the curve
skeleton of a shape captures its essential topology. In this package,
we implement the <i>Mean Curvature Skeleton</i> algorithm described in
\cgalCite{tagliasacchi2012mean} that extracts a curve skeleton from a
triangulated surface mesh without borders by iteratively contracting the
input triangulated surface mesh.
\section MCFSkelSecAPI User Interface Description
\subsection MCFSkelSecAPI-IO Input and Output
The input is a triangulated surface mesh, model of the `FaceListGraph`
concept (`CGAL::Surface_mesh`, `CGAL::Polyhedron_3`, ...)
that has no boundary and that has only one connected component.
The skeleton is provided as a graph of type
<a href="http://www.boost.org/doc/libs/release/libs/graph/doc/adjacency_list.html"><code>boost::adjacency_list</code></a>.
Each vertex of the skeleton is associated to a 3D location point and to the
set of input vertices that contracted to that skeleton vertex.
Note that due to the construction process of the skeleton, a
skeleton vertex might have no corresponding input vertex.
This package needs a sparse linear solver and we recommend the use of
\ref thirdpartyEigen 3.2 or later.
The input and output are illustrated in \cgalFigureRef{MCFInputOutput}.
\cgalFigureBegin{MCFInputOutput, correspondence.png}
Left: An input triangulated surface mesh model of an octopus;
Middle: The mean curvature flow skeleton extracted using this package;
Right: Each vertex of the input surface is associated to a skeleton vertex.
\cgalFigureEnd
\subsection MCFSkelSecAPI-FF Free Function
If a \cgal model of the `FaceListGraph` concept such as
`CGAL::Surface_mesh` or `CGAL::Polyhedron_3` is used as triangulated
input surface mesh, \ref thirdpartyEigen 3.2 or later is available
and `CGAL_EIGEN3_ENABLED` is defined, the function
`CGAL::extract_mean_curvature_flow_skeleton()` can be used to
extract a mean curvature flow skeleton from the input surface mesh
using the default parameters, which work well in most cases.
The following example shows how to extract a skeleton out of a triangulated
surface mesh and how to access the point of each skeleton vertex and the
set of input vertices associated.
\cgalExample{Surface_mesh_skeletonization/simple_mcfskel_example.cpp}
\subsection MCFSkelSecAPI-AUUFO Advanced Usage with the Function Object
The class `CGAL::Mean_curvature_flow_skeletonization` enables the
usage of low level functions such as \link
CGAL::Mean_curvature_flow_skeletonization::contract_geometry()
`contract_geometry()`\endlink and \link
CGAL::Mean_curvature_flow_skeletonization::collapse_edges()
`collapse_edges()`\endlink. The class further enables to change the
parameters of the algorithm, for example by calling \link
CGAL::Mean_curvature_flow_skeletonization::is_medially_centered()
`set_is_medially_centered()`\endlink. The class gives the user full
control over each step of the algorithm as well as the intermediate
contracted mesh (called <i>meso-skeleton</i>) as illustrated by
\cgalFigureRef{MCFMesoSkel}.
\cgalFigureBegin{MCFMesoSkel, iterations.png}
Three iterations of the mean curvature flow contraction of the horse of
\cgalFigureRef{Main_image_suggestion}. The red points indicate
vertices where the surface has locally degenerated to a curvilinear
structure.
\cgalFigureEnd
In this example, we show how to use the API of the class
`CGAL::Mean_curvature_flow_skeletonization`.
\cgalExample{Surface_mesh_skeletonization/MCF_Skeleton_sm_example.cpp}
\subsection Mesh Segmentation through Skeletonization
As a proof of concept, we show how to use the skeleton and the
association of input vertices to skeleton to compute a segmentation
of the input triangulated surface mesh using the package
\ref PkgSurfaceSegmentationSummary.
The segmentation algorithm consists in computing a <i>shape diameter function</i>
for each face of the input mesh, followed by solving a graph cut problem.
Here we use the skeleton to define a new shape diameter function.
Specifically, for each face we compute the diameter value as
the average distance between its three incident vertices and their
corresponding skeletal point.
The result of this segmentation is shown in \cgalFigureRef{Segmentation}.
\cgalFigureBegin{Segmentation, segmentation.png}
Left: Curve skeleton extracted of a triangulated surface mesh;
Right: Segmentation using the skeleton to compute a shape diameter function.
\cgalFigureEnd
\cgalExample{Surface_mesh_skeletonization/segmentation_example.cpp}
\section MCFSkelSecEvaluation Performance
The elephant model is used to illustrate the performance of the
mean curvature flow skeletonization procedure.
Different resolutions of the model obtained by loop subdivision are used.
As can be seen on \cgalFigureRef{MCFBenchComp}, the more sampled the
surface is, the better the skeleton is inside the model.
\cgalFigureBegin{MCFBenchComp, comparison.png}
Skeleton of the head of the elephant model.
Left: Using the original mesh (2,775 vertices);
Middle: After one loop subdivision step (11,112 vertices);
Right: After two loops subdivision steps (44,460 vertices).
\cgalFigureEnd
Runtime in milliseconds of `extract_mean_curvature_flow_skeleton()` using
`Polyhedron_3<Simple_cartesian<double> >` as input data structure. The runtimes are similar if
`Surface_mesh<Simple_cartesian<double>::%Point_3>` is used instead. The solver used is the
default one when \ref thirdpartyEigen is available.
The code was compiled using \cgal 4.7 with g++ 4.8.4 with <code>-O3 -DNDEBUG</code>
as compiling flags and was run on a Intel(R) Core(TM) i7-2820QM CPU @ 2.30GHz
<center>
Number of loop subdivision of <code>elephant.off</code> | Number of vertices | Runtime in s |
-------------------------------------------------------:| -------------------:| -----------: |
0 | 2,775 | 0.768 |
1 | 11,112 | 2.212 |
2 | 44,460 | 9.948 |
3 | 177,852 | 41.248 |
</center>
\section MCFSkelSecDesign Design and Implementation History
The initial implementation of this package is the result of the work
of Xiang Gao during the 2013 season of the Google Summer of Code
mentored by Andrea Tagliasacchi and Sébastien Loriot. It was finalized
by Andreas Fabri and Sébastien Loriot.
*/
} /* namespace CGAL */

View File

@ -0,0 +1,13 @@
Manual
Kernel_23
STL_Extension
Algebraic_foundations
Circulator
Stream_support
BGL
Polyhedron
Surface_modeling
Surface_mesh_parameterization
Surface_mesh_segmentation
Surface_mesh

View File

@ -0,0 +1,7 @@
/*!
\example Surface_mesh_skeletonization/simple_mcfskel_example.cpp
\example Surface_mesh_skeletonization/MCF_Skeleton_example.cpp
\example Surface_mesh_skeletonization/segmentation_example.cpp
\example Surface_mesh_skeletonization/MCF_Skeleton_sm_example.cpp
\example Surface_mesh_skeletonization/simple_mcfskel_sm_example.cpp
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

View File

@ -0,0 +1,47 @@
# Created by the script cgal_create_cmake_script
# This is the CMake script for compiling a CGAL application.
project( Surface_mesh_skeletonization_ )
cmake_minimum_required(VERSION 2.6.2)
if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_GREATER 2.6)
if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}" VERSION_GREATER 2.8.3)
cmake_policy(VERSION 2.8.4)
else()
cmake_policy(VERSION 2.6)
endif()
endif()
find_package(CGAL QUIET COMPONENTS Core )
if ( CGAL_FOUND )
include( ${CGAL_USE_FILE} )
include( CGAL_CreateSingleSourceCGALProgram )
find_package(Eigen3 3.2.0) #(requires 3.2.0 or greater)
if(EIGEN3_FOUND)
include( ${EIGEN3_USE_FILE} )
include( CGAL_CreateSingleSourceCGALProgram )
include_directories (BEFORE "../../include")
create_single_source_cgal_program( "simple_mcfskel_example.cpp" )
create_single_source_cgal_program( "simple_mcfskel_sm_example.cpp" )
create_single_source_cgal_program( "MCF_Skeleton_example.cpp" )
create_single_source_cgal_program( "MCF_Skeleton_sm_example.cpp" )
create_single_source_cgal_program( "segmentation_example.cpp" )
else()
message(STATUS "These programs require the Eigen library (3.2 or greater), and will not be compiled.")
endif()
else()
message(STATUS "This program requires the CGAL library, and will not be compiled.")
endif()

View File

@ -0,0 +1,72 @@
#include <CGAL/Polyhedron_3.h>
#include <CGAL/IO/Polyhedron_iostream.h>
#include <CGAL/boost/graph/graph_traits_Polyhedron_3.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Mean_curvature_flow_skeletonization.h>
#include <fstream>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point;
typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
typedef boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
typedef CGAL::Mean_curvature_flow_skeletonization<Polyhedron> Skeletonization;
typedef Skeletonization::Skeleton Skeleton;
typedef Skeleton::vertex_descriptor Skeleton_vertex;
typedef Skeleton::edge_descriptor Skeleton_edge;
int main(int argc, char* argv[])
{
std::ifstream input((argc>1)?argv[1]:"data/elephant.off");
Polyhedron tmesh;
input >> tmesh;
Skeleton skeleton;
Skeletonization mcs(tmesh);
// 1. Contract the mesh by mean curvature flow.
mcs.contract_geometry();
// 2. Collapse short edges and split bad triangles.
mcs.collapse_edges();
mcs.split_faces();
// 3. Fix degenerate vertices.
mcs.detect_degeneracies();
// Perform the above three steps in one iteration.
mcs.contract();
// Iteratively apply step 1 to 3 until convergence.
mcs.contract_until_convergence();
// Convert the contracted mesh into a curve skeleton and
// get the correspondent surface points
mcs.convert_to_skeleton(skeleton);
std::cout << "Number of vertices of the skeleton: " << boost::num_vertices(skeleton) << "\n";
std::cout << "Number of edges of the skeleton: " << boost::num_edges(skeleton) << "\n";
// Output all the edges of the skeleton.
std::ofstream output("skel.cgal");
BOOST_FOREACH(Skeleton_edge e, edges(skeleton))
{
const Point& s = skeleton[source(e, skeleton)].point;
const Point& t = skeleton[target(e, skeleton)].point;
output << "2 "<< s << " " << t << "\n";
}
output.close();
// Output skeleton points and the corresponding surface points
output.open("correspondance.cgal");
BOOST_FOREACH(Skeleton_vertex v, vertices(skeleton))
BOOST_FOREACH(vertex_descriptor vd, skeleton[v].vertices)
output << "2 " << skeleton[v].point << " " << get(CGAL::vertex_point, tmesh, vd) << "\n";
return 0;
}

View File

@ -0,0 +1,70 @@
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/boost/graph/graph_traits_Surface_mesh.h>
#include <CGAL/boost/graph/properties_Surface_mesh.h>
#include <CGAL/Mean_curvature_flow_skeletonization.h>
#include <fstream>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point;
typedef CGAL::Surface_mesh<Point> Triangle_mesh;
typedef boost::graph_traits<Triangle_mesh>::vertex_descriptor vertex_descriptor;
typedef CGAL::Mean_curvature_flow_skeletonization<Triangle_mesh> Skeletonization;
typedef Skeletonization::Skeleton Skeleton;
typedef Skeleton::vertex_descriptor Skeleton_vertex;
typedef Skeleton::edge_descriptor Skeleton_edge;
int main(int argc, char* argv[])
{
std::ifstream input((argc>1)?argv[1]:"data/elephant.off");
Triangle_mesh tmesh;
input >> tmesh;
Skeleton skeleton;
Skeletonization mcs(tmesh);
// 1. Contract the mesh by mean curvature flow.
mcs.contract_geometry();
// 2. Collapse short edges and split bad triangles.
mcs.collapse_edges();
mcs.split_faces();
// 3. Fix degenerate vertices.
mcs.detect_degeneracies();
// Perform the above three steps in one iteration.
mcs.contract();
// Iteratively apply step 1 to 3 until convergence.
mcs.contract_until_convergence();
// Convert the contracted mesh into a curve skeleton and
// get the correspondent surface points
mcs.convert_to_skeleton(skeleton);
std::cout << "Number of vertices of the skeleton: " << boost::num_vertices(skeleton) << "\n";
std::cout << "Number of edges of the skeleton: " << boost::num_edges(skeleton) << "\n";
// Output all the edges of the skeleton.
std::ofstream output("skel.cgal");
BOOST_FOREACH(Skeleton_edge e, edges(skeleton))
{
const Point& s = skeleton[source(e, skeleton)].point;
const Point& t = skeleton[target(e, skeleton)].point;
output << "2 "<< s << " " << t << "\n";
}
output.close();
// Output skeleton points and the corresponding surface points
output.open("correspondance.cgal");
BOOST_FOREACH(Skeleton_vertex v, vertices(skeleton))
BOOST_FOREACH(vertex_descriptor vd, skeleton[v].vertices)
output << "2 " << skeleton[v].point << " " << get(CGAL::vertex_point, tmesh, vd) << "\n";
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,98 @@
#include <CGAL/Polyhedron_3.h>
#include <CGAL/Polyhedron_items_with_id_3.h>
#include <CGAL/IO/Polyhedron_iostream.h>
#include <CGAL/boost/graph/graph_traits_Polyhedron_3.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/extract_mean_curvature_flow_skeleton.h>
#include <CGAL/mesh_segmentation.h>
#include <fstream>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point;
typedef CGAL::Polyhedron_3<Kernel, CGAL::Polyhedron_items_with_id_3> Polyhedron;
typedef boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
typedef boost::graph_traits<Polyhedron>::halfedge_descriptor halfedge_descriptor;
typedef boost::graph_traits<Polyhedron>::face_descriptor face_descriptor;
typedef CGAL::Mean_curvature_flow_skeletonization<Polyhedron> Skeletonization;
typedef Skeletonization::Skeleton Skeleton;
typedef Skeleton::vertex_descriptor Skeleton_vertex;
// Property map associating a facet with an integer as id to an
// element in a vector stored internally
template<class ValueType>
struct Facet_with_id_pmap
: public boost::put_get_helper<ValueType&,
Facet_with_id_pmap<ValueType> >
{
typedef face_descriptor key_type;
typedef ValueType value_type;
typedef value_type& reference;
typedef boost::lvalue_property_map_tag category;
Facet_with_id_pmap(
std::vector<ValueType>& internal_vector
) : internal_vector(internal_vector) { }
reference operator[](key_type key) const
{ return internal_vector[key->id()]; }
private:
std::vector<ValueType>& internal_vector;
};
int main(int argc, char* argv[])
{
std::ifstream input((argc>1)?argv[1]:"data/161.off");
Polyhedron tmesh;
input >> tmesh;
// extract the skeleton
Skeleton skeleton;
CGAL::extract_mean_curvature_flow_skeleton(tmesh, skeleton);
// init the polyhedron simplex indices
CGAL::set_halfedgeds_items_id(tmesh);
//for each input vertex compute its distance to the skeleton
std::vector<double> distances(num_vertices(tmesh));
BOOST_FOREACH(Skeleton_vertex v, vertices(skeleton) )
{
const Point& skel_pt = skeleton[v].point;
BOOST_FOREACH(vertex_descriptor mesh_v, skeleton[v].vertices)
{
const Point& mesh_pt = mesh_v->point();
distances[mesh_v->id()] = std::sqrt(CGAL::squared_distance(skel_pt, mesh_pt));
}
}
// create a property-map for sdf values
std::vector<double> sdf_values( num_faces(tmesh) );
Facet_with_id_pmap<double> sdf_property_map(sdf_values);
// compute sdf values with skeleton
BOOST_FOREACH(face_descriptor f, faces(tmesh))
{
double dist = 0;
BOOST_FOREACH(halfedge_descriptor hd, halfedges_around_face(halfedge(f, tmesh), tmesh))
dist+=distances[target(hd, tmesh)->id()];
sdf_property_map[f] = dist / 3.;
}
// post-process the sdf values
CGAL::sdf_values_postprocessing(tmesh, sdf_property_map);
// create a property-map for segment-ids (it is an adaptor for this case)
std::vector<std::size_t> segment_ids( num_faces(tmesh) );
Facet_with_id_pmap<std::size_t> segment_property_map(segment_ids);
// segment the mesh using default parameters
std::cout << "Number of segments: "
<< CGAL::segmentation_from_sdf_values(tmesh, sdf_property_map, segment_property_map) <<"\n";
return 0;
}

View File

@ -0,0 +1,62 @@
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/IO/Polyhedron_iostream.h>
#include <CGAL/boost/graph/graph_traits_Polyhedron_3.h>
#include <CGAL/boost/graph/properties_Polyhedron_3.h>
#include <CGAL/extract_mean_curvature_flow_skeleton.h>
#include <fstream>
#include <boost/foreach.hpp>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point;
typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
typedef boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
typedef CGAL::Mean_curvature_flow_skeletonization<Polyhedron> Skeletonization;
typedef Skeletonization::Skeleton Skeleton;
typedef Skeleton::vertex_descriptor Skeleton_vertex;
typedef Skeleton::edge_descriptor Skeleton_edge;
// This example extracts a medially centered skeleton from a given mesh.
int main(int argc, char* argv[])
{
std::ifstream input((argc>1)?argv[1]:"data/elephant.off");
Polyhedron tmesh;
input >> tmesh;
Skeleton skeleton;
CGAL::extract_mean_curvature_flow_skeleton(tmesh, skeleton);
std::cout << "Number of vertices of the skeleton: " << boost::num_vertices(skeleton) << "\n";
std::cout << "Number of edges of the skeleton: " << boost::num_edges(skeleton) << "\n";
// Output all the edges of the skeleton.
std::ofstream output("skel.cgal");
BOOST_FOREACH(Skeleton_edge e, edges(skeleton))
{
const Point& s = skeleton[source(e, skeleton)].point;
const Point& t = skeleton[target(e, skeleton)].point;
output << s << " " << t << "\n";
}
output.close();
// Output skeleton points and the corresponding surface points
output.open("correspondance.cgal");
BOOST_FOREACH(Skeleton_vertex v, vertices(skeleton))
{
output << skeleton[v].point << ": ";
BOOST_FOREACH(vertex_descriptor vd, skeleton[v].vertices)
output << get(CGAL::vertex_point, tmesh, vd) << " ";
output << "\n";
}
return 0;
}

View File

@ -0,0 +1,61 @@
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/boost/graph/graph_traits_Polyhedron_3.h>
#include <CGAL/boost/graph/properties_Polyhedron_3.h>
#include <CGAL/extract_mean_curvature_flow_skeleton.h>
#include <fstream>
#include <boost/foreach.hpp>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point;
typedef CGAL::Surface_mesh<Point> Triangle_mesh;
typedef boost::graph_traits<Triangle_mesh>::vertex_descriptor vertex_descriptor;
typedef CGAL::Mean_curvature_flow_skeletonization<Triangle_mesh> Skeletonization;
typedef Skeletonization::Skeleton Skeleton;
typedef Skeleton::vertex_descriptor Skeleton_vertex;
typedef Skeleton::edge_descriptor Skeleton_edge;
// This example extracts a medially centered skeleton from a given mesh.
int main(int argc, char* argv[])
{
std::ifstream input((argc>1)?argv[1]:"data/elephant.off");
Triangle_mesh tmesh;
input >> tmesh;
Skeleton skeleton;
CGAL::extract_mean_curvature_flow_skeleton(tmesh, skeleton);
std::cout << "Number of vertices of the skeleton: " << boost::num_vertices(skeleton) << "\n";
std::cout << "Number of edges of the skeleton: " << boost::num_edges(skeleton) << "\n";
// Output all the edges of the skeleton.
std::ofstream output("skel.cgal");
BOOST_FOREACH(Skeleton_edge e, edges(skeleton))
{
const Point& s = skeleton[source(e, skeleton)].point;
const Point& t = skeleton[target(e, skeleton)].point;
output << s << " " << t << "\n";
}
output.close();
// Output skeleton points and the corresponding surface points
output.open("correspondance.cgal");
BOOST_FOREACH(Skeleton_vertex v, vertices(skeleton))
{
output << skeleton[v].point << ": ";
BOOST_FOREACH(vertex_descriptor vd, skeleton[v].vertices)
output << get(CGAL::vertex_point, tmesh, vd) << " ";
output << "\n";
}
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,67 @@
// Copyright (c) 2014 GeometryFactory (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
// 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.
//
// $URL$
// $Id$
//
// Author(s) : Sebastien Loriot
//
#ifndef CGAL_EXTRACT_MEAN_CURVATURE_FLOW_SKELETON_H
#define CGAL_EXTRACT_MEAN_CURVATURE_FLOW_SKELETON_H
#include <CGAL/Polyhedron_3.h>
#include <CGAL/Polyhedron_items_with_id_3.h>
#include <CGAL/FaceGraph_to_Polyhedron_3.h>
#ifdef CGAL_EIGEN3_ENABLED
#include <CGAL/Eigen_solver_traits.h> // for sparse linear system solver
#endif
#include <CGAL/Mean_curvature_flow_skeletonization.h>
namespace CGAL{
#if defined(DOXYGEN_RUNNING) || defined(CGAL_EIGEN3_ENABLED)
/// \ingroup PkgMeanCurvatureSkeleton3
/// @brief extracts a medially centered curve skeleton for the triangle mesh `tmesh`.
/// This function uses the class CGAL::Mean_curvature_flow_skeletonization with the default parameters.
/// This function is provided only if \ref thirdpartyEigen "Eigen" 3.2 (or greater) is available and `CGAL_EIGEN3_ENABLED` is defined.
/// @pre `tmesh` is a triangle mesh without borders and having exactly one connected component.
/// @pre The specialization `boost::property_map<TriangleMesh, CGAL::vertex_point_t>::%type` and `get(vertex_point, tmesh)` are defined.
/// @pre The value type of `boost::property_map<TriangleMesh, CGAL::vertex_point_t>::%type` is a point type from a \cgal Kernel.
///
/// @tparam TriangleMesh
/// a model of `FaceListGraph`
///
/// @param tmesh
/// input mesh
/// @param skeleton
/// graph that will contain the skeleton of `tmesh`.
/// For each vertex descriptor `vd` of `skeleton`, the corresponding point
/// and the set of input vertices that contracted to `vd` can be retrieved
/// using `skeleton[vd].point` and `skeleton[vd].vertices` respectively.
///
template <class TriangleMesh>
void extract_mean_curvature_flow_skeleton(const TriangleMesh& tmesh,
typename Mean_curvature_flow_skeletonization<TriangleMesh>::Skeleton& skeleton)
{
// extract the skeleton
typedef CGAL::Mean_curvature_flow_skeletonization<TriangleMesh> Mcfskel;
Mcfskel mcfs(tmesh);
mcfs(skeleton);
}
#endif
}// end of namespace CGAL
#endif //CGAL_EXTRACT_MEAN_CURVATURE_FLOW_SKELETON_H

View File

@ -0,0 +1,617 @@
// Copyright (c) 2013 GeometryFactory (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
// 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.
//
// $URL$
// $Id$
//
// Author(s) : Xiang Gao <gaox@ethz.ch>
//
#ifndef CGAL_MCFSKEL_CURVE_SKELETON_H
#define CGAL_MCFSKEL_CURVE_SKELETON_H
/// @cond CGAL_DOCUMENT_INTERNAL
/**
* @file Curve_skeleton.h
* @brief This file contains the class used to turn a contracted mesh to a
* curve skeleton.
*
*/
#include <cmath>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/copy.hpp>
// For debugging macro
#include <CGAL/internal/Surface_mesh_skeletonization/Debug.h>
namespace CGAL {
namespace internal {
template <class TriangleMesh, class VertexIndexMap,
class HalfedgeIndexMap, class TriangleMeshPointPMap>
class Curve_skeleton
{
// Public types
public:
// Geometric types
typedef typename TriangleMesh::Traits Kernel;
typedef typename Kernel::Vector_3 Vector;
typedef typename Kernel::Point_3 Point;
// Repeat TriangleMesh types
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::face_descriptor face_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::edge_descriptor edge_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
// Data members
private:
std::vector<std::vector<int> > edge_to_face;
std::vector<std::vector<int> > edge_to_vertex;
std::vector<std::vector<int> > vertex_to_edge;
std::vector<std::vector<int> > face_to_edge;
std::vector<bool> is_vertex_deleted;
std::vector<bool> is_edge_deleted;
std::vector<bool> is_face_deleted;
std::vector<int> surface_vertex_id;
// records the vertices collapsed to a given vertex
std::vector<std::vector<int> > record;
// vertex id mapped to vertex descriptor
std::vector<vertex_descriptor> id_to_descriptor;
TriangleMesh& hg;
VertexIndexMap vertex_id_pmap;
HalfedgeIndexMap hedge_id_pmap;
TriangleMeshPointPMap hg_point_pmap;
std::vector<double> edge_squared_lengths;
class EdgeCompareFunctor
{
public:
std::vector<double> edge_squared_lengths;
EdgeCompareFunctor(std::vector<double>& squared_lengths)
{
edge_squared_lengths = squared_lengths;
}
bool operator() (const int& e0, const int& e1) const
{
double p0 = edge_squared_lengths[e0];
double p1 = edge_squared_lengths[e1];
return (p0 == p1) ? (e0 < e1) : (p0 < p1);
}
};
// type of priority queue for edges
typedef std::set<int, EdgeCompareFunctor> Edge_queue;
// Public methods
public:
Curve_skeleton(TriangleMesh& hg,
VertexIndexMap vertex_id_pmap,
HalfedgeIndexMap hedge_id_pmap,
TriangleMeshPointPMap hg_point_pmap)
: hg(hg)
, vertex_id_pmap(vertex_id_pmap)
, hedge_id_pmap(hedge_id_pmap)
, hg_point_pmap(hg_point_pmap)
{}
// Extracting the skeleton to a boost::graph data structure.
template <class Graph>
void extract_skeleton(Graph& curve)
{
typedef typename boost::graph_traits<Graph>::vertex_descriptor vertex_desc;
typedef typename boost::graph_traits<Graph>::edge_descriptor edge_desc;
init();
collapse();
// orig_vertex_id maps the new id for a vertex to its original id.
// new_vertex_id only contains id for vertices that have not been deleted.
std::vector<int> new_vertex_id;
std::vector<int> orig_vertex_id;
new_vertex_id.clear();
new_vertex_id.resize(vertex_to_edge.size(), -1);
orig_vertex_id.clear();
orig_vertex_id.resize(vertex_to_edge.size(), -1);
int id = 0;
for (size_t i = 0; i < is_vertex_deleted.size(); ++i)
{
if (!is_vertex_deleted[i])
{
orig_vertex_id[id] = static_cast<int>(i);
new_vertex_id[i] = id++;
}
}
// Mapping a skeleton vertex id to its descriptor.
std::vector<vertex_desc> id_to_vd;
id_to_vd.clear();
id_to_vd.resize(id);
for (int i = 0; i < id; ++i)
id_to_vd[i] = boost::add_vertex(curve);
for (int i = 0; i < id; ++i)
{
int orig_id = orig_vertex_id[i];
vertex_desc vd = id_to_vd[i];
/// code that is not working
BOOST_FOREACH(int vid, record[orig_id])
{
vertex_descriptor ovd = id_to_descriptor[vid];
curve[vd].vertices.insert(
curve[vd].vertices.end(),
ovd->vertices.begin(),
ovd->vertices.end()
);
}
}
for (size_t i = 0; i < is_edge_deleted.size(); ++i)
{
if (!is_edge_deleted[i])
{
int p1 = edge_to_vertex[i][0];
int p2 = edge_to_vertex[i][1];
int p1_id = new_vertex_id[p1];
int p2_id = new_vertex_id[p2];
vertex_desc p1_vd = id_to_vd[p1_id];
vertex_desc p2_vd = id_to_vd[p2_id];
bool exist;
edge_desc edge;
boost::tie(edge, exist) = boost::edge(p1_vd, p2_vd, curve);
if (!exist)
{
boost::add_edge(p1_vd, p2_vd, curve);
}
}
}
BOOST_FOREACH(vertex_descriptor vd, vertices(hg))
{
int id = static_cast<int>(get(vertex_id_pmap, vd));
int new_id = new_vertex_id[id];
if (new_id == -1)
{
continue;
}
// move to the centroid
Point pos = Point(0, 0, 0);
for (size_t i = 0; i < record[id].size(); ++i)
{
vertex_descriptor vd = id_to_descriptor[record[id][i]];
Point pv = get(hg_point_pmap, vd);
pos = Point(pos.x() + pv.x(), pos.y() + pv.y(), pos.z() + pv.z());
}
double num = static_cast<double>(record[id].size());
curve[id_to_vd[new_id]].point = Point(pos.x() / num, pos.y() / num, pos.z() / num);
}
}
// Private methods
private:
void init()
{
MCFSKEL_DEBUG( std::cerr <<"init" << std::endl; )
int nb_edges = static_cast<int>(num_edges(hg));
int num_faces = static_cast<int>(hg.size_of_facets());
int nb_vertices = static_cast<int>(num_vertices(hg));
edge_to_face.resize(nb_edges);
edge_to_vertex.resize(nb_edges);
vertex_to_edge.resize(nb_vertices);
face_to_edge.resize(num_faces);
is_vertex_deleted.resize(nb_vertices, false);
is_edge_deleted.resize(nb_edges, false);
is_face_deleted.resize(num_faces, false);
record.resize(nb_vertices);
for (size_t i = 0; i < record.size(); ++i)
{
record[i].push_back(static_cast<int>(i));
}
id_to_descriptor.resize(nb_vertices);
edge_squared_lengths.resize(nb_edges);
// assign vertex id
surface_vertex_id.resize(nb_vertices);
int idx = 0;
BOOST_FOREACH(vertex_descriptor vd, vertices(hg))
{
surface_vertex_id[idx] = static_cast<int>(get(vertex_id_pmap, vd));
put(vertex_id_pmap, vd, idx++);
}
// assign edge id
// the two halfedges representing the same edge get the same id
idx = 0;
BOOST_FOREACH(edge_descriptor ed, edges(hg))
{
halfedge_descriptor hd = halfedge(ed, hg);
put(hedge_id_pmap, hd, idx);
halfedge_descriptor hd_opposite = opposite(hd,hg);
put(hedge_id_pmap, hd_opposite, idx);
// also cache the length of the edge
vertex_descriptor v1 = target(hd,hg);
vertex_descriptor v2 = source(hd,hg);
Point source = get(hg_point_pmap, v1);
Point target = get(hg_point_pmap, v2);
edge_squared_lengths[idx] = squared_distance(source, target);
++idx;
}
// assign face id and compute edge-face connectivity
int face_id = 0;
BOOST_FOREACH(face_descriptor fd, faces(hg))
{
BOOST_FOREACH(halfedge_descriptor hd, halfedges_around_face(halfedge(fd,hg), hg))
{
int id = static_cast<int>(get(hedge_id_pmap, hd));
face_to_edge[face_id].push_back(id);
edge_to_face[id].push_back(face_id);
}
++face_id;
}
// compute vertex-edge connectivity
BOOST_FOREACH(vertex_descriptor vd, vertices(hg))
{
int vid = static_cast<int>(get(vertex_id_pmap, vd));
BOOST_FOREACH(edge_descriptor ed, in_edges(vd, hg))
{
halfedge_descriptor hd = halfedge(ed, hg);
int eid = static_cast<int>(get(hedge_id_pmap, hd));
vertex_to_edge[vid].push_back(eid);
edge_to_vertex[eid].push_back(vid);
}
// save the vertex descriptor
id_to_descriptor[vid] = vd;
}
}
void init_queue(Edge_queue& queue)
{
// put all the edges into a priority queue
// shorter edge has higher priority
BOOST_FOREACH(edge_descriptor ed, edges(hg))
{
int id = static_cast<int>(get(hedge_id_pmap, halfedge(ed, hg)));
queue.insert(id);
}
}
// iteratively collapse short edges until no edges have incident faces
void collapse()
{
EdgeCompareFunctor edge_comparator(edge_squared_lengths);
Edge_queue queue(edge_comparator);
init_queue(queue);
// start collapsing edges until all the edges have no incident faces
while (!queue.empty())
{
int eid = *(queue.begin());
queue.erase(queue.begin());
// skip already deleted edges and edges with no face
if (is_edge_deleted[eid]) continue;
if (edge_to_face[eid].size() == 0) continue;
// mark the incident faces as deleted
remove_incident_faces(eid);
// p1 to be deleted
int p1 = edge_to_vertex[eid][0];
int p2 = edge_to_vertex[eid][1];
is_vertex_deleted[p1] = true;
// merge vertices collapsed on p1 to p2
update_record(p1, p2);
// delete the edge from p1 and p2's incident edges
delete_edge(p1, p2, eid);
// add the incident edges of p1 to p2
add_edge(queue, p1, p2);
// remove duplicate edges
std::vector<int> vertex_to_edge_p2(vertex_to_edge[p2]);
for (size_t i = 0; i < vertex_to_edge_p2.size(); ++i)
{
// ei to be removed
int ei = vertex_to_edge_p2[i];
for (size_t j = i + 1; j < vertex_to_edge_p2.size(); ++j)
{
int ej = vertex_to_edge_p2[j];
if (is_same_edge(ei, ej) || is_edge_deleted[ei])
{
// look for ei from p2's incident edges
bool found;
int ind;
boost::tie(found, ind) = find_edge(vertex_to_edge[p2], ei);
if (!found)
{
continue;
}
// migrate faces from ei to ej
move_face(ei, ej);
// finally remove ei from p2
remove_edge(p2, ei, ind);
break;
}
}
}
}
// for debugging purpose
MCFSKEL_INFO(print_stat();)
MCFSKEL_INFO(check_edge();)
}
void add_edge(Edge_queue& queue, int p1, int p2)
{
for (size_t i = 0; i < vertex_to_edge[p1].size(); ++i)
{
int edge = vertex_to_edge[p1][i];
if (is_edge_deleted[edge])
{
continue;
}
vertex_to_edge[p2].push_back(edge);
// after change the incident vertex of an edge,
// we need to update the length of the edge
update_edge_length(queue, edge, p1, p2);
// change the incident vertex to p2
for (size_t j = 0; j < edge_to_vertex[edge].size(); ++j)
{
if (edge_to_vertex[edge][j] == p1)
{
edge_to_vertex[edge][j] = p2;
}
}
}
}
void remove_incident_faces(int eid)
{
BOOST_FOREACH(int fid, edge_to_face[eid])
{
is_face_deleted[fid] = true;
// remove the face from the container of the other incident edges
for (size_t j = 0; j < face_to_edge[fid].size(); ++j)
{
int e = face_to_edge[fid][j];
if (e==eid) continue;
for (size_t k = 0; k < edge_to_face[e].size(); ++k)
{
if (edge_to_face[e][k] == fid)
{
edge_to_face[e].erase(edge_to_face[e].begin() + k);
break;
}
}
}
}
edge_to_face[eid].clear();
}
void delete_edge(int p1, int p2, int eid)
{
for (size_t i = 0; i < vertex_to_edge[p1].size(); ++i)
{
if (vertex_to_edge[p1][i] == eid)
{
vertex_to_edge[p1].erase(vertex_to_edge[p1].begin() + i);
break;
}
}
for (size_t i = 0; i < vertex_to_edge[p2].size(); ++i)
{
if (vertex_to_edge[p2][i] == eid)
{
vertex_to_edge[p2].erase(vertex_to_edge[p2].begin() + i);
break;
}
}
is_edge_deleted[eid] = true;
}
bool is_same_edge(int ei, int ej)
{
if (edge_to_vertex[ei][0] == edge_to_vertex[ej][0]
&& edge_to_vertex[ei][1] == edge_to_vertex[ej][1])
{
return true;
}
if (edge_to_vertex[ei][1] == edge_to_vertex[ej][0]
&& edge_to_vertex[ei][0] == edge_to_vertex[ej][1])
{
return true;
}
return false;
}
std::pair<bool, int> find_edge(std::vector<int>& edges, int eid)
{
for (size_t i = 0; i < edges.size(); ++i)
{
if (eid == edges[i])
{
return std::make_pair(true, static_cast<int>(i));
}
}
return std::make_pair(false, -1);
}
void move_face(int ei, int ej)
{
for (size_t i = 0; i < edge_to_face[ei].size(); ++i)
{
int fid = edge_to_face[ei][i];
if (!is_face_deleted[fid])
{
if (std::find(edge_to_face[ej].begin(),
edge_to_face[ej].end(),
fid)
== edge_to_face[ej].end())
{
edge_to_face[ej].push_back(fid);
for (size_t j = 0; j < face_to_edge[fid].size(); ++j)
{
if (face_to_edge[fid][j] == ei)
{
face_to_edge[fid][j] = ej;
break;
}
}
}
}
}
}
void remove_edge(int v, int e, int ind)
{
vertex_to_edge[v].erase(vertex_to_edge[v].begin() + ind);
// and also remove ei from the other end point
for (size_t i = 0; i < edge_to_vertex[e].size(); ++i)
{
int vid = edge_to_vertex[e][i];
if (vid != v)
{
for (size_t j = 0; j < vertex_to_edge[vid].size(); ++j)
{
if (vertex_to_edge[vid][j] == e)
{
vertex_to_edge[vid].erase(vertex_to_edge[vid].begin() + j);
break;
}
}
}
}
is_edge_deleted[e] = true;
}
void update_record(int p1, int p2)
{
for (size_t i = 0; i < record[p1].size(); ++i)
{
record[p2].push_back(record[p1][i]);
}
record[p1].clear();
}
void update_edge_length(Edge_queue& queue, int eid, int p1, int p2)
{
int vid1 = edge_to_vertex[eid][0];
int vid2 = edge_to_vertex[eid][1];
if (vid1 == p1)
{
vid1 = p2;
}
else
{
vid2 = p2;
}
if (queue.find(eid) != queue.end())
{
vertex_descriptor v1 = id_to_descriptor[vid1];
vertex_descriptor v2 = id_to_descriptor[vid2];
Point source = get(hg_point_pmap, v1);
Point target = get(hg_point_pmap, v2);
double new_len = squared_distance(source, target);
edge_squared_lengths[eid] = new_len;
queue.insert(eid);
}
}
void check_edge()
{
BOOST_FOREACH(halfedge_descriptor hd, halfedges(hg))
{
int id = get(hedge_id_pmap, hd);
if (!is_edge_deleted[id])
{
if (edge_to_face[id].size() > 0)
{
std::cerr << "edge should not have faces " << edge_to_face[id].size() << "\n";
}
}
}
}
void print_stat()
{
int cnt = 0;
for (size_t i = 0; i < is_vertex_deleted.size(); ++i)
{
if (!is_vertex_deleted[i])
{
++cnt;
}
}
std::cerr << "num of vertices " << cnt << "\n";
cnt = 0;
for (size_t i = 0; i < is_edge_deleted.size(); ++i)
{
if (!is_edge_deleted[i])
{
++cnt;
}
}
std::cerr << "num of edges " << cnt << "\n";
cnt = 0;
for (size_t i = 0; i < is_face_deleted.size(); ++i)
{
if (!is_face_deleted[i])
{
++cnt;
}
}
std::cerr << "num of faces " << cnt << "\n";
}
};
} // namespace internal
} // namespace CGAL
/// @endcond
#endif // CGAL_MCFSKEL_CURVE_SKELETON_H

View File

@ -0,0 +1,54 @@
// Copyright (c) 2013 GeometryFactory (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
// 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.
//
// $URL$
// $Id$
//
// Author(s) : Xiang Gao <gaox@ethz.ch>
//
#ifndef CGAL_MCFSKEL_DEBUG_H
#define CGAL_MCFSKEL_DEBUG_H
/// @cond CGAL_DOCUMENT_INTERNAL
/**
* @file Debug.h
* @brief This file contains some macro used to hide/show the log for debugging
* purpose.
*
*/
// enable debugging output statement
// this is for locating bugs
//#define CGAL_MCFSKEL_DEBUG
// enable info output statement
// this is for tuning parameters
//#define CGAL_MCFSKEL_INFO
#ifdef CGAL_MCFSKEL_DEBUG
#define MCFSKEL_DEBUG(x) x
#else
#define MCFSKEL_DEBUG(x)
#endif
#ifdef CGAL_MCFSKEL_INFO
#define MCFSKEL_INFO(x) x
#else
#define MCFSKEL_INFO(x)
#endif
/// @endcond
#endif // CGAL_MCFSKEL_DEBUG_H

View File

@ -0,0 +1,164 @@
// Copyright (c) 2013 GeometryFactory (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
// 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.
//
// $URL$
// $Id$
//
// Author(s) : Xiang Gao <gaox@ethz.ch>
//
#ifndef CGAL_MCFSKEL_DETECT_DEGENERACY_H
#define CGAL_MCFSKEL_DETECT_DEGENERACY_H
/// @cond CGAL_DOCUMENT_INTERNAL
/**
* @file Detect_degeneracy.h
* @brief This file contains functions to detect degeneracy at a given vertex.
*
*/
#include <boost/graph/graph_traits.hpp>
#include <boost/foreach.hpp>
#include <CGAL/boost/graph/iterator.h>
#include <cmath>
#include <queue>
namespace CGAL {
namespace internal {
/**
* Test if a given vertex is degenerate.
*
* The approach is to count the Euler characteristics within a small geodesic
* distance at the given vertex. If it is not equal to one, which is the case
* for disk topology, the vertex is considered to be degenerate.
* @param hg the mesh containing the given vertex
* @param root the given vertex
* @param min_edge_length the diameter of the geodesic disk
*/
template<class TriangleMesh, class TriangleMeshPointPMap, class Traits>
bool is_vertex_degenerate(TriangleMesh& hg,
TriangleMeshPointPMap& hg_point_pmap,
typename boost::graph_traits<TriangleMesh>::vertex_descriptor root,
double min_edge_length,
const Traits& traits)
{
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::edge_descriptor edge_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::face_descriptor face_descriptor;
std::set<vertex_descriptor> vertices_in_disk;
std::set<halfedge_descriptor> edges_in_disk;
std::set<face_descriptor> faces_in_disk;
vertices_in_disk.clear();
search_vertices_in_disk(hg, hg_point_pmap, root, vertices_in_disk, min_edge_length, traits);
BOOST_FOREACH(vertex_descriptor vd, vertices_in_disk)
{
BOOST_FOREACH(edge_descriptor ed, out_edges(vd, hg))
{
halfedge_descriptor hd = halfedge(ed, hg);
halfedge_descriptor hd_op = opposite(hd, hg);
vertex_descriptor tgt = target(hd, hg);
if (vertices_in_disk.find(tgt) != vertices_in_disk.end())
{
edges_in_disk.insert(hd);
edges_in_disk.insert(hd_op);
}
bool in = true;
BOOST_FOREACH(halfedge_descriptor hd, halfedges_around_face(hd, hg))
{
vertex_descriptor v = target(hd,hg);
if (vertices_in_disk.find(v) == vertices_in_disk.end())
{
in = false;
break;
}
}
if (in)
{
faces_in_disk.insert(face(hd,hg));
}
}
}
std::size_t V = vertices_in_disk.size();
std::size_t E = edges_in_disk.size() / 2;
std::size_t F = faces_in_disk.size();
std::size_t euler = V + F - E;
if (euler != 1)
{
return true;
}
return false;
}
/**
* Find all the vertices within a geodesic disk.
*
* @param hg the mesh containing the vertices
* @param root the center of the geodesic disk
* @param vertices_in_disk containing the found vertices within the disk
* @param min_edge_length the diameter of the geodesic disk
*/
template<class TriangleMesh, class TriangleMeshPointPMap, class Traits>
void search_vertices_in_disk(TriangleMesh& hg,
TriangleMeshPointPMap& hg_point_pmap,
typename boost::graph_traits<TriangleMesh>::vertex_descriptor root,
std::set<typename boost::graph_traits<TriangleMesh>::vertex_descriptor>& vertices_in_disk,
double min_edge_length,
const Traits& traits)
{
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::edge_descriptor edge_descriptor;
std::queue<vertex_descriptor> Q;
Q.push(root);
vertices_in_disk.insert(root);
double dist_TH = min_edge_length;
while (!Q.empty())
{
vertex_descriptor v = Q.front();
Q.pop();
BOOST_FOREACH(edge_descriptor ed, out_edges(v, hg))
{
vertex_descriptor new_v = target(ed, hg);
if (!vertices_in_disk.count(new_v))
{
double distance = std::sqrt(traits.compute_squared_distance_3_object()(
get(hg_point_pmap, new_v),
get(hg_point_pmap, root)) );
if (distance < dist_TH)
{
Q.push(new_v);
vertices_in_disk.insert(new_v);
}
}
}
}
}
} //namespace internal
} //namespace CGAL
/// @endcond
#endif //CGAL_MCFSKEL_DETECT_DEGENERACY_H

View File

@ -0,0 +1,69 @@
// Copyright (c) 2013 GeometryFactory (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
// 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.
//
// $URL$
// $Id$
//
// Author(s) : Xiang Gao <gaox@ethz.ch>
//
#ifndef CGAL_MCFSKEL_UTILITY_H
#define CGAL_MCFSKEL_UTILITY_H
/// @cond CGAL_DOCUMENT_INTERNAL
/**
* @file Utility.h
* @brief This file contains some helper functions like splitting an edge at a
* given point.
*/
#include <boost/graph/graph_traits.hpp>
#include <boost/foreach.hpp>
#include <cmath>
namespace CGAL {
namespace internal {
template<class TriangleMesh, class TriangleMeshPointPMap, class Traits>
double get_surface_area(TriangleMesh& hg, TriangleMeshPointPMap& hg_point_pmap, const Traits& traits)
{
typedef typename Traits::Point_3 Point;
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::face_descriptor face_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
double total_area = 0;
BOOST_FOREACH(face_descriptor fd, faces(hg))
{
halfedge_descriptor hd = halfedge(fd, hg);
vertex_descriptor v1 = target(hd, hg);
hd = next(hd, hg);
vertex_descriptor v2 = target(hd, hg);
hd = next(hd, hg);
vertex_descriptor v3 = target(hd, hg);
Point p1 = get(hg_point_pmap, v1);
Point p2 = get(hg_point_pmap, v2);
Point p3 = get(hg_point_pmap, v3);
total_area += traits.compute_area_3_object()(p1, p2, p3);
}
return total_area;
}
} //namespace internal
} //namespace CGAL
/// @endcond
#endif //CGAL_MCFSKEL_UTILITY_H

View File

@ -0,0 +1,378 @@
// Copyright (c) 2015 GeometryFactory (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
// 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.
//
// $URL$
// $Id$
//
//
// Author(s) : Sebastien Loriot
#ifndef CGAL_MCS_WEIGHTS_H
#define CGAL_MCS_WEIGHTS_H
/// @cond CGAL_DOCUMENT_INTERNAL
#include <boost/graph/graph_traits.hpp>
#include <cmath>
namespace CGAL {
namespace internal {
struct Vector{
double coords[3];
template<class Traits>
Vector(const typename Traits::Point_3& b, const typename Traits::Point_3& a, const Traits& traits) {
coords[0] = traits.compute_x_3_object()(a) - traits.compute_x_3_object()(b);
coords[1] = traits.compute_y_3_object()(a) - traits.compute_y_3_object()(b);
coords[2] = traits.compute_z_3_object()(a) - traits.compute_z_3_object()(b);
}
double& operator[](int i) { return coords[i]; }
double operator[](int i) const { return coords[i]; }
double squared_length() const {
return coords[0]*coords[0] + coords[1]*coords[1] + coords[2]*coords[2];
}
double length() const {
return std::sqrt(coords[0]*coords[0] + coords[1]*coords[1] + coords[2]*coords[2]);
}
bool normalize() {
double len = length();
if (len < 1e-10)
{
return false;
}
coords[0] /= len;
coords[1] /= len;
coords[2] /= len;
return true;
}
double dot(const Vector& b) {
return coords[0] * b.coords[0] + coords[1] * b.coords[1] + coords[2] * b.coords[2];
}
};
/////////////////////////////////////////////////////////////////////////////////////////
// Returns the cotangent value of half angle v0 v1 v2
template<class TriangleMesh>
class Cotangent_value
{
public:
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
template<class Traits>
double operator()(vertex_descriptor v0, vertex_descriptor v1, vertex_descriptor v2, const Traits& traits)
{
Vector vec0(v2->point(), v1->point(), traits);
Vector vec1(v0->point(), v2->point(), traits);
Vector vec2(v1->point(), v0->point(), traits);
double e0_square = vec0.squared_length();
double e1_square = vec1.squared_length();
double e2_square = vec2.squared_length();
double e0 = std::sqrt(e0_square);
double e2 = std::sqrt(e2_square);
double cos_angle = ( e0_square + e2_square - e1_square ) / 2.0 / e0 / e2;
double sin_angle = std::sqrt(1-cos_angle*cos_angle);
return (cos_angle/sin_angle);
}
};
// Returns the cotangent value of half angle v0 v1 v2
// using formula in -[Meyer02] Discrete Differential-Geometry Operators for- page 19
// The potential problem with previous one (Cotangent_value) is that it does not produce symmetric results
// (i.e. for v0, v1, v2 and v2, v1, v0 returned cot weights can be slightly different)
// This one provides stable results.
template<class TriangleMesh>
class Cotangent_value_Meyer
{
public:
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
template<class Traits>
double operator()(vertex_descriptor v0, vertex_descriptor v1, vertex_descriptor v2, const Traits& traits)
{
Vector a(v1->point(), v0->point(), traits);
Vector b(v1->point(), v2->point(), traits);
double dot_ab = a[0]*b[0] + a[1]*b[1] + a[2]*b[2];
double dot_aa = a.squared_length();
double dot_bb = b.squared_length();
return dot_ab / std::sqrt( dot_aa * dot_bb - dot_ab * dot_ab );
}
};
template<class TriangleMesh>
class Cotangent_value_Meyer_secure
{
public:
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
template<class Traits>
double operator()(vertex_descriptor v0, vertex_descriptor v1, vertex_descriptor v2, const Traits& traits)
{
Vector a(v1->point(), v0->point(), traits);
Vector b(v1->point(), v2->point(), traits);
double dot_ab = a[0]*b[0] + a[1]*b[1] + a[2]*b[2];
double dot_aa = a.squared_length();
double dot_bb = b.squared_length();
double lb = -0.999, ub = 0.999;
double cosine = dot_ab / std::sqrt(dot_aa) / std::sqrt(dot_bb);
cosine = (cosine < lb) ? lb : cosine;
cosine = (cosine > ub) ? ub : cosine;
double sine = std::sqrt(1.0 - cosine * cosine);
return cosine / sine;
}
};
// Returns the cotangent value of half angle v0 v1 v2 by clamping between [1, 89] degrees
// as suggested by -[Friedel] Unconstrained Spherical Parameterization-
template<class TriangleMesh, class CotangentValue = Cotangent_value_Meyer<TriangleMesh> >
class Cotangent_value_clamped : CotangentValue
{
public:
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
template<class Traits>
double operator()(vertex_descriptor v0, vertex_descriptor v1, vertex_descriptor v2, const Traits& traits)
{
const double cot_1 = 57.289962;
const double cot_89 = 0.017455;
double value = CotangentValue::operator()(v0, v1, v2, traits);
return (std::max)(cot_89, (std::min)(value, cot_1));
}
};
template<class TriangleMesh, class CotangentValue = Cotangent_value_Meyer<TriangleMesh> >
class Cotangent_value_minimum_zero : CotangentValue
{
public:
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
template <class Traits>
double operator()(vertex_descriptor v0, vertex_descriptor v1, vertex_descriptor v2, const Traits& traits)
{
double value = CotangentValue::operator()(v0, v1, v2, traits);
return (std::max)(0.0, value);
}
};
// Returns the cotangent value of half angle v0 v1 v2 by dividing the triangle area
// as suggested by -[Mullen08] Spectral Conformal Parameterization-
template<class TriangleMesh,
class CotangentValue = Cotangent_value_Meyer<TriangleMesh> >
class Cotangent_value_area_weighted : CotangentValue
{
public:
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
template <class Traits>
double operator()(vertex_descriptor v0, vertex_descriptor v1, vertex_descriptor v2, const Traits& traits)
{
return CotangentValue::operator()(v0, v1, v2, traits)
/ traits.compute_area_3_object()(v0->point(), v1->point(), v2->point());
}
};
/////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////// Edge Weight Calculators ///////////////////////////////////
// Cotangent weight calculator
// Cotangent_value: as suggested by -[Sorkine07] ARAP Surface Modeling-
// Cotangent_value_area_weighted: as suggested by -[Mullen08] Spectral Conformal Parameterization-
template<class TriangleMesh,
class CotangentValue = Cotangent_value_minimum_zero<TriangleMesh> >
class Cotangent_weight : CotangentValue
{
public:
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
// typedef typename TriangleMesh::Traits::Point_3 Point;
// Returns the cotangent weight of specified halfedge_descriptor
// Edge orientation is trivial
template <class Traits>
double operator()(halfedge_descriptor e, TriangleMesh& hg, const Traits& traits)
{
vertex_descriptor v0 = target(e, hg);
vertex_descriptor v1 = source(e, hg);
// Only one triangle for border edges
if ( is_border(e, hg) ||
is_border( opposite(e, hg), hg) )
{
halfedge_descriptor e_cw = opposite(next(e, hg), hg);
vertex_descriptor v2 = source(e_cw, hg);
if ( is_border(e_cw, hg) ||
is_border( opposite(e_cw, hg), hg) )
{
halfedge_descriptor e_ccw = prev(opposite(e, hg), hg);
v2 = source(e_ccw, hg);
}
return ( CotangentValue::operator()(v0, v2, v1, traits)/2.0 );
}
else
{
halfedge_descriptor e_cw = opposite(next(e, hg), hg);
vertex_descriptor v2 = source(e_cw, hg);
halfedge_descriptor e_ccw = prev(opposite(e, hg), hg);
vertex_descriptor v3 = source(e_ccw, hg);
return ( CotangentValue::operator()(v0, v2, v1, traits)/2.0 + CotangentValue::operator()(v0, v3, v1, traits)/2.0 );
}
}
};
// Single cotangent from -[Chao10] Simple Geometric Model for Elastic Deformation
template<class TriangleMesh,
class CotangentValue = Cotangent_value_Meyer<TriangleMesh> >
class Single_cotangent_weight : CotangentValue
{
public:
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
// typedef typename TriangleMesh::Traits::Point_3 Point;
// Returns the cotangent of the opposite angle of the edge
// 0 for border edges (which does not have an opposite angle)
template <class Traits>
double operator()(halfedge_descriptor e, TriangleMesh& hg, const Traits& traits)
{
if( is_border(e, hg) ) { return 0.0;}
vertex_descriptor v0 = target(e, hg);
vertex_descriptor v1 = source(e, hg);
vertex_descriptor v_op = target(next(e, hg), hg);
return CotangentValue::operator()(v0, v_op, v1, traits);
}
};
// Mean value calculator described in -[Floater04] Mean Value Coordinates-
template<class TriangleMesh>
class Mean_value_weight
{
public:
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
typedef typename TriangleMesh::Traits::Point_3 Point;
// Returns the mean-value coordinate of specified halfedge_descriptor
// Returns different value for different edge orientation (which is a normal behaivour according to formula)
template <class Traits>
double operator()(halfedge_descriptor e, TriangleMesh& hg, const Traits& traits)
{
vertex_descriptor v0 = target(e, hg);
vertex_descriptor v1 = source(e, hg);
Vector vec(v1->point(), v0->point(), traits);
double norm = std::sqrt( vec.squared_length() );
// Only one triangle for border edges
if ( is_border(e, hg) ||
is_border( opposite(e, hg), hg) )
{
halfedge_descriptor e_cw = opposite(next(e, hg), hg);
vertex_descriptor v2 = source(e_cw, hg);
if ( is_border(e_cw, hg) ||
is_border( opposite(e_cw, hg), hg) )
{
halfedge_descriptor e_ccw = prev(opposite(e, hg), hg);
v2 = source(e_ccw, hg);
}
return ( half_tan_value_2(v1, v0, v2, traits)/norm);
}
else
{
halfedge_descriptor e_cw = opposite(next(e, hg), hg);
vertex_descriptor v2 = source(e_cw, hg);
halfedge_descriptor e_ccw = prev(opposite(e, hg), hg);
vertex_descriptor v3 = source(e_ccw, hg);
return ( half_tan_value_2(v1, v0, v2, traits)/norm + half_tan_value_2(v1, v0, v3, traits)/norm);
}
}
private:
// Returns the tangent value of half angle v0_v1_v2/2
template <class Traits>
double half_tan_value(vertex_descriptor v0, vertex_descriptor v1, vertex_descriptor v2, const Traits& traits)
{
Vector vec0(v2->point(), v1->point(), traits);
Vector vec1(v0->point(), v2->point(), traits);
Vector vec2(v1->point(), v0->point(), traits);
double e0_square = vec0.squared_length();
double e1_square = vec1.squared_length();
double e2_square = vec2.squared_length();
double e0 = std::sqrt(e0_square);
double e2 = std::sqrt(e2_square);
double cos_angle = ( e0_square + e2_square - e1_square ) / 2.0 / e0 / e2;
cos_angle = (std::max)(-1.0, (std::min)(1.0, cos_angle)); // clamp into [-1, 1]
double angle = acos(cos_angle);
return ( tan(angle/2.0) );
}
// My deviation built on Meyer_02
template <class Traits>
double half_tan_value_2(vertex_descriptor v0, vertex_descriptor v1, vertex_descriptor v2, const Traits& traits)
{
Vector a(v1->point(), v0->point(), traits);
Vector b(v1->point(), v2->point(), traits);
double dot_ab = a[0]*b[0] + a[1]*b[1] + a[2]*b[2];
double dot_aa = a.squared_length();
double dot_bb = b.squared_length();
double dot_aa_bb = dot_aa * dot_bb;
double cos_rep = dot_ab;
double sin_rep = std::sqrt(dot_aa_bb - dot_ab * dot_ab);
double normalizer = std::sqrt(dot_aa_bb); // |a|*|b|
return (normalizer - cos_rep) / sin_rep; // formula from [Floater04] page 4
// tan(Q/2) = (1 - cos(Q)) / sin(Q)
}
};
template< class TriangleMesh,
class PrimaryWeight = Cotangent_weight<TriangleMesh>,
class SecondaryWeight = Mean_value_weight<TriangleMesh> >
class Hybrid_weight : public PrimaryWeight, SecondaryWeight
{
public:
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
template<class Traits>
double operator()(halfedge_descriptor e, TriangleMesh& hg, const Traits& traits)
{
double weight = PrimaryWeight::operator()(e, hg);
//if(weight < 0) { std::cout << "Negative weight" << std::endl; }
return (weight >= 0) ? weight : SecondaryWeight::operator()(e, hg, traits);
}
};
// Trivial uniform weights (created for test purposes)
template<class TriangleMesh>
class Uniform_weight
{
public:
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
template<class Traits>
double operator()(halfedge_descriptor /*e*/, TriangleMesh& /*hg*/,const Traits&)
{ return 1.0; }
};
}//namespace internal
/// @endcond
}//namespace CGAL
#endif //CGAL_MCS_WEIGHTS_H

View File

@ -0,0 +1,82 @@
// Copyright (c) 2015 GeometryFactory (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
// 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.
//
// $URL$
// $Id$
//
//
// Author(s) : Sebastien Loriot
#ifndef CGAL_MCS_GET_NORMAL_H
#define CGAL_MCS_GET_NORMAL_H
namespace CGAL {
namespace internal {
template <class Traits>
void normalize(typename Traits::Vector_3& v, const Traits& traits)
{
double norm = std::sqrt(traits.compute_squared_length_3_object()(v));
v = traits.construct_divided_vector_3_object()(v, norm);
}
template <class Vertex, class Traits>
typename Traits::Vector_3 get_vertex_normal(
Vertex& v,
const Traits& traits)
{
typedef typename Traits::Point_3 Point;
typedef typename Traits::Vector_3 Vector;
typedef typename Vertex::Halfedge_around_vertex_const_circulator HV_circulator;
Vector normal = traits.construct_vector_3_object()(CGAL::NULL_VECTOR);
HV_circulator he = v.vertex_begin();
HV_circulator end = he;
CGAL_For_all(he,end)
{
if(!he->is_border())
{
const Point& prev = he->prev()->vertex()->point();
const Point& curr = he->vertex()->point();
const Point& next = he->next()->vertex()->point();
Vector p1 = traits.construct_vector_3_object()(curr, next);
normalize(p1, traits);
Vector p2 = traits.construct_vector_3_object()(curr, prev);
normalize(p2, traits);
double cosine = traits.compute_scalar_product_3_object()(p1, p2);
if (cosine < -1.0) cosine = -1.0;
else if (cosine > 1.0) cosine = 1.0;
double angle = acos(cosine);
Vector n = traits.construct_cross_product_vector_3_object()(
traits.construct_vector_3_object()(curr, next),
traits.construct_vector_3_object()(curr, prev) );
normalize(n, traits);
n = traits.construct_scaled_vector_3_object()(n, angle);
normal = traits.construct_sum_of_vectors_3_object()(normal, n);
}
}
normalize(normal, traits);
return normal;
}
} //namespace internal
} //namespace CGAL
#endif // CGAL_MCS_GET_NORMAL_H

View File

@ -0,0 +1 @@
GeometryFactory (France)

View File

@ -0,0 +1 @@
Extraction of a curve skeleton from a triangulated surface mesh

View File

@ -0,0 +1 @@
GPL (v3 or later)

View File

@ -0,0 +1 @@
Sebastien Loriot

View File

@ -0,0 +1,41 @@
# Created by the script cgal_create_cmake_script
# This is the CMake script for compiling a CGAL application.
project( Mean_curvature_skeleton_ )
cmake_minimum_required(VERSION 2.6.2)
if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_GREATER 2.6)
if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}" VERSION_GREATER 2.8.3)
cmake_policy(VERSION 2.8.4)
else()
cmake_policy(VERSION 2.6)
endif()
endif()
find_package(CGAL QUIET COMPONENTS Core )
if ( CGAL_FOUND )
include( ${CGAL_USE_FILE} )
find_package(Eigen3 3.2.0) #(requires 3.2.0 or greater)
if(EIGEN3_FOUND)
include( ${EIGEN3_USE_FILE} )
include( CGAL_CreateSingleSourceCGALProgram )
include_directories (BEFORE "../../include")
create_single_source_cgal_program( "MCF_Skeleton_test.cpp" )
create_single_source_cgal_program( "skeleton_connectivity_test.cpp" )
else()
message(STATUS "These tests require the Eigen library (3.2 or greater), and will not be compiled.")
endif()
else()
message(STATUS "This program requires the CGAL library, and will not be compiled.")
endif()

View File

@ -0,0 +1,166 @@
#include <CGAL/Polyhedron_3.h>
#include <CGAL/Polyhedron_items_with_id_3.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Mean_curvature_flow_skeletonization.h>
#include <CGAL/internal/corefinement/Polyhedron_subset_extraction.h>
#include <CGAL/IO/Polyhedron_iostream.h>
#include <fstream>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point;
typedef Kernel::Vector_3 Vector;
typedef CGAL::Polyhedron_3<Kernel, CGAL::Polyhedron_items_with_id_3> Polyhedron;
typedef CGAL::Mean_curvature_flow_skeletonization<Polyhedron> Mean_curvature_skeleton;
typedef Mean_curvature_skeleton::Skeleton Skeleton;
typedef boost::graph_traits<Skeleton>::vertex_descriptor vertex_desc;
typedef boost::graph_traits<Skeleton>::vertex_iterator vertex_iter;
typedef boost::graph_traits<Skeleton>::edge_iterator edge_iter;
// The input of the skeletonization algorithm must be a pure triangular closed
// mesh and has only one component.
bool is_mesh_valid(Polyhedron& pMesh)
{
if (!pMesh.is_closed())
{
std::cerr << "The mesh is not closed.";
return false;
}
if (!pMesh.is_pure_triangle())
{
std::cerr << "The mesh is not a pure triangle mesh.";
return false;
}
// the algorithm is only applicable on a mesh
// that has only one connected component
std::size_t num_component;
CGAL::Counting_output_iterator output_it(&num_component);
CGAL::internal::extract_connected_components(pMesh, output_it);
++output_it;
if (num_component != 1)
{
std::cerr << "The mesh is not a single closed mesh. It has "
<< num_component << " components.";
return false;
}
return true;
}
template<class T>
bool check_value_equal(T a, T b)
{
if (a != b)
{
std::cerr << "Value not equal! "
<< "line " << __LINE__
<< " file " << __FILE__ << "\n";
return false;
}
return true;
}
int main()
{
Polyhedron mesh;
std::ifstream input("data/elephant.off");
if ( !input || !(input >> mesh) || mesh.empty() ) {
std::cerr << "Cannot open data/elephant.off" << std::endl;
return EXIT_FAILURE;
}
if (!is_mesh_valid(mesh)) {
return EXIT_FAILURE;
}
Skeleton g;
Mean_curvature_skeleton* mcs = new Mean_curvature_skeleton(mesh);
double value;
bool bvalue;
std::size_t ivalue;
double omega_H = 0.2;
mcs->set_quality_speed_tradeoff(omega_H);
value = mcs->quality_speed_tradeoff();
if (!check_value_equal(omega_H, value))
{
return EXIT_FAILURE;
}
double omega_P = 0.3;
mcs->set_medially_centered_speed_tradeoff(omega_P);
value = mcs->medially_centered_speed_tradeoff();
if (!check_value_equal(omega_P, value))
{
return EXIT_FAILURE;
}
double min_edge_length = 0.002;
mcs->set_min_edge_length(min_edge_length);
value = mcs->min_edge_length();
if (!check_value_equal(min_edge_length, value))
{
return EXIT_FAILURE;
}
double delta_area = 0.0005;
mcs->set_area_variation_factor(delta_area);
value = mcs->area_variation_factor();
if (!check_value_equal(delta_area, value))
{
return EXIT_FAILURE;
}
bool is_medially_centered = false;
mcs->set_is_medially_centered(is_medially_centered);
bvalue = mcs->is_medially_centered();
if (!check_value_equal(is_medially_centered, bvalue))
{
return EXIT_FAILURE;
}
std::size_t max_iterations = 200;
mcs->set_max_iterations(max_iterations);
ivalue = mcs->max_iterations();
if (!check_value_equal(max_iterations, ivalue))
{
return EXIT_FAILURE;
}
// Check the following API does not crash.
mcs->contract_geometry();
mcs->remesh();
mcs->collapse_edges();
mcs->split_faces();
mcs->detect_degeneracies();
mcs->contract();
mcs->contract_until_convergence();
mcs->convert_to_skeleton(g);
delete mcs;
mcs = new Mean_curvature_skeleton(mesh);
(*mcs)(g);
delete mcs;
mcs = new Mean_curvature_skeleton(mesh);
mcs->contract_until_convergence();
mcs->convert_to_skeleton(g);
delete mcs;
std::cout << "Pass MCF_Skeleton API test.\n";
return EXIT_SUCCESS;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,134 @@
#include <CGAL/Polyhedron_3.h>
#include <CGAL/Polyhedron_items_with_id_3.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/extract_mean_curvature_flow_skeleton.h>
#include <CGAL/internal/corefinement/Polyhedron_subset_extraction.h>
#include <CGAL/IO/Polyhedron_iostream.h>
#include <boost/foreach.hpp>
#include <fstream>
#include <set>
#include <queue>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point;
typedef Kernel::Vector_3 Vector;
typedef CGAL::Polyhedron_3<Kernel, CGAL::Polyhedron_items_with_id_3> Polyhedron;
typedef boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
typedef CGAL::Mean_curvature_flow_skeletonization<Polyhedron> Mean_curvature_skeleton;
typedef Mean_curvature_skeleton::Skeleton Skeleton;
typedef boost::graph_traits<Skeleton>::vertex_descriptor vertex_desc;
typedef boost::graph_traits<Skeleton>::edge_descriptor edge_desc;
// The input of the skeletonization algorithm must be a pure triangular closed
// mesh and has only one component.
bool is_mesh_valid(Polyhedron& pMesh)
{
if (!pMesh.is_closed())
{
std::cerr << "The mesh is not closed.";
return false;
}
if (!pMesh.is_pure_triangle())
{
std::cerr << "The mesh is not a pure triangle mesh.";
return false;
}
// the algorithm is only applicable on a mesh
// that has only one connected component
std::size_t num_component;
CGAL::Counting_output_iterator output_it(&num_component);
CGAL::internal::extract_connected_components(pMesh, output_it);
++output_it;
if (num_component != 1)
{
std::cerr << "The mesh is not a single closed mesh. It has "
<< num_component << " components.";
return false;
}
return true;
}
int main()
{
Polyhedron mesh;
std::ifstream input("data/elephant.off");
if ( !input || !(input >> mesh) || mesh.empty() ) {
std::cerr << "Cannot open data/elephant.off" << std::endl;
return EXIT_FAILURE;
}
if (!is_mesh_valid(mesh)) {
return EXIT_FAILURE;
}
Skeleton skeleton;
CGAL::extract_mean_curvature_flow_skeleton(mesh, skeleton);
if (num_vertices(skeleton) == 0)
{
std::cerr << "The number of skeletal points is zero!\n";
return EXIT_FAILURE;
}
// check all vertices are seen exactly once
{
std::set<vertex_descriptor> visited;
BOOST_FOREACH(vertex_desc v, vertices(skeleton))
{
BOOST_FOREACH(vertex_descriptor vd, skeleton[v].vertices)
if (!visited.insert(vd).second)
{
std::cerr << "A vertex was seen twice!\n";
return EXIT_FAILURE;
}
}
BOOST_FOREACH(vertex_descriptor vd, vertices(mesh))
{
if (!visited.count(vd))
{
std::cerr << "A vertex was not seen!\n";
return EXIT_FAILURE;
}
}
}
// check the skeleton is connected
{
std::queue<vertex_desc> qu;
std::set<vertex_desc> visited;
qu.push(*vertices(skeleton).first);
visited.insert(qu.back());
while (!qu.empty())
{
vertex_desc cur = qu.front();
qu.pop();
BOOST_FOREACH(edge_desc ed, in_edges(cur, skeleton))
{
vertex_desc next = source(ed, skeleton);
if (visited.insert(next).second)
qu.push(next);
}
}
BOOST_FOREACH(vertex_desc vd, vertices(skeleton))
{
if (!visited.count(vd))
{
std::cerr << "Skeleton curve is not fully connected!\n";
return EXIT_FAILURE;
}
}
}
std::cout << "Pass connectivity test.\n";
return EXIT_SUCCESS;
}