Merge PR #6760 into gsoc2023-adaptive_remesh-ipadjen

This commit is contained in:
Ivan Paden 2023-05-21 08:50:10 +02:00
commit eb2a023c31
22 changed files with 2859 additions and 30 deletions

View File

@ -152043,6 +152043,7 @@ pages = {179--189}
Pages = {215--224},
Year = {2012},
Url = {https://monge.univ-mlv.fr/~colinde/pub/09edgewidth.pdf}
}
@inproceedings{tang2009interactive,
title={Interactive Hausdorff distance computation for general polygonal models},
@ -152054,3 +152055,25 @@ pages = {179--189}
year={2009},
organization={ACM}
}
@article{lachaud2020,
author = {Jacques-Olivier Lachaud and Pascal Romon and Boris Thibert and David Coeurjolly},
journal = {Computer Graphics Forum (Proceedings of Symposium on Geometry Processing)},
number = {5},
title = {Interpolated corrected curvature measures for polygonal surfaces},
volume = {39},
month = jul,
year = {2020}
}
@article{lachaud2022
author = {Jacques-Olivier Lachaud and Pascal Romon and Boris Thibert},
journal = {Discrete & Computational Geometry},
title = {Corrected Curvature Measures},
volume = {68},
pages = {477-524},
month = jul,
year = {2022},
url = {https://doi.org/10.1007/s00454-022-00399-4}
}

View File

@ -0,0 +1,54 @@
// Copyright (c) 2016 GeometryFactory SARL (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org)
//
// $URL$
// $Id$
// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial
//
// Author(s) : Andreas Fabri
//
// Warning: this file is generated, see include/CGAL/licence/README.md
#ifndef CGAL_POLYGON_MESH_PROCESSING_INTERPOLATED_CORRECTED_CURVATURES_H
#define CGAL_POLYGON_MESH_PROCESSING_INTERPOLATED_CORRECTED_CURVATURES_H
#include <CGAL/config.h>
#include <CGAL/license.h>
#ifdef CGAL_POLYGON_MESH_PROCESSING_INTERPOLATED_CORRECTED_CURVATURES_COMMERCIAL_LICENSE
# if CGAL_POLYGON_MESH_PROCESSING_INTERPOLATED_CORRECTED_CURVATURES_COMMERCIAL_LICENSE < CGAL_RELEASE_DATE
# if defined(CGAL_LICENSE_WARNING)
CGAL_pragma_warning("Your commercial license for CGAL does not cover "
"this release of the Polygon Mesh Processing - Interpolated Corrected Curvatures package.")
# endif
# ifdef CGAL_LICENSE_ERROR
# error "Your commercial license for CGAL does not cover this release \
of the Polygon Mesh Processing - Interpolated Corrected Curvatures package. \
You get this error, as you defined CGAL_LICENSE_ERROR."
# endif // CGAL_LICENSE_ERROR
# endif // CGAL_POLYGON_MESH_PROCESSING_INTERPOLATED_CORRECTED_CURVATURES_COMMERCIAL_LICENSE < CGAL_RELEASE_DATE
#else // no CGAL_POLYGON_MESH_PROCESSING_INTERPOLATED_CORRECTED_CURVATURES_COMMERCIAL_LICENSE
# if defined(CGAL_LICENSE_WARNING)
CGAL_pragma_warning("\nThe macro CGAL_POLYGON_MESH_PROCESSING_INTERPOLATED_CORRECTED_CURVATURES_COMMERCIAL_LICENSE is not defined."
"\nYou use the CGAL Polygon Mesh Processing - Interpolated Corrected Curvatures package under "
"the terms of the GPLv3+.")
# endif // CGAL_LICENSE_WARNING
# ifdef CGAL_LICENSE_ERROR
# error "The macro CGAL_POLYGON_MESH_PROCESSING_INTERPOLATED_CORRECTED_CURVATURES_COMMERCIAL_LICENSE is not defined.\
You use the CGAL Polygon Mesh Processing - Interpolated Corrected Curvatures package under the terms of \
the GPLv3+. You get this error, as you defined CGAL_LICENSE_ERROR."
# endif // CGAL_LICENSE_ERROR
#endif // no CGAL_POLYGON_MESH_PROCESSING_INTERPOLATED_CORRECTED_CURVATURES_COMMERCIAL_LICENSE
#endif // CGAL_POLYGON_MESH_PROCESSING_INTERPOLATED_CORRECTED_CURVATURES_H

View File

@ -51,6 +51,7 @@ Polygon_mesh_processing/connected_components Polygon Mesh Processing - Connected
Polygon_mesh_processing/corefinement Polygon Mesh Processing - Corefinement
Polygon_mesh_processing/core Polygon Mesh Processing - Core
Polygon_mesh_processing/distance Polygon Mesh Processing - Distance
Polygon_mesh_processing/interpolated_corrected_curvatures Polygon Mesh Processing - Interpolated Corrected Curvatures
Polygon_mesh_processing/measure Polygon Mesh Processing - Geometric Measure
Polygon_mesh_processing/meshing_hole_filling Polygon Mesh Processing - Meshing and Hole Filling
Polygon_mesh_processing/orientation Polygon Mesh Processing - Orientation

View File

@ -23,6 +23,8 @@ EXCLUDE_SYMBOLS += experimental
HTML_EXTRA_FILES = ${CGAL_PACKAGE_DOC_DIR}/fig/selfintersections.jpg \
${CGAL_PACKAGE_DOC_DIR}/fig/mesh_smoothing.png \
${CGAL_PACKAGE_DOC_DIR}/fig/shape_smoothing.png \
${CGAL_PACKAGE_DOC_DIR}/fig/icc_diff_radius.png \
${CGAL_PACKAGE_DOC_DIR}/fig/decimate_cheese.png \
${CGAL_PACKAGE_DOC_DIR}/fig/decimate_colors.png \
${CGAL_PACKAGE_DOC_DIR}/fig/decimate_rg_joint.png

View File

@ -16,6 +16,10 @@
/// Functions to triangulate faces, and to refine and fair regions of a polygon mesh.
/// \ingroup PkgPolygonMeshProcessingRef
/// \defgroup PMP_corrected_curvatures_grp Corrected Curvature Computation
/// Functions to compute the corrected curvatures of a polygon mesh.
/// \ingroup PkgPolygonMeshProcessingRef
/// \defgroup PMP_normal_grp Normal Computation
/// Functions to compute unit normals for individual/all vertices or faces.
/// \ingroup PkgPolygonMeshProcessingRef
@ -75,7 +79,7 @@
\cgalPkgPicture{hole_filling_ico.png}
\cgalPkgSummaryBegin
\cgalPkgAuthors{Sébastien Loriot, Mael Rouxel-Labbé, Jane Tournois, and Ilker %O. Yaz}
\cgalPkgAuthors{David Coeurjolly, Jaques-Olivier Lachaud, Konstantinos Katriopla, Sébastien Loriot, Mael Rouxel-Labbé, Hossam Saeed, Jane Tournois, and Ilker %O. Yaz}
\cgalPkgDesc{This package provides a collection of methods and classes for polygon mesh processing,
ranging from basic operations on simplices, to complex geometry processing algorithms such as
Boolean operations, remeshing, repairing, collision and intersection detection, and more.}
@ -208,6 +212,17 @@ The page \ref bgl_namedparameters "Named Parameters" describes their usage.
- \link PMP_locate_grp Nearest Face Location Queries \endlink
- \link PMP_locate_grp Random Location Generation \endlink
\cgalCRPSection{Corrected Curvatures}
- `CGAL::Polygon_mesh_processing::interpolated_corrected_mean_curvature()`
- `CGAL::Polygon_mesh_processing::interpolated_corrected_Gaussian_curvature()`
- `CGAL::Polygon_mesh_processing::interpolated_corrected_principal_curvatures_and_directions()`
- `CGAL::Polygon_mesh_processing::interpolated_corrected_curvatures()`
- `CGAL::Polygon_mesh_processing::interpolated_corrected_mean_curvature_one_vertex()`
- `CGAL::Polygon_mesh_processing::interpolated_corrected_Gaussian_curvature_one_vertex()`
- `CGAL::Polygon_mesh_processing::interpolated_corrected_principal_curvatures_and_directions_one_vertex()`
- `CGAL::Polygon_mesh_processing::interpolated_corrected_curvatures_one_vertex()`
- `CGAL::Polygon_mesh_processing::Principal_curvatures_and_directions`
\cgalCRPSection{Normal Computation Functions}
- `CGAL::Polygon_mesh_processing::compute_face_normal()`
- `CGAL::Polygon_mesh_processing::compute_face_normals()`

View File

@ -4,7 +4,7 @@ namespace CGAL {
\anchor Chapter_PolygonMeshProcessing
\cgalAutoToc
\authors Sébastien Loriot, Mael Rouxel-Labbé, Jane Tournois, Ilker %O. Yaz
\authors David Coeurjolly, Jaques-Olivier Lachaud, Konstantinos Katriopla, Sébastien Loriot, Mael Rouxel-Labbé, Hossam Saeed, Jane Tournois, and Ilker %O. Yaz
\image html neptun_head.jpg
\image latex neptun_head.jpg
@ -49,6 +49,7 @@ mesh, which includes point location and self intersection tests.
- \ref PMPCombinatorialRepair : repair of polygon meshes and polygon soups.
- \ref PMPGeometricRepair : repair of the geometry of polygon meshes.
- \ref PMPNormalComp : normal computation at vertices and on faces of a polygon mesh.
- \ref PMPICC : computing curvatures (mean, gaussian, principal) on a polygon mesh.
- \ref PMPSlicer : functor able to compute the intersections of a polygon mesh with arbitrary planes (slicer).
- \ref PMPConnectedComponents : methods to deal with connected
components of a polygon mesh (extraction, marks, removal, ...).
@ -935,6 +936,128 @@ not provide storage for the normals.
\cgalExample{Polygon_mesh_processing/compute_normals_example_Polyhedron.cpp}
****************************************
\section PMPICC Computing Curvatures
This package provides methods to compute curvatures on polygonal meshes based on Interpolated
Corrected Curvatures on Polyhedral Surfaces \cgalCite{lachaud2020}. This includes mean curvature,
Gaussian curvature, principal curvatures and directions. These can be computed on triangle meshes,
quad meshes, and meshes with n-gon faces (for n-gons, the centroid must be inside the n-gon face).
The algorithms used prove to work well in general. Also, on meshes with noise on vertex positions,
they give accurate results, on the condition that the correct vertex normals are provided.
\subsection ICCBackground Brief Background
Surface curvatures are quantities that describe the local geometry of a surface. They are important in many
geometry processing applications. As surfaces are 2-dimensional objects (embedded in 3D), they can bend
in 2 independent directions. These directions are called principal directions, and the amount of bending
in each direction is called the principal curvature: \f$ k_1 \f$ and \f$ k_2 \f$ (denoting max and min
curvatures). Curvature is usually expressed as scalar quantities like the mean curvature \f$ H \f$ and
the Gaussian curvature \f$ K \f$ which are defined in terms of the principal curvatures.
The algorithms are based on the two papers \cgalCite{lachaud2022} and \cgalCite{lachaud2020}. They
introduce a new way to compute curvatures on polygonal meshes. The main idea in \cgalCite{lachaud2022} is
based on decoupling the normal information from the position information, which is useful for dealing with
digital surfaces, or meshes with noise on vertex positions. \cgalCite{lachaud2020} introduces some
extensions to this framework. As it uses linear interpolation on the corrected normal vector field
to derive new closed form equations for the corrected curvature measures. These <b>interpolated</b>
curvature measures are the first step for computing the curvatures. For a triangle \f$ \tau_{ijk} \f$,
with vertices \a i, \a j, \a k:
\f[
\begin{align*}
\mu^{(0)}(\tau_{ijk}) = &\frac{1}{2} \langle \bar{\mathbf{u}} \mid (\mathbf{x}_j - \mathbf{x}_i) \times (\mathbf{x}_k - \mathbf{x}_i) \rangle, \\
\mu^{(1)}(\tau_{ijk}) = &\frac{1}{2} \langle \bar{\mathbf{u}} \mid (\mathbf{u}_k - \mathbf{u}_j) \times \mathbf{x}_i + (\mathbf{u}_i - \mathbf{u}_k) \times \mathbf{x}_j + (\mathbf{u}_j - \mathbf{u}_i) \times \mathbf{x}_k \rangle, \\
\mu^{(2)}(\tau_{ijk}) = &\frac{1}{2} \langle \mathbf{u}_i \mid \mathbf{u}_j \times \mathbf{u}_k \rangle, \\
\mu^{\mathbf{X},\mathbf{Y}}(\tau_{ijk}) = & \frac{1}{2} \big\langle \bar{\mathbf{u}} \big| \langle \mathbf{Y} | \mathbf{u}_k -\mathbf{u}_i \rangle \mathbf{X} \times (\mathbf{x}_j - \mathbf{x}_i) \big\rangle
-\frac{1}{2} \big\langle \bar{\mathbf{u}} \big| \langle \mathbf{Y} | \mathbf{u}_j -\mathbf{u}_i \rangle \mathbf{X} \times (\mathbf{x}_k - \mathbf{x}_i) \big\rangle,
\end{align*}
\f]
where \f$ \langle \cdot \mid \cdot \rangle \f$ denotes the usual scalar product,
\f$ \bar{\mathbf{u}}=\frac{1}{3}( \mathbf{u}_i + \mathbf{u}_j + \mathbf{u}_k )\f$.
The first measure \f$ \mu^{(0)} \f$ is the area measure of the triangle, and the measures \f$ \mu^{(1)} \f$ and
\f$ \mu^{(2)} \f$ are the mean and Gaussian corrected curvature measures of the triangle. The last measure
\f$ \mu^{\mathbf{X},\mathbf{Y}} \f$ is the anisotropic corrected curvature measure of the triangle. The
anisotropic measure is later used to compute the principal curvatures and directions through an eigenvalue
solver.
The interpolated curvature measures are then computed for each vertex \f$ v \f$ as the sum of
the curvature measures of the faces in a ball around \f$ v \f$ weighted by the inclusion ratio of the
triangle in the ball. if the ball radius is not specified, the sum is instead over the incident faces
of \f$ v \f$.
To get the final curvature value for a vertex \f$ v \f$, the respective interpolated curvature measure
is divided by the interpolated area measure.
\f[
\mu^{(k)}( B ) = \sum_{\tau : \text{triangle} } \mu^{(k)}( \tau ) \frac{\mathrm{Area}( \tau \cap B )}{\mathrm{Area}(\tau)}.
\f]
\subsection ICCAPI API
The implementation is generic in terms of mesh data structure. It can be used on `Surface_mesh`,
`Polyhedron_3` and other polygonal mesh structures based on the concept `FaceGraph`.
These computations are performed using (on all vertices of the mesh):
- `CGAL::Polygon_mesh_processing::interpolated_corrected_mean_curvature()`
- `CGAL::Polygon_mesh_processing::interpolated_corrected_Gaussian_curvature()`
- `CGAL::Polygon_mesh_processing::interpolated_corrected_principal_curvatures_and_directions()`
- `CGAL::Polygon_mesh_processing::interpolated_corrected_curvatures()`
Where it is recommended to use the last function for computing multiple curvatures (for example: mean and
Gaussian) as the implementation performs the shared computations only once, making it more efficient.
Similarly, we can use the following functions to compute curvatures on a specific vertex:
- `CGAL::Polygon_mesh_processing::interpolated_corrected_mean_curvature_one_vertex()`
- `CGAL::Polygon_mesh_processing::interpolated_corrected_Gaussian_curvature_one_vertex()`
- `CGAL::Polygon_mesh_processing::interpolated_corrected_principal_curvatures_and_directions_one_vertex()`
- `CGAL::Polygon_mesh_processing::interpolated_corrected_curvatures_one_vertex()`
\subsection ICCResults Results & Performance
**To be updated**
\cgalFigureRef{icc_diff_radius} shows how the mean curvature changes depending on
the named parameter `ball_radius`, which can be set to a value > 0 to get a smoother
distribution of values and "diffuses" the extreme values of curvatures across the mesh.
\cgalFigureAnchor{icc_diff_radius}
<center>
<img src="icc_diff_radius.png" style="max-width:85%;"/>
</center>
\cgalFigureCaptionBegin{icc_diff_radius}
The mean curvature on a mesh with different values for the ball radius
parameter: (a) R = 0, (b) R = 0.025, (c) R = 0.05, (d) R = 0.16. Note that the max
edge length is 0.031 and the size of the bounding box of the mesh is 1 x .7 x .8.
\cgalFigureCaptionEnd
\ref BGLPropertyMaps are used to record the computed curvatures as shown in examples. In the following examples, for each property map, we associate
a curvature value to each vertex.
\subsection ICCExampleSM Interpolated Corrected Curvatures on a Surface Mesh Example
The following example illustrates how to
compute the curvatures on vertices
and store them in the property maps provided by the class `Surface_mesh`.
\cgalExample{Polygon_mesh_processing/interpolated_corrected_curvatures_SM.cpp}
\subsection ICCExamplePH Interpolated Corrected Curvatures on a Polyhedron Example
The following example illustrates how to
compute the curvatures on vertices
and store them in dynamic property maps as the class `Polyhedron_3` does
not provide storage for the curvatures.
\cgalExample{Polygon_mesh_processing/interpolated_corrected_curvatures_PH.cpp}
\subsection ICCExampleSV Interpolated Corrected Curvatures on a Vertex Example
The following example illustrates how to
compute the curvatures on a specific vertex
\cgalExample{Polygon_mesh_processing/interpolated_corrected_curvatures_vertex.cpp}
****************************************
\section PMPSlicer Slicer
@ -1143,6 +1266,10 @@ available on 7th of October 2020. It only uses the high level algorithm of chec
is covered by a set of prisms, where each prism is an offset for an input triangle.
That is, the implementation in \cgal does not use indirect predicates.
The interpolated corrected curvatures were implemented during GSoC 2022. This was implemented by Hossam Saeed and under
supervision of David Coeurjolly, Jaques-Olivier Lachaud, and Sébastien Loriot. The implementation is based on \cgalCite{lachaud2020}.
<a href="https://dgtal-team.github.io/doc-nightly/moduleCurvatureMeasures.html">DGtal's implementation</a> was also
used as a reference during the project.
*/
} /* namespace CGAL */

View File

@ -19,6 +19,9 @@
\example Polygon_mesh_processing/refine_fair_example.cpp
\example Polygon_mesh_processing/mesh_slicer_example.cpp
\example Polygon_mesh_processing/isotropic_remeshing_example.cpp
\example Polygon_mesh_processing/interpolated_corrected_curvatures_SM.cpp
\example Polygon_mesh_processing/interpolated_corrected_curvatures_PH.cpp
\example Polygon_mesh_processing/interpolated_corrected_curvatures_vertex.cpp
\example Polygon_mesh_processing/delaunay_remeshing_example.cpp
\example Polygon_mesh_processing/compute_normals_example_Polyhedron.cpp
\example Polygon_mesh_processing/hausdorff_distance_remeshing_example.cpp

View File

@ -74,6 +74,12 @@ if(TARGET CGAL::Eigen3_support)
target_link_libraries(delaunay_remeshing_example PUBLIC CGAL::Eigen3_support)
create_single_source_cgal_program("remesh_almost_planar_patches.cpp")
target_link_libraries(remesh_almost_planar_patches PUBLIC CGAL::Eigen3_support)
create_single_source_cgal_program("interpolated_corrected_curvatures_SM.cpp")
target_link_libraries(interpolated_corrected_curvatures_SM PUBLIC CGAL::Eigen3_support)
create_single_source_cgal_program("interpolated_corrected_curvatures_PH.cpp")
target_link_libraries(interpolated_corrected_curvatures_PH PUBLIC CGAL::Eigen3_support)
create_single_source_cgal_program("interpolated_corrected_curvatures_vertex.cpp")
target_link_libraries(interpolated_corrected_curvatures_vertex PUBLIC CGAL::Eigen3_support)
else()
message(STATUS "NOTICE: Examples that use Eigen will not be compiled.")
endif()

View File

@ -0,0 +1,72 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polygon_mesh_processing/interpolated_corrected_curvatures.h>
#include <CGAL/Polygon_mesh_processing/IO/polygon_mesh_io.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/property_map.h>
#include <boost/graph/graph_traits.hpp>
#include <iostream>
#include <unordered_map>
namespace PMP = CGAL::Polygon_mesh_processing;
typedef CGAL::Exact_predicates_inexact_constructions_kernel Epic_kernel;
typedef CGAL::Polyhedron_3<Epic_kernel> Polyhedron;
typedef boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
int main(int argc, char* argv[])
{
Polyhedron polyhedron;
const std::string filename = (argc > 1) ?
argv[1] :
CGAL::data_file_path("meshes/sphere.off");
if (!CGAL::IO::read_polygon_mesh(filename, polyhedron))
{
std::cerr << "Invalid input file." << std::endl;
return EXIT_FAILURE;
}
boost::property_map<Polyhedron, CGAL::dynamic_vertex_property_t<Epic_kernel::FT>>::type
mean_curvature_map = get(CGAL::dynamic_vertex_property_t<Epic_kernel::FT>(), polyhedron),
Gaussian_curvature_map = get(CGAL::dynamic_vertex_property_t<Epic_kernel::FT>(), polyhedron);
boost::property_map<Polyhedron, CGAL::dynamic_vertex_property_t<PMP::Principal_curvatures_and_directions<Epic_kernel>>>::type
principal_curvatures_and_directions_map =
get(CGAL::dynamic_vertex_property_t<PMP::Principal_curvatures_and_directions<Epic_kernel>>(), polyhedron);
PMP::interpolated_corrected_mean_curvature(polyhedron, mean_curvature_map);
PMP::interpolated_corrected_Gaussian_curvature(polyhedron, Gaussian_curvature_map);
PMP::interpolated_corrected_principal_curvatures_and_directions(polyhedron, principal_curvatures_and_directions_map);
// uncomment this to compute a curvature while specifying named parameters
// Example: an expansion ball radius of 0.5 and a vertex normals map (does not have to depend on positions)
/*std::unordered_map<vertex_descriptor, Epic_Kernel::Vector_3> vnm;
PMP::interpolated_corrected_mean_curvature(
polyhedron,
mean_curvature_map,
CGAL::parameters::ball_radius(0.5).vertex_normal_map(boost::make_assoc_property_map(vnm))
);*/
// This function can be used to compute multiple curvature types by specifiying them as named parameters
// This is more efficient than computing each one separately (shared computations).
PMP::interpolated_corrected_curvatures(
polyhedron,
CGAL::parameters::vertex_mean_curvature_map(mean_curvature_map)
.vertex_principal_curvatures_and_directions_map(principal_curvatures_and_directions_map));
int i = 0;
for (vertex_descriptor v : vertices(polyhedron))
{
auto PC = get(principal_curvatures_and_directions_map, v);
std::cout << i << ": HC = " << get(mean_curvature_map, v)
<< ", GC = " << get(Gaussian_curvature_map, v) << "\n"
<< ", PC = [ " << PC.min_curvature << " , " << PC.max_curvature << " ]\n";
i++;
}
}

View File

@ -0,0 +1,94 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polygon_mesh_processing/interpolated_corrected_curvatures.h>
#include <CGAL/Polygon_mesh_processing/IO/polygon_mesh_io.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/property_map.h>
#include <boost/graph/graph_traits.hpp>
#include <iostream>
namespace PMP = CGAL::Polygon_mesh_processing;
typedef CGAL::Exact_predicates_inexact_constructions_kernel Epic_kernel;
typedef CGAL::Surface_mesh<Epic_kernel::Point_3> Surface_Mesh;
typedef boost::graph_traits<Surface_Mesh>::vertex_descriptor vertex_descriptor;
int main(int argc, char* argv[])
{
Surface_Mesh smesh;
const std::string filename = (argc > 1) ?
argv[1] :
CGAL::data_file_path("meshes/sphere.off");
if (!CGAL::IO::read_polygon_mesh(filename, smesh))
{
std::cerr << "Invalid input file." << std::endl;
return EXIT_FAILURE;
}
// creating and tying surface mesh property maps for curvatures (with defaults = 0)
bool created = false;
Surface_Mesh::Property_map<vertex_descriptor, Epic_kernel::FT>
mean_curvature_map, Gaussian_curvature_map;
boost::tie(mean_curvature_map, created) =
smesh.add_property_map<vertex_descriptor, Epic_kernel::FT>("v:mean_curvature_map", 0);
assert(created);
boost::tie(Gaussian_curvature_map, created) =
smesh.add_property_map<vertex_descriptor, Epic_kernel::FT>("v:Gaussian_curvature_map", 0);
assert(created);
// we use a tuple of 2 scalar values and 2 vectors for principal curvatures and directions
Surface_Mesh::Property_map<vertex_descriptor, PMP::Principal_curvatures_and_directions<Epic_kernel>>
principal_curvatures_and_directions_map;
boost::tie(principal_curvatures_and_directions_map, created) =
smesh.add_property_map<vertex_descriptor, PMP::Principal_curvatures_and_directions<Epic_kernel>>
("v:principal_curvatures_and_directions_map", { 0, 0,
Epic_kernel::Vector_3(0,0,0),
Epic_kernel::Vector_3(0,0,0) });
assert(created);
// user can call these fucntions to compute a specfic curvature type on each vertex.
// (Note: if no ball radius is specified, the measure expansion of each vertex happens by
// summing measures on faces adjacent to each vertex.)
PMP::interpolated_corrected_mean_curvature(smesh, mean_curvature_map);
PMP::interpolated_corrected_Gaussian_curvature(smesh, Gaussian_curvature_map);
PMP::interpolated_corrected_principal_curvatures_and_directions(smesh, principal_curvatures_and_directions_map);
// uncomment this to compute a curvature while specifying named parameters
// Example: an expansion ball radius of 0.5 and a vertex normals map (does not have to depend on positions)
/*Surface_Mesh::Property_map<vertex_descriptor, Epic_kernel::Vector_3> vnm;
boost::tie(vnm, created) = smesh.add_property_map<vertex_descriptor, Epic_kernel::Vector_3>(
"v:vnm", Epic_kernel::Vector_3(0, 0, 0)
);
assert(created);
PMP::interpolated_corrected_mean_curvature(
smesh,
mean_curvature_map,
CGAL::parameters::ball_radius(0.5).vertex_normal_map(vnm)
);*/
// This function can be used to compute multiple curvature types by specifiying them as named parameters
// This is more efficient than computing each one separately (shared computations).
PMP::interpolated_corrected_curvatures(
smesh,
CGAL::parameters::vertex_mean_curvature_map(mean_curvature_map)
.vertex_principal_curvatures_and_directions_map(principal_curvatures_and_directions_map)
);
for (vertex_descriptor v : vertices(smesh))
{
auto PC = principal_curvatures_and_directions_map[v];
std::cout << v.idx() << ": HC = " << mean_curvature_map[v]
<< ", GC = " << Gaussian_curvature_map[v] << "\n"
<< ", PC = [ " << PC.min_curvature << " , " << PC.max_curvature << " ]\n";
}
}

View File

@ -0,0 +1,58 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polygon_mesh_processing/interpolated_corrected_curvatures.h>
#include <CGAL/Polygon_mesh_processing/IO/polygon_mesh_io.h>
#include <CGAL/Surface_mesh.h>
#include <boost/graph/graph_traits.hpp>
#include <iostream>
namespace PMP = CGAL::Polygon_mesh_processing;
typedef CGAL::Exact_predicates_inexact_constructions_kernel Epic_kernel;
typedef CGAL::Surface_mesh<Epic_kernel::Point_3> Surface_Mesh;
typedef boost::graph_traits<Surface_Mesh>::vertex_descriptor vertex_descriptor;
int main(int argc, char* argv[])
{
// instantiating and reading mesh
Surface_Mesh smesh;
const std::string filename = (argc > 1) ?
argv[1] :
CGAL::data_file_path("meshes/sphere.off");
if (!CGAL::IO::read_polygon_mesh(filename, smesh))
{
std::cerr << "Invalid input file." << std::endl;
return EXIT_FAILURE;
}
// loop over vertices and use vertex_descriptor to compute a curvature on one vertex
for (vertex_descriptor v : vertices(smesh))
{
double h = PMP::interpolated_corrected_mean_curvature_one_vertex(smesh, v);
double g = PMP::interpolated_corrected_Gaussian_curvature_one_vertex(smesh, v);
PMP::Principal_curvatures_and_directions<Epic_kernel> p =
PMP::interpolated_corrected_principal_curvatures_and_directions_one_vertex(smesh, v);
// we can also specify a ball radius for expansion and a user defined vertex normals map using
// named parameters. Refer to interpolated_corrected_curvatures_SM.cpp to see example usage.
// Can also use interpolated_corrected_curvatures_one_vertex() to compute multiple curvatures
// on the vertex at the same time. This is more efficient than computing each one separately.
// The following commented lines show this (all mentioned named parameters work on it as well)
// we specify which curvatures we want to compute by passing pointers as named parameters
// as shown. These pointers are used for storing the result as well. in this example we
// selected mean and Gaussian curvatures
// PMP::interpolated_corrected_curvatures_one_vertex(
// smesh,
// v,
// CGAL::parameters::vertex_mean_curvature(&h)
// .vertex_Gaussian_curvature(&g)
// );
std::cout << v.idx() << ": HC = " << h
<< ", GC = " << g << "\n"
<< ", PC = [ " << p.min_curvature << " , " << p.max_curvature << " ]\n";
}
}

View File

@ -80,6 +80,8 @@ if(TARGET CGAL::Eigen3_support)
target_link_libraries(test_shape_smoothing PUBLIC CGAL::Eigen3_support)
create_single_source_cgal_program("delaunay_remeshing_test.cpp")
target_link_libraries(delaunay_remeshing_test PUBLIC CGAL::Eigen3_support)
create_single_source_cgal_program("test_interpolated_corrected_curvatures.cpp")
target_link_libraries(test_interpolated_corrected_curvatures PUBLIC CGAL::Eigen3_support)
create_single_source_cgal_program("test_decimation_of_planar_patches.cpp")
target_link_libraries(test_decimation_of_planar_patches PUBLIC CGAL::Eigen3_support)
else()

View File

@ -0,0 +1,255 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polygon_mesh_processing/interpolated_corrected_curvatures.h>
#include <CGAL/Polygon_mesh_processing/random_perturbation.h>
#include <CGAL/Polygon_mesh_processing/IO/polygon_mesh_io.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/property_map.h>
#include <boost/graph/graph_traits.hpp>
#include <iostream>
#include <unordered_map>
#define ABS_ERROR 1e-6
namespace PMP = CGAL::Polygon_mesh_processing;
typedef CGAL::Exact_predicates_inexact_constructions_kernel Epic_kernel;
typedef CGAL::Surface_mesh<Epic_kernel::Point_3> SMesh;
typedef CGAL::Polyhedron_3<Epic_kernel> Polyhedron;
struct Average_test_info {
Epic_kernel::FT expansion_radius = -1;
Epic_kernel::FT mean_curvature_avg;
Epic_kernel::FT gaussian_curvature_avg;
Epic_kernel::FT principal_curvature_avg;
Epic_kernel::FT tolerance = 0.9;
Average_test_info(
Epic_kernel::FT mean_curvature_avg,
Epic_kernel::FT gaussian_curvature_avg,
Epic_kernel::FT principal_curvature_avg,
Epic_kernel::FT expansion_radius = -1,
Epic_kernel::FT tolerance = 0.9
) :
expansion_radius(expansion_radius),
mean_curvature_avg(mean_curvature_avg),
gaussian_curvature_avg(gaussian_curvature_avg),
principal_curvature_avg(principal_curvature_avg),
tolerance(tolerance)
{
}
};
bool passes_comparison(Epic_kernel::FT result, Epic_kernel::FT expected, Epic_kernel::FT tolerance) {
if (abs(expected) < ABS_ERROR && abs(result) < ABS_ERROR)
return true; // expected 0, got 0
else if (abs(expected) < ABS_ERROR)
return false; // expected 0, got non-0
return (std::min)(result, expected) / (std::max)(result, expected) > tolerance;
}
template <typename PolygonMesh>
void test_average_curvatures(std::string mesh_path,
Average_test_info test_info,
bool compare_single_vertex = false,
int scale_factor_exponent = 0
) {
typedef typename boost::graph_traits<PolygonMesh>::vertex_descriptor vertex_descriptor;
PolygonMesh pmesh;
const std::string filename = CGAL::data_file_path(mesh_path);
if (!CGAL::IO::read_polygon_mesh(filename, pmesh) || faces(pmesh).size() == 0)
{
std::cerr << "Invalid input file." << std::endl;
}
// The following part is used to scale the given mesh and expected curvatures by a constant factor
// this is used to test the stability of the implementation across different scales
if (scale_factor_exponent) {
Epic_kernel::FT factor = pow(10, scale_factor_exponent);
test_info.expansion_radius *= factor;
test_info.mean_curvature_avg /= factor;
test_info.gaussian_curvature_avg /= factor * factor;
test_info.principal_curvature_avg /= factor;
auto vpm = get(CGAL::vertex_point, pmesh);
for (vertex_descriptor vi : vertices(pmesh)) {
Epic_kernel::Point_3 pi = get(vpm, vi);
Epic_kernel::Point_3 pi_new(pi.x() * factor, pi.y() * factor, pi.z() * factor);
put(vpm, vi, pi_new);
}
}
typename boost::property_map<PolygonMesh, CGAL::dynamic_vertex_property_t<Epic_kernel::FT>>::type
mean_curvature_map = get(CGAL::dynamic_vertex_property_t<Epic_kernel::FT>(), pmesh),
gaussian_curvature_map = get(CGAL::dynamic_vertex_property_t<Epic_kernel::FT>(), pmesh);
typename boost::property_map
<PolygonMesh, CGAL::dynamic_vertex_property_t<PMP::Principal_curvatures_and_directions<Epic_kernel>>>::type
principal_curvatures_and_directions_map =
get(CGAL::dynamic_vertex_property_t<PMP::Principal_curvatures_and_directions<Epic_kernel>>(), pmesh);
// test_info.expansion_radius -> test if no radius is provided by user.
if (test_info.expansion_radius < 0) {
PMP::interpolated_corrected_mean_curvature(pmesh, mean_curvature_map);
PMP::interpolated_corrected_Gaussian_curvature(pmesh, gaussian_curvature_map);
PMP::interpolated_corrected_principal_curvatures_and_directions(pmesh, principal_curvatures_and_directions_map);
}
else {
PMP::interpolated_corrected_mean_curvature(
pmesh,
mean_curvature_map,
CGAL::parameters::ball_radius(test_info.expansion_radius)
);
PMP::interpolated_corrected_Gaussian_curvature(
pmesh,
gaussian_curvature_map,
CGAL::parameters::ball_radius(test_info.expansion_radius)
);
PMP::interpolated_corrected_principal_curvatures_and_directions(
pmesh,
principal_curvatures_and_directions_map,
CGAL::parameters::ball_radius(test_info.expansion_radius)
);
}
Epic_kernel::FT mean_curvature_avg = 0, gaussian_curvature_avg = 0, principal_curvature_avg = 0;
for (vertex_descriptor v : vertices(pmesh)) {
mean_curvature_avg += get(mean_curvature_map, v);
gaussian_curvature_avg += get(gaussian_curvature_map, v);
principal_curvature_avg += get(principal_curvatures_and_directions_map, v).min_curvature
+ get(principal_curvatures_and_directions_map, v).max_curvature;
}
mean_curvature_avg /= vertices(pmesh).size();
gaussian_curvature_avg /= vertices(pmesh).size();
principal_curvature_avg /= vertices(pmesh).size() * 2;
// are average curvatures equal to expected?
assert(passes_comparison(mean_curvature_avg, test_info.mean_curvature_avg, test_info.tolerance));
assert(passes_comparison(gaussian_curvature_avg, test_info.gaussian_curvature_avg, test_info.tolerance));
assert(passes_comparison(principal_curvature_avg, test_info.principal_curvature_avg, test_info.tolerance));
// computing curvatures together from interpolated_corrected_curvatures()
PMP::interpolated_corrected_curvatures(
pmesh,
CGAL::parameters::ball_radius(test_info.expansion_radius)
.vertex_mean_curvature_map(mean_curvature_map)
.vertex_Gaussian_curvature_map(gaussian_curvature_map)
.vertex_principal_curvatures_and_directions_map(principal_curvatures_and_directions_map)
);
Epic_kernel::FT new_mean_curvature_avg = 0, new_Gaussian_curvature_avg = 0, new_principal_curvature_avg = 0;
for (vertex_descriptor v : vertices(pmesh)) {
new_mean_curvature_avg += get(mean_curvature_map, v);
new_Gaussian_curvature_avg += get(gaussian_curvature_map, v);
new_principal_curvature_avg += get(principal_curvatures_and_directions_map, v).min_curvature
+ get(principal_curvatures_and_directions_map, v).max_curvature;
}
new_mean_curvature_avg /= vertices(pmesh).size();
new_Gaussian_curvature_avg /= vertices(pmesh).size();
new_principal_curvature_avg /= vertices(pmesh).size() * 2;
// are average curvatures computed from interpolated_corrected_curvatures()
// equal to average curvatures each computed on its own?
assert(passes_comparison(mean_curvature_avg, new_mean_curvature_avg, 0.99));
assert(passes_comparison(gaussian_curvature_avg, new_Gaussian_curvature_avg, 0.99));
assert(passes_comparison(principal_curvature_avg, new_principal_curvature_avg, 0.99));
if (compare_single_vertex) {
// computing curvatures together from interpolated_corrected_curvatures()
Epic_kernel::FT single_vertex_mean_curvature_avg = 0,
single_vertex_Gaussian_curvature_avg = 0,
single_vertex_principal_curvature_avg = 0;
Epic_kernel::FT h, g;
PMP::Principal_curvatures_and_directions<Epic_kernel> p;
for (vertex_descriptor v : vertices(pmesh)) {
PMP::interpolated_corrected_curvatures_one_vertex(
pmesh,
v,
CGAL::parameters::vertex_Gaussian_curvature(std::ref(g))
.vertex_mean_curvature(std::ref(h))
.vertex_principal_curvatures_and_directions(std::ref(p))
.ball_radius(test_info.expansion_radius)
);
single_vertex_mean_curvature_avg += h;
single_vertex_Gaussian_curvature_avg += g;
single_vertex_principal_curvature_avg += p.min_curvature + p.max_curvature;
}
single_vertex_mean_curvature_avg /= vertices(pmesh).size();
single_vertex_Gaussian_curvature_avg /= vertices(pmesh).size();
single_vertex_principal_curvature_avg /= vertices(pmesh).size() * 2;
assert(passes_comparison(mean_curvature_avg, single_vertex_mean_curvature_avg, 0.99));
assert(passes_comparison(gaussian_curvature_avg, single_vertex_Gaussian_curvature_avg, 0.99));
assert(passes_comparison(principal_curvature_avg, single_vertex_principal_curvature_avg, 0.99));
}
}
int main()
{
// testing on a simple sphere(r = 0.5), on both Polyhedron & SurfaceMesh:
// For this mesh, ina addition to the whole mesh functions, we also compare against the single vertex
// curvature functions to make sure the produce the same results
// Expected: Mean Curvature = 2, Gaussian Curvature = 4, Principal Curvatures = 2 & 2 so 2 on avg.
test_average_curvatures<Polyhedron>("meshes/sphere.off", Average_test_info(2, 4, 2), true);
test_average_curvatures<SMesh>("meshes/sphere.off", Average_test_info(2, 4, 2), true);
// Same mesh but with specified expansion radii of 0 and 0.25 (half radius of sphere)
test_average_curvatures<SMesh>("meshes/sphere.off", Average_test_info(2, 4, 2, 0), true);
test_average_curvatures<SMesh>("meshes/sphere.off", Average_test_info(2, 4, 2, 0.25), true);
// testing on a simple sphere(r = 10), on both Polyhedron & SurfaceMesh:
// Expected: Mean Curvature = 0.1, Gaussian Curvature = 0.01, Principal Curvatures = 0.1 & 0.1 so 0.1 on avg.
test_average_curvatures<Polyhedron>("meshes/sphere966.off", Average_test_info(0.1, 0.01, 0.1));
test_average_curvatures<SMesh>("meshes/sphere966.off", Average_test_info(0.1, 0.01, 0.1));
// Same mesh but with specified expansion radii of 0 and 5 (half radius of sphere)
test_average_curvatures<SMesh>("meshes/sphere966.off", Average_test_info(0.1, 0.01, 0.1, 0));
test_average_curvatures<SMesh>("meshes/sphere966.off", Average_test_info(0.1, 0.01, 0.1, 5));
// testing on a simple half cylinder(r = 1), on both Polyhedron & SurfaceMesh:
// Expected: Mean Curvature = 0.5, Gaussian Curvature = 0, Principal Curvatures = 0 & 1 so 0.5 on avg.
test_average_curvatures<Polyhedron>("meshes/cylinder.off", Average_test_info(0.5, 0, 0.5));
test_average_curvatures<SMesh>("meshes/cylinder.off", Average_test_info(0.5, 0, 0.5));
// Same mesh but with specified expansion radii of 0 and 0.5 (half radius of cylinder)
test_average_curvatures<SMesh>("meshes/cylinder.off", Average_test_info(0.5, 0, 0.5, 0));
test_average_curvatures<SMesh>("meshes/cylinder.off", Average_test_info(0.5, 0, 0.5, 0.5));
// Same tests as last one, but with a scaling on the mesh with different values to check for scale stability
test_average_curvatures<SMesh>("meshes/cylinder.off", Average_test_info(0.5, 0, 0.5, 0), false, -6);
test_average_curvatures<SMesh>("meshes/cylinder.off", Average_test_info(0.5, 0, 0.5, 0.5), false, -6);
test_average_curvatures<SMesh>("meshes/cylinder.off", Average_test_info(0.5, 0, 0.5, 0), false, -3);
test_average_curvatures<SMesh>("meshes/cylinder.off", Average_test_info(0.5, 0, 0.5, 0.5), false, -3);
test_average_curvatures<SMesh>("meshes/cylinder.off", Average_test_info(0.5, 0, 0.5, 0), false, -1);
test_average_curvatures<SMesh>("meshes/cylinder.off", Average_test_info(0.5, 0, 0.5, 0.5), false, -1);
test_average_curvatures<SMesh>("meshes/cylinder.off", Average_test_info(0.5, 0, 0.5, 0), false, 1);
test_average_curvatures<SMesh>("meshes/cylinder.off", Average_test_info(0.5, 0, 0.5, 0.5), false, 1);
test_average_curvatures<SMesh>("meshes/cylinder.off", Average_test_info(0.5, 0, 0.5, 0), false, 3);
test_average_curvatures<SMesh>("meshes/cylinder.off", Average_test_info(0.5, 0, 0.5, 0.5), false, 3);
test_average_curvatures<SMesh>("meshes/cylinder.off", Average_test_info(0.5, 0, 0.5, 0), false, 6);
test_average_curvatures<SMesh>("meshes/cylinder.off", Average_test_info(0.5, 0, 0.5, 0.5), false, 6);
}

View File

@ -84,18 +84,18 @@
<property name="title">
<string>Ramp Colors</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QPushButton" name="minColorButton">
<property name="text">
<string>Color Min...</string>
<string>Min</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="maxColorButton">
<property name="text">
<string>Color Max...</string>
<string>Max</string>
</property>
</widget>
</item>
@ -135,7 +135,7 @@
<property name="title">
<string>Zoom </string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QPushButton" name="zoomToMinButton">
<property name="text">
@ -158,7 +158,7 @@
<property name="title">
<string>Ramp Extrema</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="DoubleEdit" name="minBox">
<property name="text">
@ -186,6 +186,32 @@
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QSlider" name="expandingRadiusSlider">
<property name="minimum">
<float>0</float>
</property>
<property name="maximum">
<float>100</float>
</property>
<property name="tracking">
<bool>true</bool>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TicksAbove</enum>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QLabel" name="expandingRadiusLabel">
<property name="text">
<string>Expanding Radius: 0</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>

View File

@ -8,6 +8,7 @@
#include <QColorDialog>
#include <QPalette>
#include <QColor>
#include <QSlider>
#include <QStyleFactory>
#include <QMessageBox>
#include <QAbstractItemView>
@ -15,6 +16,7 @@
#include <CGAL/boost/graph/helpers.h>
#include <CGAL/Polygon_mesh_processing/compute_normal.h>
#include <CGAL/Heat_method_3/Surface_mesh_geodesic_distances_3.h>
#include <CGAL/Polygon_mesh_processing/interpolated_corrected_curvatures.h>
#include "Scene_points_with_normal_item.h"
@ -31,6 +33,7 @@
#include <CGAL/Dynamic_property_map.h>
#include <type_traits>
#include <unordered_map>
#include <vector>
#define ARBITRARY_DBL_MIN 1.0E-30
#define ARBITRARY_DBL_MAX 1.0E+30
@ -41,6 +44,8 @@
typedef CGAL::Three::Triangle_container Tri;
typedef CGAL::Three::Viewer_interface VI;
namespace PMP = CGAL::Polygon_mesh_processing;
class Scene_heat_item
: public CGAL::Three::Scene_item_rendering_helper
{
@ -333,7 +338,10 @@ class DisplayPropertyPlugin :
typedef CGAL::Heat_method_3::Surface_mesh_geodesic_distances_3<SMesh, CGAL::Heat_method_3::Intrinsic_Delaunay> Heat_method_idt;
typedef CGAL::dynamic_vertex_property_t<bool> Vertex_source_tag;
typedef boost::property_map<SMesh, Vertex_source_tag>::type Vertex_source_map;
enum CurvatureType {
MEAN_CURVATURE,
GAUSSIAN_CURVATURE,
};
public:
bool applicable(QAction* action) const Q_DECL_OVERRIDE
@ -482,8 +490,10 @@ public:
connect(scene_obj, SIGNAL(itemIndexSelected(int)),
this,SLOT(detectScalarProperties(int)));
on_propertyBox_currentIndexChanged(0);
connect(dock_widget->expandingRadiusSlider, SIGNAL(valueChanged(int)),
this, SLOT(setExpandingRadius(int)));
on_propertyBox_currentIndexChanged(0);
}
private:
@ -529,6 +539,8 @@ private Q_SLOTS:
dock_widget->propertyBox->addItem("Scaled Jacobian");
dock_widget->propertyBox->addItem("Heat Intensity");
dock_widget->propertyBox->addItem("Heat Intensity (Intrinsic Delaunay)");
dock_widget->propertyBox->addItem("Interpolated Corrected Mean Curvature");
dock_widget->propertyBox->addItem("Interpolated Corrected Gaussian Curvature");
detectSMScalarProperties(sm_item->face_graph());
}
@ -603,6 +615,14 @@ private Q_SLOTS:
return;
sm_item->setRenderingMode(Gouraud);
break;
case 4: // Interpolated Corrected Mean Curvature
displayInterpolatedCurvatureMeasure(sm_item, MEAN_CURVATURE);
sm_item->setRenderingMode(Gouraud);
break;
case 5: // Interpolated Corrected Gaussian Curvature
displayInterpolatedCurvatureMeasure(sm_item, GAUSSIAN_CURVATURE);
sm_item->setRenderingMode(Gouraud);
break;
default:
if(dock_widget->propertyBox->currentText().contains("v:"))
{
@ -637,6 +657,14 @@ private Q_SLOTS:
sm_item->face_graph()->property_map<face_descriptor,double>("f:angle");
if(does_exist)
sm_item->face_graph()->remove_property_map(pmap);
std::tie(pmap, does_exist) =
sm_item->face_graph()->property_map<face_descriptor, double>("v:interpolated_corrected_mean_curvature");
if (does_exist)
sm_item->face_graph()->remove_property_map(pmap);
std::tie(pmap, does_exist) =
sm_item->face_graph()->property_map<face_descriptor, double>("v:interpolated_corrected_Gaussian_curvature");
if (does_exist)
sm_item->face_graph()->remove_property_map(pmap);
});
QApplication::restoreOverrideCursor();
sm_item->invalidateOpenGLBuffers();
@ -668,13 +696,21 @@ private Q_SLOTS:
switch(dock_widget->propertyBox->currentIndex())
{
case 0:
dock_widget->zoomToMinButton->setEnabled(angles_max.count(sm_item)>0 );
dock_widget->zoomToMinButton->setEnabled(angles_min.count(sm_item)>0 );
dock_widget->zoomToMaxButton->setEnabled(angles_max.count(sm_item)>0 );
break;
case 1:
dock_widget->zoomToMinButton->setEnabled(jacobian_max.count(sm_item)>0);
dock_widget->zoomToMinButton->setEnabled(jacobian_min.count(sm_item)>0);
dock_widget->zoomToMaxButton->setEnabled(jacobian_max.count(sm_item)>0);
break;
case 4:
dock_widget->zoomToMinButton->setEnabled(mean_curvature_min.count(sm_item) > 0);
dock_widget->zoomToMaxButton->setEnabled(mean_curvature_max.count(sm_item) > 0);
break;
case 5:
dock_widget->zoomToMinButton->setEnabled(gaussian_curvature_min.count(sm_item) > 0);
dock_widget->zoomToMaxButton->setEnabled(gaussian_curvature_max.count(sm_item) > 0);
break;
default:
break;
}
@ -685,6 +721,10 @@ private Q_SLOTS:
{
Scene_surface_mesh_item* item =
qobject_cast<Scene_surface_mesh_item*>(sender());
maxEdgeLength = -1;
setExpandingRadius(dock_widget->expandingRadiusSlider->value());
if(!item)
return;
SMesh& smesh = *item->face_graph();
@ -701,11 +741,22 @@ private Q_SLOTS:
{
smesh.remove_property_map(angles);
}
SMesh::Property_map<vertex_descriptor, double> mean_curvature;
std::tie(mean_curvature, found) = smesh.property_map<vertex_descriptor, double>("v:interpolated_corrected_mean_curvature");
if (found)
{
smesh.remove_property_map(mean_curvature);
}
SMesh::Property_map<vertex_descriptor, double> gaussian_curvature;
std::tie(gaussian_curvature, found) = smesh.property_map<vertex_descriptor, double>("v:interpolated_corrected_Gaussian_curvature");
if (found)
{
smesh.remove_property_map(gaussian_curvature);
}
}
void displayScaledJacobian(Scene_surface_mesh_item* item)
{
SMesh& smesh = *item->face_graph();
//compute and store the jacobian per face
bool non_init;
@ -742,16 +793,118 @@ private Q_SLOTS:
treat_sm_property<face_descriptor>("f:jacobian", item->face_graph());
}
bool resetScaledJacobian(Scene_surface_mesh_item* item)
void setExpandingRadius(int val_int)
{
SMesh& smesh = *item->face_graph();
if(!smesh.property_map<face_descriptor, double>("f:jacobian").second)
double sliderMin = dock_widget->expandingRadiusSlider->minimum();
double sliderMax = dock_widget->expandingRadiusSlider->maximum() - sliderMin;
double val = val_int - sliderMin;
sliderMin = 0;
SMesh& smesh = *(qobject_cast<Scene_surface_mesh_item*>(scene->item(scene->mainSelectionIndex())))->face_graph();
auto vpm = get(CGAL::vertex_point, smesh);
if (maxEdgeLength < 0)
{
return false;
auto edge_range = CGAL::edges(smesh);
if (edge_range.begin() == edge_range.end())
{
expand_radius = 0;
dock_widget->expandingRadiusLabel->setText(tr("Expanding Radius : %1").arg(expand_radius));
return;
}
auto edge_reference = std::max_element(edge_range.begin(), edge_range.end(), [&, vpm, smesh](auto l, auto r) {
auto res = EPICK().compare_squared_distance_3_object()(
get(vpm, source((l), smesh)),
get(vpm, target((l), smesh)),
get(vpm, source((r), smesh)),
get(vpm, target((r), smesh)));
return res == CGAL::SMALLER;
});
// if edge_reference is not derefrenceble
if (edge_reference == edge_range.end())
{
expand_radius = 0;
dock_widget->expandingRadiusLabel->setText(tr("Expanding Radius : %1").arg(expand_radius));
return;
}
maxEdgeLength = sqrt(
(get(vpm, source((*edge_reference), smesh)) - get(vpm, target((*edge_reference), smesh)))
.squared_length()
);
}
dock_widget->minBox->setValue(jacobian_min[item].first-0.01);
dock_widget->maxBox->setValue(jacobian_max[item].first);
return true;
double outMax = 5 * maxEdgeLength, base = 1.2;
expand_radius = (pow(base, val) - 1) * outMax / (pow(base, sliderMax) - 1);
dock_widget->expandingRadiusLabel->setText(tr("Expanding Radius : %1").arg(expand_radius));
}
void displayInterpolatedCurvatureMeasure(Scene_surface_mesh_item* item, CurvatureType mu_index)
{
if (mu_index != MEAN_CURVATURE && mu_index != GAUSSIAN_CURVATURE) return;
std::string tied_string = (mu_index == MEAN_CURVATURE)?
"v:interpolated_corrected_mean_curvature": "v:interpolated_corrected_Gaussian_curvature";
SMesh& smesh = *item->face_graph();
const auto vnm = smesh.property_map<vertex_descriptor, EPICK::Vector_3>("v:normal_before_perturbation").first;
const bool vnm_exists = smesh.property_map<vertex_descriptor, EPICK::Vector_3>("v:normal_before_perturbation").second;
//compute once and store the value per vertex
bool non_init;
SMesh::Property_map<vertex_descriptor, double> mu_i_map;
std::tie(mu_i_map, non_init) =
smesh.add_property_map<vertex_descriptor, double>(tied_string, 0);
if (non_init)
{
if (vnm_exists) {
if (mu_index == MEAN_CURVATURE)
PMP::interpolated_corrected_mean_curvature(smesh, mu_i_map, CGAL::parameters::ball_radius(expand_radius).vertex_normal_map(vnm));
else
PMP::interpolated_corrected_Gaussian_curvature(smesh, mu_i_map, CGAL::parameters::ball_radius(expand_radius).vertex_normal_map(vnm));
}
else {
if (mu_index == MEAN_CURVATURE)
PMP::interpolated_corrected_mean_curvature(smesh, mu_i_map, CGAL::parameters::ball_radius(expand_radius));
else
PMP::interpolated_corrected_Gaussian_curvature(smesh, mu_i_map, CGAL::parameters::ball_radius(expand_radius));
}
double res_min = ARBITRARY_DBL_MAX,
res_max = -ARBITRARY_DBL_MAX;
SMesh::Vertex_index min_index, max_index;
for (SMesh::Vertex_index v : vertices(smesh))
{
if (mu_i_map[v] > res_max)
{
res_max = mu_i_map[v];
max_index = v;
}
if (mu_i_map[v] < res_min)
{
res_min = mu_i_map[v];
min_index = v;
}
}
if (mu_index == MEAN_CURVATURE){
mean_curvature_max[item] = std::make_pair(res_max, max_index);
mean_curvature_min[item] = std::make_pair(res_min, min_index);
}
else {
gaussian_curvature_max[item] = std::make_pair(res_max, max_index);
gaussian_curvature_min[item] = std::make_pair(res_min, min_index);
}
connect(item, &Scene_surface_mesh_item::itemChanged,
this, &DisplayPropertyPlugin::resetProperty);
}
treat_sm_property<vertex_descriptor>(tied_string, item->face_graph());
}
@ -1054,6 +1207,8 @@ private Q_SLOTS:
break;
}
case 1:
case 4:
case 5:
dock_widget->groupBox-> setEnabled(true);
dock_widget->groupBox_3->setEnabled(true);
@ -1113,6 +1268,26 @@ private Q_SLOTS:
dummy_fd,
dummy_p);
}
break;
case 4:
{
::zoomToId(*item->face_graph(),
QString("v%1").arg(mean_curvature_min[item].second),
getActiveViewer(),
dummy_fd,
dummy_p);
}
break;
case 5:
{
::zoomToId(*item->face_graph(),
QString("v%1").arg(gaussian_curvature_min[item].second),
getActiveViewer(),
dummy_fd,
dummy_p);
}
break;
break;
break;
default:
break;
@ -1146,7 +1321,25 @@ private Q_SLOTS:
dummy_fd,
dummy_p);
}
break;
break;
case 4:
{
::zoomToId(*item->face_graph(),
QString("v%1").arg(mean_curvature_max[item].second),
getActiveViewer(),
dummy_fd,
dummy_p);
}
break;
case 5:
{
::zoomToId(*item->face_graph(),
QString("v%1").arg(gaussian_curvature_max[item].second),
getActiveViewer(),
dummy_fd,
dummy_p);
}
break;
default:
break;
}
@ -1315,10 +1508,10 @@ private:
void displayMapLegend(const std::vector<Value_type>& values)
{
// Create a legend_ and display it
const std::size_t size = (std::min)(color_map.size(), (std::size_t)256);
const std::size_t size = (std::min)(color_map.size(), (std::size_t)2048);
const int text_height = 20;
const int height = text_height*static_cast<int>(size) + text_height;
const int width = 140;
const int width = 170;
const int cell_width = width/3;
const int top_margin = 15;
const int left_margin = 5;
@ -1344,8 +1537,8 @@ private:
tick_height,
color);
QRect text_rect(left_margin + cell_width+10, drawing_height - top_margin - j,
50, text_height);
painter.drawText(text_rect, Qt::AlignCenter, tr("%1").arg(values[i], 0, 'f', 3, QLatin1Char(' ')));
100, text_height);
painter.drawText(text_rect, Qt::AlignCenter, tr("%1").arg(values[i], 0, 'f', 7, QLatin1Char(' ')));
}
if(color_map.size() > size){
QRect text_rect(left_margin + cell_width+10, 0,
@ -1364,7 +1557,7 @@ private:
{
// Create a legend_ and display it
const int height = 256;
const int width = 140;
const int width = 170;
const int cell_width = width/3;
const int top_margin = 5;
const int left_margin = 5;
@ -1408,11 +1601,11 @@ private:
painter.setPen(Qt::blue);
QRect min_text_rect(left_margin + cell_width+10,drawing_height - top_margin,
100, text_height);
painter.drawText(min_text_rect, Qt::AlignCenter, tr("%1").arg(min_value, 0, 'f', 1));
painter.drawText(min_text_rect, Qt::AlignCenter, tr("%1").arg(min_value, 0, 'f', 7));
QRect max_text_rect(left_margin + cell_width+10, drawing_height - top_margin - 200,
100, text_height);
painter.drawText(max_text_rect, Qt::AlignCenter, tr("%1").arg(max_value, 0, 'f', 1));
painter.drawText(max_text_rect, Qt::AlignCenter, tr("%1").arg(max_value, 0, 'f', 7));
dock_widget->legendLabel->setPixmap(legend_);
}
@ -1436,9 +1629,17 @@ private:
std::unordered_map<Scene_surface_mesh_item*, std::pair<double, SMesh::Face_index> > angles_min;
std::unordered_map<Scene_surface_mesh_item*, std::pair<double, SMesh::Face_index> > angles_max;
std::unordered_map<Scene_surface_mesh_item*, std::pair<double, SMesh::Vertex_index> > mean_curvature_min;
std::unordered_map<Scene_surface_mesh_item*, std::pair<double, SMesh::Vertex_index> > mean_curvature_max;
std::unordered_map<Scene_surface_mesh_item*, std::pair<double, SMesh::Vertex_index> > gaussian_curvature_min;
std::unordered_map<Scene_surface_mesh_item*, std::pair<double, SMesh::Vertex_index> > gaussian_curvature_max;
std::unordered_map<Scene_surface_mesh_item*, Vertex_source_map> is_source;
double expand_radius = 0;
double maxEdgeLength = -1;
double minBox;
double maxBox;
QPixmap legend_;
@ -1718,7 +1919,7 @@ private:
}
EPICK::Vector_3 unit_center_normal = CGAL::Polygon_mesh_processing::compute_face_normal(f, mesh);
EPICK::Vector_3 unit_center_normal = PMP::compute_face_normal(f, mesh);
unit_center_normal *= 1.0/CGAL::approximate_sqrt(unit_center_normal.squared_length());
for(std::size_t i = 0; i < corner_areas.size(); ++i)

View File

@ -9,6 +9,20 @@ else()
message(STATUS "NOTICE: Eigen 3.1 (or greater) was not found. Jet fitting plugin will not be available.")
endif()
if(TARGET CGAL::Eigen3_support)
polyhedron_demo_plugin(interpolated_corrected_principal_curvatures_plugin Interpolated_corrected_principal_curvatures_plugin)
target_link_libraries(
interpolated_corrected_principal_curvatures_plugin PUBLIC scene_surface_mesh_item scene_polylines_item
CGAL::Eigen3_support)
else()
message(
STATUS
"NOTICE: Eigen 3.1 (or greater) was not found. Interpolated corrected principal curvatures plugin will not be available."
)
endif()
polyhedron_demo_plugin(extrude_plugin Extrude_plugin KEYWORDS PMP)
target_link_libraries(extrude_plugin PUBLIC scene_surface_mesh_item
scene_selection_item)

View File

@ -0,0 +1,180 @@
#include <CGAL/Three/Polyhedron_demo_plugin_interface.h>
#include <CGAL/property_map.h>
#include <CGAL/Surface_mesh.h>
#include "Scene_surface_mesh_item.h"
#include "Scene_polylines_item.h"
#include <limits>
#include "Scene.h"
#include <QApplication>
#include <QMainWindow>
#include <CGAL/Polygon_mesh_processing/measure.h>
#include <CGAL/Polygon_mesh_processing/interpolated_corrected_curvatures.h>
using namespace CGAL::Three;
class Polyhedron_demo_interpolated_corrected_principal_curvatures_and_directions_plugin :
public QObject,
public Polyhedron_demo_plugin_interface
{
Q_OBJECT
Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface)
Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0")
public:
QList<QAction*> actions() const {
return _actions;
}
void init(QMainWindow* mw,
Scene_interface* scene_interface,
Messages_interface*)
{
scene = scene_interface;
QAction *actionEstimateCurvature = new QAction(tr("Interpolated Corrected Principal Curvatures"), mw);
connect(actionEstimateCurvature, SIGNAL(triggered()), this, SLOT(on_actionEstimateCurvature_triggered()));
_actions <<actionEstimateCurvature;
}
bool applicable(QAction*) const {
return qobject_cast<Scene_surface_mesh_item*>(scene->item(scene->mainSelectionIndex()));
}
public Q_SLOTS:
void on_actionEstimateCurvature_triggered();
private :
Scene_interface *scene;
QList<QAction*> _actions;
}; // end Polyhedron_demo_interpolated_corrected_principal_curvatures_and_directions_plugin
void compute(SMesh* sMesh,
Scene_polylines_item* max_curv,
Scene_polylines_item* min_curv,
Scene_polylines_item* max_negative_curv,
Scene_polylines_item* min_negative_curv)
{
namespace PMP = CGAL::Polygon_mesh_processing;
typedef CGAL::Exact_predicates_inexact_constructions_kernel Epic_kernel;
typedef Epic_kernel::Point_3 Point;
typedef Epic_kernel::Point_3 Point;
typedef Epic_kernel::Vector_3 Vector;
typedef boost::graph_traits<SMesh>::vertex_descriptor Vertex_descriptor;
typename boost::property_map<SMesh, CGAL::vertex_point_t>::type vpmap = get(CGAL::vertex_point, *sMesh);
bool created = false;
SMesh::Property_map<Vertex_descriptor, PMP::Principal_curvatures_and_directions<Epic_kernel>> principal_curvatures_and_directions_map;
boost::tie(principal_curvatures_and_directions_map, created) = sMesh->add_property_map<Vertex_descriptor, PMP::Principal_curvatures_and_directions<Epic_kernel>>
("v:principal_curvatures_and_directions_map", { 0, 0,
Vector(0,0,0),
Vector(0,0,0) });
assert(created);
PMP::interpolated_corrected_principal_curvatures_and_directions(
*sMesh,
principal_curvatures_and_directions_map,
CGAL::parameters::ball_radius(0)
);
double max_curvature_magnitude_on_mesh = 0;
for (Vertex_descriptor v : vertices(*sMesh))
{
const PMP::Principal_curvatures_and_directions<Epic_kernel> pc = principal_curvatures_and_directions_map[v];
max_curvature_magnitude_on_mesh = std::max(max_curvature_magnitude_on_mesh, std::max(abs(pc.min_curvature), abs(pc.max_curvature)));
}
for(Vertex_descriptor v : vertices(*sMesh))
{
std::vector<Point> points;
// pick central point
const Point& central_point = get(vpmap,v);
points.push_back(central_point);
// compute min edge len around central vertex
// to scale the ribbons used to display the directions
const std::size_t n = CGAL::edges(*sMesh).size();
double avg_edge_length = 0;
if (n > 0) {
for (auto e : CGAL::edges(*sMesh))
avg_edge_length += PMP::edge_length(e, *sMesh);
avg_edge_length /= n;
}
const PMP::Principal_curvatures_and_directions<Epic_kernel> pc = principal_curvatures_and_directions_map[v];
Vector umin = (pc.min_curvature / max_curvature_magnitude_on_mesh) * pc.min_direction * avg_edge_length;
Vector umax = (pc.max_curvature / max_curvature_magnitude_on_mesh) * pc.max_direction * avg_edge_length;
Scene_polylines_item::Polyline max_segment(2), min_segment(2);
const double du = 0.4;
min_segment[0] = central_point + du * umin;
min_segment[1] = central_point - du * umin;
max_segment[0] = central_point + du * umax;
max_segment[1] = central_point - du * umax;
(pc.min_curvature > 0 ? min_curv : min_negative_curv)->polylines.push_back(min_segment);
(pc.max_curvature > 0 ? max_curv : max_negative_curv)->polylines.push_back(max_segment);
}
}
void Polyhedron_demo_interpolated_corrected_principal_curvatures_and_directions_plugin::on_actionEstimateCurvature_triggered()
{
// get active polyhedron
const CGAL::Three::Scene_interface::Item_id index = scene->mainSelectionIndex();
QString name = scene->item(index)->name();
Scene_surface_mesh_item* sm_item =
qobject_cast<Scene_surface_mesh_item*>(scene->item(index));
if(! sm_item){
return;
}
// wait cursor
QApplication::setOverrideCursor(Qt::WaitCursor);
// types
Scene_polylines_item* max_curv = new Scene_polylines_item;
max_curv->setColor(Qt::red);
max_curv->setWidth(3);
max_curv->setName(tr("%1 (max curvatures)").arg(name));
Scene_polylines_item* min_curv = new Scene_polylines_item;
min_curv->setColor(QColor(255,210,0));
min_curv->setWidth(4);
min_curv->setName(tr("%1 (min curvatures)").arg(name));
Scene_polylines_item* max_negative_curv = new Scene_polylines_item;
max_negative_curv->setColor(Qt::cyan);
max_negative_curv->setWidth(4);
max_negative_curv->setName(tr("%1 (max negative curvatures)").arg(name));
Scene_polylines_item* min_negative_curv = new Scene_polylines_item;
min_negative_curv->setColor(Qt::blue);
min_negative_curv->setWidth(3);
min_negative_curv->setName(tr("%1 (min negative curvatures)").arg(name));
SMesh* pMesh = sm_item->polyhedron();
compute(pMesh, max_curv, min_curv, max_negative_curv, min_negative_curv);
scene->addItem(max_curv);
scene->addItem(min_curv);
max_curv->invalidateOpenGLBuffers();
min_curv->invalidateOpenGLBuffers();
scene->addItem(max_negative_curv);
scene->addItem(min_negative_curv);
max_negative_curv->invalidateOpenGLBuffers();
min_negative_curv->invalidateOpenGLBuffers();
// default cursor
QApplication::restoreOverrideCursor();
}
#include "Interpolated_corrected_principal_curvatures_plugin.moc"

View File

@ -135,6 +135,26 @@
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="keep_normal_label">
<property name="text">
<string>keep vertex normals unperturbed</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>seed_spinbox</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="keep_normal_checkbox">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="seed_label">
<property name="text">
<string>Random seed</string>
@ -147,7 +167,7 @@
</property>
</widget>
</item>
<item row="1" column="1">
<item row="2" column="1">
<widget class="QSpinBox" name="seed_spinbox">
<property name="minimumSize">
<size>
@ -163,6 +183,8 @@
</layout>
<zorder>seed_spinbox</zorder>
<zorder>seed_label</zorder>
<zorder>keep_normal_label</zorder>
<zorder>keep_normal_checkbox</zorder>
<zorder>deterministic_label</zorder>
<zorder>deterministic_checkbox</zorder>
</widget>

View File

@ -10,6 +10,7 @@
#include <CGAL/utility.h>
#include <CGAL/Polygon_mesh_processing/random_perturbation.h>
#include <CGAL/Polygon_mesh_processing/compute_normal.h>
#include <boost/graph/graph_traits.hpp>
#include <CGAL/property_map.h>
@ -102,6 +103,12 @@ public Q_SLOTS:
if (poly_item)
{
SMesh& pmesh = *poly_item->face_graph();
if(ui.keep_normal_checkbox->isChecked())
{
SMesh::Property_map<vertex_descriptor, EPICK::Vector_3 > vnormals =
pmesh.add_property_map<vertex_descriptor, EPICK::Vector_3 >("v:normal_before_perturbation").first;
CGAL::Polygon_mesh_processing::compute_vertex_normals(pmesh,vnormals);
}
if(ui.deterministic_checkbox->isChecked())
{
unsigned int seed = static_cast<unsigned int>(ui.seed_spinbox->value());

View File

@ -149,6 +149,7 @@ hole_filling_plugin \
hole_filling_sm_plugin \
hole_filling_polyline_plugin \
inside_out_plugin \
interpolated_corrected_principal_curvatures_plugin\
surface_intersection_plugin \
surface_intersection_sm_plugin \
io_image_plugin \

View File

@ -93,6 +93,13 @@ CGAL_add_named_parameter(number_of_points_per_edge_t, number_of_points_per_edge,
CGAL_add_named_parameter(number_of_points_on_edges_t, number_of_points_on_edges, number_of_points_on_edges)
CGAL_add_named_parameter(nb_points_per_area_unit_t, nb_points_per_area_unit, number_of_points_per_area_unit)
CGAL_add_named_parameter(nb_points_per_distance_unit_t, nb_points_per_distance_unit, number_of_points_per_distance_unit)
CGAL_add_named_parameter(vertex_mean_curvature_map_t, vertex_mean_curvature_map, vertex_mean_curvature_map)
CGAL_add_named_parameter(vertex_Gaussian_curvature_map_t, vertex_Gaussian_curvature_map, vertex_Gaussian_curvature_map)
CGAL_add_named_parameter(vertex_principal_curvatures_and_directions_map_t, vertex_principal_curvatures_and_directions_map, vertex_principal_curvatures_and_directions_map)
CGAL_add_named_parameter(vertex_mean_curvature_t, vertex_mean_curvature, vertex_mean_curvature)
CGAL_add_named_parameter(vertex_Gaussian_curvature_t, vertex_Gaussian_curvature, vertex_Gaussian_curvature)
CGAL_add_named_parameter(vertex_principal_curvatures_and_directions_t, vertex_principal_curvatures_and_directions, vertex_principal_curvatures_and_directions)
CGAL_add_named_parameter(ball_radius_t, ball_radius, ball_radius)
CGAL_add_named_parameter(outward_orientation_t, outward_orientation, outward_orientation)
CGAL_add_named_parameter(overlap_test_t, overlap_test, do_overlap_test_of_bounded_sides)
CGAL_add_named_parameter(preserve_genus_t, preserve_genus, preserve_genus)