mirror of https://github.com/CGAL/cgal
Merge branch 'gsoc13-CurveSkeleton-new_package-xgao'
Conflicts: Installation/changes.html
This commit is contained in:
commit
e7563d3e05
|
|
@ -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 ;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ Algebraic_kernel_d
|
|||
Kernel_d
|
||||
Point_set_2
|
||||
SearchStructures
|
||||
Surface_mesh_skeletonization
|
||||
Polytope_distance_d
|
||||
Polyhedron
|
||||
Polynomial
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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 -->
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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"
|
||||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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 //
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -6,3 +6,5 @@ Circulator
|
|||
Stream_support
|
||||
Polyhedron
|
||||
Kernel_d
|
||||
Surface_modeling
|
||||
Surface_mesh_skeletonization
|
||||
|
|
|
|||
|
|
@ -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
|
||||
--------------
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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" )
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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 */
|
||||
|
||||
|
|
@ -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 */
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
*/
|
||||
|
||||
|
|
@ -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 */
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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 |
|
|
@ -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()
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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
File diff suppressed because it is too large
Load Diff
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1 @@
|
|||
GeometryFactory (France)
|
||||
|
|
@ -0,0 +1 @@
|
|||
Extraction of a curve skeleton from a triangulated surface mesh
|
||||
|
|
@ -0,0 +1 @@
|
|||
GPL (v3 or later)
|
||||
|
|
@ -0,0 +1 @@
|
|||
Sebastien Loriot
|
||||
|
|
@ -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()
|
||||
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue