Merge pull request #6849 from JulyCode/gsoc2022-isosurface
Isosurfacing_3 (new package)
|
|
@ -1,3 +1,4 @@
|
||||||
|
build
|
||||||
/*build*
|
/*build*
|
||||||
/*/*/*/build
|
/*/*/*/build
|
||||||
/*/*/*/VC*
|
/*/*/*/VC*
|
||||||
|
|
|
||||||
|
|
@ -2500,7 +2500,7 @@ public:
|
||||||
double min_dist = -1;
|
double min_dist = -1;
|
||||||
Integer aux_coeffs[6];
|
Integer aux_coeffs[6];
|
||||||
for (int k = 1; k <= 2; ++k) {
|
for (int k = 1; k <= 2; ++k) {
|
||||||
// Get the integer coefficients of the k'th auxiliary conic curve.
|
// Get the integer coefficients of the k-th auxiliary conic curve.
|
||||||
aux_rat_coeffs[0] = (k == 1) ? r_1 : r_2;
|
aux_rat_coeffs[0] = (k == 1) ? r_1 : r_2;
|
||||||
aux_rat_coeffs[1] = (k == 1) ? s_1 : s_2;
|
aux_rat_coeffs[1] = (k == 1) ? s_1 : s_2;
|
||||||
aux_rat_coeffs[2] = (k == 1) ? t_1 : t_2;
|
aux_rat_coeffs[2] = (k == 1) ? t_1 : t_2;
|
||||||
|
|
@ -2516,7 +2516,7 @@ public:
|
||||||
(CGAL::sign(aux_coeffs[2]) == ZERO)) ? 1 : 2;
|
(CGAL::sign(aux_coeffs[2]) == ZERO)) ? 1 : 2;
|
||||||
|
|
||||||
// Compute the \f$x\f$- and \f$y\f$-coordinates of intersection points
|
// Compute the \f$x\f$- and \f$y\f$-coordinates of intersection points
|
||||||
// of the base conic and the k'th auxiliary conic.
|
// of the base conic and the k-th auxiliary conic.
|
||||||
int n_xs = compute_resultant_roots(*nt_traits,
|
int n_xs = compute_resultant_roots(*nt_traits,
|
||||||
base_coeffs[0], base_coeffs[1],
|
base_coeffs[0], base_coeffs[1],
|
||||||
base_coeffs[2],
|
base_coeffs[2],
|
||||||
|
|
|
||||||
|
|
@ -590,7 +590,7 @@ bool _Bezier_cache<NtTraits>::_intersection_params
|
||||||
|
|
||||||
// Construct the bivariate polynomial that corresponds to Equation I.
|
// Construct the bivariate polynomial that corresponds to Equation I.
|
||||||
// Note that we represent a bivariate polynomial as a vector of univariate
|
// Note that we represent a bivariate polynomial as a vector of univariate
|
||||||
// polynomials, whose i'th entry corresponds to the coefficient of t^i,
|
// polynomials, whose i-th entry corresponds to the coefficient of t^i,
|
||||||
// which is in turn a polynomial it s.
|
// which is in turn a polynomial it s.
|
||||||
const int degX_2 = nt_traits.degree (polyX_2);
|
const int degX_2 = nt_traits.degree (polyX_2);
|
||||||
std::vector<Polynomial> coeffsX_st (degX_2 < 0 ? 1 : (degX_2 + 1));
|
std::vector<Polynomial> coeffsX_st (degX_2 < 0 ? 1 : (degX_2 + 1));
|
||||||
|
|
@ -657,7 +657,7 @@ void _Bezier_cache<NtTraits>::_self_intersection_params
|
||||||
|
|
||||||
// Construct the bivariate polynomial that corresponds to Equation I.
|
// Construct the bivariate polynomial that corresponds to Equation I.
|
||||||
// Note that we represent a bivariate polynomial as a vector of univariate
|
// Note that we represent a bivariate polynomial as a vector of univariate
|
||||||
// polynomials, whose i'th entry corresponds to the coefficient of t^i,
|
// polynomials, whose i-th entry corresponds to the coefficient of t^i,
|
||||||
// which is in turn a polynomial it s.
|
// which is in turn a polynomial it s.
|
||||||
const int degX = nt_traits.degree (polyX);
|
const int degX = nt_traits.degree (polyX);
|
||||||
CGAL_assertion(degX > 0);
|
CGAL_assertion(degX > 0);
|
||||||
|
|
@ -771,7 +771,7 @@ _Bezier_cache<NtTraits>::_compute_resultant
|
||||||
if (nt_traits.degree (mat[i][i]) < 0)
|
if (nt_traits.degree (mat[i][i]) < 0)
|
||||||
{
|
{
|
||||||
// If the current diagonal value is a zero polynomial, try to replace
|
// If the current diagonal value is a zero polynomial, try to replace
|
||||||
// the current i'th row with a row with a higher index k, such that
|
// the current i-th row with a row with a higher index k, such that
|
||||||
// mat[k][i] is not a zero polynomial.
|
// mat[k][i] is not a zero polynomial.
|
||||||
|
|
||||||
found_row = false;
|
found_row = false;
|
||||||
|
|
@ -786,7 +786,7 @@ _Bezier_cache<NtTraits>::_compute_resultant
|
||||||
|
|
||||||
if (found_row)
|
if (found_row)
|
||||||
{
|
{
|
||||||
// Swap the i'th and the k'th rows (note that we start from the i'th
|
// Swap the i-th and the k-th rows (note that we start from the i-th
|
||||||
// column, because the first i entries in every row with index i or
|
// column, because the first i entries in every row with index i or
|
||||||
// higher should be zero by now).
|
// higher should be zero by now).
|
||||||
for (j = i; j < dim; j++)
|
for (j = i; j < dim; j++)
|
||||||
|
|
@ -808,7 +808,7 @@ _Bezier_cache<NtTraits>::_compute_resultant
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zero the whole i'th column of the following rows.
|
// Zero the whole i-th column of the following rows.
|
||||||
for (k = i + 1; k < dim; k++)
|
for (k = i + 1; k < dim; k++)
|
||||||
{
|
{
|
||||||
if (nt_traits.degree (mat[k][i]) >= 0)
|
if (nt_traits.degree (mat[k][i]) >= 0)
|
||||||
|
|
@ -821,7 +821,7 @@ _Bezier_cache<NtTraits>::_compute_resultant
|
||||||
mat[k][j] = mat[k][j] * mat[i][i] - mat[i][j] * value;
|
mat[k][j] = mat[k][j] * mat[i][i] - mat[i][j] * value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We multiplied the current row by the i'th diagonal entry, thus
|
// We multiplied the current row by the i-th diagonal entry, thus
|
||||||
// multiplying the determinant value by it. We therefore increment
|
// multiplying the determinant value by it. We therefore increment
|
||||||
// the exponent of mat[i][i] in the normalization factor.
|
// the exponent of mat[i][i] in the normalization factor.
|
||||||
exp_fact[i] = exp_fact[i] + 1;
|
exp_fact[i] = exp_fact[i] + 1;
|
||||||
|
|
|
||||||
|
|
@ -397,7 +397,7 @@ public:
|
||||||
return static_cast<unsigned int>((this->_rep()._ctrl_pts.size()));
|
return static_cast<unsigned int>((this->_rep()._ctrl_pts.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! obtains the i'th control point.
|
/*! obtains the i-th control point.
|
||||||
* \pre i must be between 0 and n - 1, where n is the number of control
|
* \pre i must be between 0 and n - 1, where n is the number of control
|
||||||
* points.
|
* points.
|
||||||
*/
|
*/
|
||||||
|
|
@ -681,7 +681,7 @@ void _Bezier_curve_2_rep<RatKer, AlgKer, NtTrt,
|
||||||
px = pts_begin->x();
|
px = pts_begin->x();
|
||||||
py = pts_begin->y();
|
py = pts_begin->y();
|
||||||
|
|
||||||
// By simplifying (1 - t)^(n-k) we obtain that the k'th expression of
|
// By simplifying (1 - t)^(n-k) we obtain that the k-th expression of
|
||||||
// the above sum is given by:
|
// the above sum is given by:
|
||||||
//
|
//
|
||||||
// n-k
|
// n-k
|
||||||
|
|
|
||||||
|
|
@ -572,7 +572,7 @@ public:
|
||||||
int k;
|
int k;
|
||||||
|
|
||||||
for (k = 1; k <= 2; ++k) {
|
for (k = 1; k <= 2; ++k) {
|
||||||
// Get the integer coefficients of the k'th auxiliary conic curve.
|
// Get the integer coefficients of the k-th auxiliary conic curve.
|
||||||
aux_rat_coeffs[0] = (k == 1) ? r_1 : r_2;
|
aux_rat_coeffs[0] = (k == 1) ? r_1 : r_2;
|
||||||
aux_rat_coeffs[1] = (k == 1) ? s_1 : s_2;
|
aux_rat_coeffs[1] = (k == 1) ? s_1 : s_2;
|
||||||
aux_rat_coeffs[2] = (k == 1) ? t_1 : t_2;
|
aux_rat_coeffs[2] = (k == 1) ? t_1 : t_2;
|
||||||
|
|
@ -593,7 +593,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute the x- and y-coordinates of intersection points of the base
|
// Compute the x- and y-coordinates of intersection points of the base
|
||||||
// conic and the k'th auxiliary conic.
|
// conic and the k-th auxiliary conic.
|
||||||
n_xs = compute_resultant_roots(nt_traits,
|
n_xs = compute_resultant_roots(nt_traits,
|
||||||
base_coeffs[0], base_coeffs[1],
|
base_coeffs[0], base_coeffs[1],
|
||||||
base_coeffs[2],
|
base_coeffs[2],
|
||||||
|
|
|
||||||
|
|
@ -1078,7 +1078,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/*! obtains the i'th order derivative by x of the conic at the point p=(x,y).
|
/*! obtains the i-th order derivative by x of the conic at the point p=(x,y).
|
||||||
* \param p The point where we derive.
|
* \param p The point where we derive.
|
||||||
* \param i The order of the derivatives (either 1, 2 or 3).
|
* \param i The order of the derivatives (either 1, 2 or 3).
|
||||||
* \param slope_numer The numerator of the slope.
|
* \param slope_numer The numerator of the slope.
|
||||||
|
|
@ -1187,7 +1187,7 @@ public:
|
||||||
CGAL_error();
|
CGAL_error();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! obtains the i'th order derivative by y of the conic at the point p=(x,y).
|
/*! obtains the i-th order derivative by y of the conic at the point p=(x,y).
|
||||||
* \param p The point where we derive.
|
* \param p The point where we derive.
|
||||||
* \param i The order of the derivatives (either 1, 2 or 3).
|
* \param i The order of the derivatives (either 1, 2 or 3).
|
||||||
* \param slope_numer The numerator of the slope.
|
* \param slope_numer The numerator of the slope.
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ bisect_control_polygon_2(InputIterator ctrl_pts_begin,
|
||||||
while (last_index > 0)
|
while (last_index > 0)
|
||||||
{
|
{
|
||||||
// Construct (m - 1) control points from the m point we currently have,
|
// Construct (m - 1) control points from the m point we currently have,
|
||||||
// where the new i'th point is the midpoint between p[i] and p[i + 1].
|
// where the new i-th point is the midpoint between p[i] and p[i + 1].
|
||||||
for (i = 0; i < last_index; ++i)
|
for (i = 0; i < last_index; ++i)
|
||||||
vec[i] = midpoint(vec[i], vec[i + 1]);
|
vec[i] = midpoint(vec[i], vec[i + 1]);
|
||||||
|
|
||||||
|
|
@ -134,7 +134,7 @@ typename InputIterator::value_type point_on_Bezier_curve_2
|
||||||
while (last_index > 0)
|
while (last_index > 0)
|
||||||
{
|
{
|
||||||
// Construct (m - 1) control points from the m point we currently have,
|
// Construct (m - 1) control points from the m point we currently have,
|
||||||
// where the new i'th point is given by: (1 - t0)*p[i] + t0*p[i + 1].
|
// where the new i-th point is given by: (1 - t0)*p[i] + t0*p[i + 1].
|
||||||
for (i = 0; i < last_index; ++i)
|
for (i = 0; i < last_index; ++i)
|
||||||
{
|
{
|
||||||
vec[i] = _Point_2(comp_t0*vec[i].x() + t0*vec[i + 1].x(),
|
vec[i] = _Point_2(comp_t0*vec[i].x() + t0*vec[i + 1].x(),
|
||||||
|
|
@ -197,7 +197,7 @@ typename InputIterator::value_type de_Casteljau_2
|
||||||
while (last_index > 0)
|
while (last_index > 0)
|
||||||
{
|
{
|
||||||
// Construct (m - 1) control points from the m point we currently have,
|
// Construct (m - 1) control points from the m point we currently have,
|
||||||
// where the new i'th point is given by: (1 - t0)*p[i] + t0*p[i + 1].
|
// where the new i-th point is given by: (1 - t0)*p[i] + t0*p[i + 1].
|
||||||
for (i = 0; i < last_index; ++i)
|
for (i = 0; i < last_index; ++i)
|
||||||
{
|
{
|
||||||
vec[i] = _Point_2(comp_t0*vec[i].x() + t0*vec[i + 1].x(),
|
vec[i] = _Point_2(comp_t0*vec[i].x() + t0*vec[i + 1].x(),
|
||||||
|
|
|
||||||
|
|
@ -2625,7 +2625,7 @@ protected:
|
||||||
|
|
||||||
if ((! is_vert(cv[0]) && (cmp_x(get_min_v(cv[i]), q) == EQUAL)) ||
|
if ((! is_vert(cv[0]) && (cmp_x(get_min_v(cv[i]), q) == EQUAL)) ||
|
||||||
(is_vert(cv[0]) && equal(get_min_v(cv[i]), q))) {
|
(is_vert(cv[0]) && equal(get_min_v(cv[i]), q))) {
|
||||||
// q is the left endpoint of the i'th subcurve:
|
// q is the left endpoint of the i-th subcurve:
|
||||||
if (to_right) return i;
|
if (to_right) return i;
|
||||||
else {
|
else {
|
||||||
// to_left
|
// to_left
|
||||||
|
|
@ -2642,7 +2642,7 @@ protected:
|
||||||
|
|
||||||
if ((! is_vert(cv[0]) && (cmp_x(get_max_v(cv[i]), q) == EQUAL)) ||
|
if ((! is_vert(cv[0]) && (cmp_x(get_max_v(cv[i]), q) == EQUAL)) ||
|
||||||
(is_vert(cv[0]) && equal(get_max_v(cv[i]), q))) {
|
(is_vert(cv[0]) && equal(get_max_v(cv[i]), q))) {
|
||||||
// q is the right endpoint of the i'th subcurve:
|
// q is the right endpoint of the i-th subcurve:
|
||||||
if (! to_right) return i;
|
if (! to_right) return i;
|
||||||
else {
|
else {
|
||||||
if (direction == SMALLER) {
|
if (direction == SMALLER) {
|
||||||
|
|
|
||||||
|
|
@ -598,15 +598,15 @@ public:
|
||||||
if (dir == SMALLER){
|
if (dir == SMALLER){
|
||||||
// Check whether the split point is xcv[i]'s source or target.
|
// Check whether the split point is xcv[i]'s source or target.
|
||||||
if (equal(max_vertex(xcv[i]), p)) {
|
if (equal(max_vertex(xcv[i]), p)) {
|
||||||
// The entire i'th subcurve belongs to xcv1:
|
// The entire i-th subcurve belongs to xcv1:
|
||||||
xcv1.push_back(xcv[i]);
|
xcv1.push_back(xcv[i]);
|
||||||
}
|
}
|
||||||
else if (equal(min_vertex(xcv[i]), p)) {
|
else if (equal(min_vertex(xcv[i]), p)) {
|
||||||
// The entire i'th subcurves belongs to xcv2:
|
// The entire i-th subcurves belongs to xcv2:
|
||||||
xcv2.push_back(xcv[i]);
|
xcv2.push_back(xcv[i]);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// The i'th subcurve should be split: The left part(seg1)
|
// The i-th subcurve should be split: The left part(seg1)
|
||||||
// goes to xcv1, and the right part(seg2) goes to xcv2.
|
// goes to xcv1, and the right part(seg2) goes to xcv2.
|
||||||
X_monotone_subcurve_2 seg1, seg2;
|
X_monotone_subcurve_2 seg1, seg2;
|
||||||
m_poly_traits.subcurve_traits_2()->split_2_object()(xcv[i], p,
|
m_poly_traits.subcurve_traits_2()->split_2_object()(xcv[i], p,
|
||||||
|
|
|
||||||
|
|
@ -541,7 +541,7 @@ public:
|
||||||
|
|
||||||
for (i = 1; i <= n_roots; i++)
|
for (i = 1; i <= n_roots; i++)
|
||||||
{
|
{
|
||||||
// Get the i'th real-valued root.
|
// Get the i-th real-valued root.
|
||||||
*oi = rootOf(poly, i);
|
*oi = rootOf(poly, i);
|
||||||
++oi;
|
++oi;
|
||||||
}
|
}
|
||||||
|
|
@ -603,7 +603,7 @@ public:
|
||||||
|
|
||||||
for (i = 0; i < root_intervals.size(); i++)
|
for (i = 0; i < root_intervals.size(); i++)
|
||||||
{
|
{
|
||||||
// Get the i'th real-valued root.
|
// Get the i-th real-valued root.
|
||||||
*oi = rootOf(poly, root_intervals[i]);
|
*oi = rootOf(poly, root_intervals[i]);
|
||||||
++oi;
|
++oi;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -197,11 +197,11 @@ public:
|
||||||
void init_subcurve_in_arrangement_flags(size_t n)
|
void init_subcurve_in_arrangement_flags(size_t n)
|
||||||
{ m_isCurveInArr.resize(n, false); }
|
{ m_isCurveInArr.resize(n, false); }
|
||||||
|
|
||||||
/*! checks if the i'th subcurve is in the arrangement. */
|
/*! checks if the i-th subcurve is in the arrangement. */
|
||||||
bool is_subcurve_in_arrangement(unsigned int i) const
|
bool is_subcurve_in_arrangement(unsigned int i) const
|
||||||
{ return (m_isCurveInArr[i]); }
|
{ return (m_isCurveInArr[i]); }
|
||||||
|
|
||||||
/*! sets the flag indicating whether the i'th subcurve is in the arrangement.
|
/*! sets the flag indicating whether the i-th subcurve is in the arrangement.
|
||||||
*/
|
*/
|
||||||
void set_subcurve_in_arrangement(unsigned int i, bool flag)
|
void set_subcurve_in_arrangement(unsigned int i, bool flag)
|
||||||
{ m_isCurveInArr[i] = flag; }
|
{ m_isCurveInArr[i] = flag; }
|
||||||
|
|
|
||||||
|
|
@ -586,7 +586,7 @@ so that they <em>virtually</em> become border edges when exploring a seam mesh w
|
||||||
The input mesh is referred to as <em>underlying</em> mesh of the seam mesh.
|
The input mesh is referred to as <em>underlying</em> mesh of the seam mesh.
|
||||||
We denote `tm` and `sm` the underlying mesh and the seam mesh respectively.
|
We denote `tm` and `sm` the underlying mesh and the seam mesh respectively.
|
||||||
|
|
||||||
Figure \cgalFigureRef{fig_Seam_mesh_1} shows an example of mesh on which two
|
\cgalFigureRef{fig_Seam_mesh_1} shows an example of mesh on which two
|
||||||
edges, defined by the halfedge pairs `h2-h3` and `h6-h7`, are marked as seams.
|
edges, defined by the halfedge pairs `h2-h3` and `h6-h7`, are marked as seams.
|
||||||
The introduction of virtual borders modifies the elementary \bgl graph traversal
|
The introduction of virtual borders modifies the elementary \bgl graph traversal
|
||||||
operations: when we circulate around the target of `h7` in the underlying mesh,
|
operations: when we circulate around the target of `h7` in the underlying mesh,
|
||||||
|
|
@ -600,7 +600,7 @@ A seam mesh with two seam edges `(h2, h3)` and `(h6, h7)`.
|
||||||
\cgalFigureEnd
|
\cgalFigureEnd
|
||||||
|
|
||||||
A vertex of the underlying mesh may correspond to multiple vertices in the seam mesh.
|
A vertex of the underlying mesh may correspond to multiple vertices in the seam mesh.
|
||||||
For example in Figure \cgalFigureRef{fig_Seam_mesh_1}, the target of `h7` corresponds to two
|
For example in \cgalFigureRef{fig_Seam_mesh_1}, the target of `h7` corresponds to two
|
||||||
vertices in the seam mesh, on either side of the virtual border created by the seam edges.
|
vertices in the seam mesh, on either side of the virtual border created by the seam edges.
|
||||||
For this reason, a vertex `v` of the seam mesh is internally represented as a halfedge `h`
|
For this reason, a vertex `v` of the seam mesh is internally represented as a halfedge `h`
|
||||||
of the seam mesh. To obtain a canonical definition, the halfedge `h` is defined as the halfedge
|
of the seam mesh. To obtain a canonical definition, the halfedge `h` is defined as the halfedge
|
||||||
|
|
@ -612,7 +612,7 @@ For vertices `v` in the underlying mesh that are not on a seam edge, we choose
|
||||||
\subsubsection BGLSeamMeshTraversal Seam Mesh Traversal
|
\subsubsection BGLSeamMeshTraversal Seam Mesh Traversal
|
||||||
|
|
||||||
Using the function `next(halfedge_descriptor, FaceGraph)`, we can walk around a face but also around
|
Using the function `next(halfedge_descriptor, FaceGraph)`, we can walk around a face but also around
|
||||||
a border of a mesh. For the seam mesh `sm` from Figure \cgalFigureRef{fig_Seam_mesh_1},
|
a border of a mesh. For the seam mesh `sm` from \cgalFigureRef{fig_Seam_mesh_1},
|
||||||
we have `opposite(h2, sm) == h3*`, and it holds that `face(h3*, sm) == null_face()`.
|
we have `opposite(h2, sm) == h3*`, and it holds that `face(h3*, sm) == null_face()`.
|
||||||
We can walk along this virtual border: starting at `h3*` and repeatedly calling `next(..,sm)`,
|
We can walk along this virtual border: starting at `h3*` and repeatedly calling `next(..,sm)`,
|
||||||
we will traverse `h6*`, `h7*`, `h2*`, before reaching `h3*` again.
|
we will traverse `h6*`, `h7*`, `h2*`, before reaching `h3*` again.
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ project(BGL_OpenMesh_Examples)
|
||||||
# CGAL and its components
|
# CGAL and its components
|
||||||
find_package(CGAL REQUIRED)
|
find_package(CGAL REQUIRED)
|
||||||
|
|
||||||
find_package(OpenMesh)
|
find_package(OpenMesh QUIET)
|
||||||
if(OpenMesh_FOUND)
|
if(OpenMesh_FOUND)
|
||||||
include(CGAL_OpenMesh_support)
|
include(CGAL_OpenMesh_support)
|
||||||
create_single_source_cgal_program("TriMesh.cpp")
|
create_single_source_cgal_program("TriMesh.cpp")
|
||||||
|
|
|
||||||
|
|
@ -382,7 +382,7 @@ int Polynomial<NT>::getTrueDegree() const {
|
||||||
return -1; // Zero polynomial
|
return -1; // Zero polynomial
|
||||||
}
|
}
|
||||||
|
|
||||||
//get i'th Coeff. We check whether i is not greater than the
|
//get i-th Coeff. We check whether i is not greater than the
|
||||||
// true degree and if not then return coeff[i] o/w 0
|
// true degree and if not then return coeff[i] o/w 0
|
||||||
template <class NT>
|
template <class NT>
|
||||||
NT Polynomial<NT>::getCoeffi(int i) const {
|
NT Polynomial<NT>::getCoeffi(int i) const {
|
||||||
|
|
|
||||||
|
|
@ -88,11 +88,10 @@ read_vtk_image_data(vtkImageData* vtk_image, Image_3::Own owning = Image_3::OWN_
|
||||||
// If there is more than a scalar per point, vtk_image->data is not immediately
|
// If there is more than a scalar per point, vtk_image->data is not immediately
|
||||||
// interpretable in Image_3->data
|
// interpretable in Image_3->data
|
||||||
CGAL_assertion(owning == Image_3::OWN_THE_DATA || cn == 1);
|
CGAL_assertion(owning == Image_3::OWN_THE_DATA || cn == 1);
|
||||||
|
CGAL_assertion(vtk_image->GetPointData()->GetScalars()->GetNumberOfTuples() == static_cast<vtkIdType>(image->xdim*image->ydim*image->zdim));
|
||||||
CGAL_assertion(vtk_image->GetPointData()->GetScalars()->GetNumberOfTuples() == dims[0]*dims[1]*dims[2]);
|
|
||||||
|
|
||||||
if(owning == Image_3::OWN_THE_DATA) {
|
if(owning == Image_3::OWN_THE_DATA) {
|
||||||
int dims_n = dims[0]*dims[1]*dims[2];
|
std::size_t dims_n = image->xdim*image->ydim*image->zdim;
|
||||||
image->data = ::ImageIO_alloc(dims_n * image->wdim);
|
image->data = ::ImageIO_alloc(dims_n * image->wdim);
|
||||||
|
|
||||||
// std::cerr << "GetNumberOfTuples() = " << vtk_image->GetPointData()->GetScalars()->GetNumberOfTuples() << "\n"
|
// std::cerr << "GetNumberOfTuples() = " << vtk_image->GetPointData()->GetScalars()->GetNumberOfTuples() << "\n"
|
||||||
|
|
@ -110,7 +109,7 @@ read_vtk_image_data(vtkImageData* vtk_image, Image_3::Own owning = Image_3::OWN_
|
||||||
char* src = static_cast<char*>(vtk_image->GetPointData()->GetScalars()->GetVoidPointer(0));
|
char* src = static_cast<char*>(vtk_image->GetPointData()->GetScalars()->GetVoidPointer(0));
|
||||||
char* dest = static_cast<char*>(image->data);
|
char* dest = static_cast<char*>(image->data);
|
||||||
|
|
||||||
for(int i=0; i<dims_n; ++i)
|
for(std::size_t i=0; i<dims_n; ++i)
|
||||||
{
|
{
|
||||||
// multiply by image->wdim because we casted to char* and not the actual data type
|
// multiply by image->wdim because we casted to char* and not the actual data type
|
||||||
memcpy(dest + image->wdim*i, src + cn*image->wdim*i, image->wdim);
|
memcpy(dest + image->wdim*i, src + cn*image->wdim*i, image->wdim);
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ operator()(const AlgebraicKernelForSpheres::Polynomial_for_spheres_2_3 &p,
|
||||||
OutputIterator res);
|
OutputIterator res);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Computes the `i`th `x`-critical point of polynomial `p`.
|
Computes the `i`-th `x`-critical point of polynomial `p`.
|
||||||
*/
|
*/
|
||||||
template < class OutputIterator >
|
template < class OutputIterator >
|
||||||
AlgebraicKernelForSpheres::Root_for_spheres_2_3
|
AlgebraicKernelForSpheres::Root_for_spheres_2_3
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ operator()(const AlgebraicKernelForSpheres::Polynomial_for_spheres_2_3 &p,
|
||||||
OutputIterator res);
|
OutputIterator res);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Computes the `i`th `y`-critical point of polynomial `p`.
|
Computes the `i`-th `y`-critical point of polynomial `p`.
|
||||||
*/
|
*/
|
||||||
template < class OutputIterator >
|
template < class OutputIterator >
|
||||||
AlgebraicKernelForSpheres::Root_for_spheres_2_3
|
AlgebraicKernelForSpheres::Root_for_spheres_2_3
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ operator()(const AlgebraicKernelForSpheres::Polynomial_for_spheres_2_3 &p,
|
||||||
OutputIterator res);
|
OutputIterator res);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Computes the `i`th `z`-critical point of polynomial `p`.
|
Computes the `i`-th `z`-critical point of polynomial `p`.
|
||||||
*/
|
*/
|
||||||
template < class OutputIterator >
|
template < class OutputIterator >
|
||||||
AlgebraicKernelForSpheres::Root_for_spheres_2_3
|
AlgebraicKernelForSpheres::Root_for_spheres_2_3
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ This component implements the algorithm described in \cgalCite{cgal:lm-clscm-12}
|
||||||
|
|
||||||
\section Classification_Organization Package Organization
|
\section Classification_Organization Package Organization
|
||||||
|
|
||||||
%Classification of data sets is achieved as follows (see Figure \cgalFigureRef{Classification_organization_fig}):
|
%Classification of data sets is achieved as follows (see \cgalFigureRef{Classification_organization_fig}):
|
||||||
|
|
||||||
- some analysis is performed on the input data set;
|
- some analysis is performed on the input data set;
|
||||||
- features are computed based on this analysis;
|
- features are computed based on this analysis;
|
||||||
|
|
@ -314,7 +314,7 @@ Though it is possible to set them up one by one, \cgal also provides a method [t
|
||||||
|
|
||||||
- the same mechanism is repeated until all features' ranges have been tested. Weights are only changed one by one, the other ones are kept to the values that gave the latest best score.
|
- the same mechanism is repeated until all features' ranges have been tested. Weights are only changed one by one, the other ones are kept to the values that gave the latest best score.
|
||||||
|
|
||||||
This usually converges to a satisfying solution (see Figure \cgalFigureRef{Classification_trainer_fig}). The number of trials is user defined, set to 300 by default. Using at least 10 times the number of features is advised (for example, at least 300 iterations if 30 features are used). If the solution is not satisfying, more inliers can be selected, for example, in a region that the user identifies as misclassified with the current configuration. The training algorithm keeps, as initialization, the best weights found at the previous round and carries on trying new weights by taking new inliers into account.
|
This usually converges to a satisfying solution (see \cgalFigureRef{Classification_trainer_fig}). The number of trials is user defined, set to 300 by default. Using at least 10 times the number of features is advised (for example, at least 300 iterations if 30 features are used). If the solution is not satisfying, more inliers can be selected, for example, in a region that the user identifies as misclassified with the current configuration. The training algorithm keeps, as initialization, the best weights found at the previous round and carries on trying new weights by taking new inliers into account.
|
||||||
|
|
||||||
\cgalFigureBegin{Classification_trainer_fig,classif_training.png}
|
\cgalFigureBegin{Classification_trainer_fig,classif_training.png}
|
||||||
Example of evolution of the mean intersection-over-union. The purple curve is the score computed at the current iteration, green curve is the best score found so far.
|
Example of evolution of the mean intersection-over-union. The purple curve is the score computed at the current iteration, green curve is the best score found so far.
|
||||||
|
|
@ -322,7 +322,7 @@ Example of evolution of the mean intersection-over-union. The purple curve is th
|
||||||
|
|
||||||
\subsubsection Classification_sowf_result Result
|
\subsubsection Classification_sowf_result Result
|
||||||
|
|
||||||
Figure \cgalFigureRef{Classification_sowf_result_fig} shows an example of output on a defect-laden point set. The accuracy on this example is 0.97 with a mean intersection-over-union of 0.85 (see section \ref Classification_evaluation).
|
\cgalFigureRef{Classification_sowf_result_fig} shows an example of output on a defect-laden point set. The accuracy on this example is 0.97 with a mean intersection-over-union of 0.85 (see section \ref Classification_evaluation).
|
||||||
|
|
||||||
\cgalFigureBegin{Classification_sowf_result_fig,noise_outliers.png}
|
\cgalFigureBegin{Classification_sowf_result_fig,noise_outliers.png}
|
||||||
Example of classification on a point set with medium noise and outliers (left: input, right: output). _Ground_ is orange, _roofs_ are pink, _vegetation_ is green. Outliers are classified with an additional label _outlier_ in black.
|
Example of classification on a point set with medium noise and outliers (left: input, right: output). _Ground_ is orange, _roofs_ are pink, _vegetation_ is green. Outliers are classified with an additional label _outlier_ in black.
|
||||||
|
|
@ -398,7 +398,7 @@ smoothing by providing a model of `CGAL::Classification::NeighborQuery`.
|
||||||
|
|
||||||
- `CGAL::Classification::classify_with_graphcut()`: this method
|
- `CGAL::Classification::classify_with_graphcut()`: this method
|
||||||
offers the best quality but requires longer computation time (see
|
offers the best quality but requires longer computation time (see
|
||||||
Figure \cgalFigureRef{Classification_image}, bottom-right). The
|
\cgalFigureRef{Classification_image}, bottom-right). The
|
||||||
total energy that is minimized is the sum of the partial data term
|
total energy that is minimized is the sum of the partial data term
|
||||||
\f$E_{di}(x_i)\f$ and of a pairwise interaction energy defined by the
|
\f$E_{di}(x_i)\f$ and of a pairwise interaction energy defined by the
|
||||||
standard Potts model \cgalCite{cgal:l-mrfmi-09} :
|
standard Potts model \cgalCite{cgal:l-mrfmi-09} :
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,7 @@ namespace CGAL
|
||||||
};
|
};
|
||||||
|
|
||||||
//count the number of time a given type have been found
|
//count the number of time a given type have been found
|
||||||
//within a tuple, until reaching position the k'th type of the tuple.
|
//within a tuple, until reaching position the k-th type of the tuple.
|
||||||
//dim is the total size of the tuple
|
//dim is the total size of the tuple
|
||||||
template <class Type,int k,class T,
|
template <class Type,int k,class T,
|
||||||
int dim=CGAL::internal::My_length<T>::value-1>
|
int dim=CGAL::internal::My_length<T>::value-1>
|
||||||
|
|
@ -191,7 +191,7 @@ namespace CGAL
|
||||||
};
|
};
|
||||||
|
|
||||||
//count the number of time a type different from Type have been found
|
//count the number of time a type different from Type have been found
|
||||||
//within a tuple, until reaching position the k'th type of the tuple.
|
//within a tuple, until reaching position the k-th type of the tuple.
|
||||||
//dim is the total size of the tuple
|
//dim is the total size of the tuple
|
||||||
template <class Type, int k,class T,
|
template <class Type, int k,class T,
|
||||||
int dim=CGAL::internal::My_length<T>::value-1>
|
int dim=CGAL::internal::My_length<T>::value-1>
|
||||||
|
|
@ -339,7 +339,7 @@ namespace CGAL
|
||||||
};
|
};
|
||||||
|
|
||||||
//Same as Foreach_static excepted that Functor
|
//Same as Foreach_static excepted that Functor
|
||||||
//is called for case k only if the k'th type in the tuple
|
//is called for case k only if the k-th type in the tuple
|
||||||
//is different from Void. Note that to the converse of Foreach_static
|
//is different from Void. Note that to the converse of Foreach_static
|
||||||
//Functor are called from n =0 to k
|
//Functor are called from n =0 to k
|
||||||
template <class Functor,class T,int n=0, int startn=0>
|
template <class Functor,class T,int n=0, int startn=0>
|
||||||
|
|
@ -365,7 +365,7 @@ namespace CGAL
|
||||||
};
|
};
|
||||||
|
|
||||||
//Same as Foreach_static_restricted excepted that Functor
|
//Same as Foreach_static_restricted excepted that Functor
|
||||||
//is called for case k only if the k'th type in the tuple
|
//is called for case k only if the k-th type in the tuple
|
||||||
//is different from Void and k!=j.
|
//is different from Void and k!=j.
|
||||||
template <class Functor,int j,class T,int n=0>
|
template <class Functor,int j,class T,int n=0>
|
||||||
struct Foreach_static_restricted_except;
|
struct Foreach_static_restricted_except;
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,7 @@
|
||||||
\package_listing{Skin_surface_3}
|
\package_listing{Skin_surface_3}
|
||||||
\package_listing{SMDS_3}
|
\package_listing{SMDS_3}
|
||||||
\package_listing{Mesh_3}
|
\package_listing{Mesh_3}
|
||||||
|
\package_listing{Isosurfacing_3}
|
||||||
\package_listing{Tetrahedral_remeshing}
|
\package_listing{Tetrahedral_remeshing}
|
||||||
\package_listing{Periodic_3_mesh_3}
|
\package_listing{Periodic_3_mesh_3}
|
||||||
\package_listing{Alpha_wrap_3}
|
\package_listing{Alpha_wrap_3}
|
||||||
|
|
|
||||||
|
|
@ -401,6 +401,12 @@ Boissonnat}
|
||||||
organization={ACM}
|
organization={ACM}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@techreport{ cgal:c-mcctci-95,
|
||||||
|
title={Marching Cubes 33: Construction of Topologically Correct Isosurfaces},
|
||||||
|
author={Chernyaev, Evgeni},
|
||||||
|
year={1995}
|
||||||
|
}
|
||||||
|
|
||||||
@inproceedings{cgal::c-mssbo-04,
|
@inproceedings{cgal::c-mssbo-04,
|
||||||
author={Chen, L.},
|
author={Chen, L.},
|
||||||
title={{Mesh Smoothing Schemes based on Optimal Delaunay Triangulations}},
|
title={{Mesh Smoothing Schemes based on Optimal Delaunay Triangulations}},
|
||||||
|
|
@ -657,6 +663,17 @@ note="Conference version: Symp. on Geometry Processing 2003"
|
||||||
, year = 2009
|
, year = 2009
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@article{cgal:dljjaw-sisp-15,
|
||||||
|
title={A survey on Implicit Surface Polygonization},
|
||||||
|
author={De Ara{\'u}jo, Bruno Rodrigues and Lopes, Daniel S and Jepp, Pauline and Jorge, Joaquim A and Wyvill, Brian},
|
||||||
|
journal={ACM Computing Surveys (CSUR)},
|
||||||
|
volume={47},
|
||||||
|
number={4},
|
||||||
|
pages={1--39},
|
||||||
|
year={2015},
|
||||||
|
publisher={ACM New York, NY, USA}
|
||||||
|
}
|
||||||
|
|
||||||
@article{cgal:dfg-cvtaa-99t,
|
@article{cgal:dfg-cvtaa-99t,
|
||||||
title={{Centroidal Voronoi Tessellations: Applications and Algorithms}},
|
title={{Centroidal Voronoi Tessellations: Applications and Algorithms}},
|
||||||
author={Du, Q. and Faber, V. and Gunzburger, M.},
|
author={Du, Q. and Faber, V. and Gunzburger, M.},
|
||||||
|
|
@ -950,6 +967,17 @@ Teillaud"
|
||||||
pages = "19--27",
|
pages = "19--27",
|
||||||
year = "2003"}
|
year = "2003"}
|
||||||
|
|
||||||
|
@inproceedings{ cgal:g-ctcmi-16,
|
||||||
|
title={Construction of Topologically Correct and Manifold Isosurfaces},
|
||||||
|
author={Grosso, Roberto},
|
||||||
|
booktitle={Computer Graphics Forum},
|
||||||
|
volume={35},
|
||||||
|
number={5},
|
||||||
|
pages={187--196},
|
||||||
|
year={2016},
|
||||||
|
organization={Wiley Online Library}
|
||||||
|
}
|
||||||
|
|
||||||
@inproceedings{ cgal:g-frseb-99
|
@inproceedings{ cgal:g-frseb-99
|
||||||
,author = "B. G{\"a}rtner"
|
,author = "B. G{\"a}rtner"
|
||||||
,title = "Fast and robust smallest enclosing balls"
|
,title = "Fast and robust smallest enclosing balls"
|
||||||
|
|
@ -1289,6 +1317,14 @@ Teillaud"
|
||||||
,update = "01.06 hoffmann"
|
,update = "01.06 hoffmann"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@inproceedings{ cgal:jlsw-dchd-02,
|
||||||
|
title={Dual contouring of Hermite Data},
|
||||||
|
author={Ju, Tao and Losasso, Frank and Schaefer, Scott and Warren, Joe},
|
||||||
|
booktitle={Proceedings of the 29th annual conference on Computer graphics and interactive techniques},
|
||||||
|
pages={339--346},
|
||||||
|
year={2002}
|
||||||
|
}
|
||||||
|
|
||||||
@incollection{ cgal:k-dat-96
|
@incollection{ cgal:k-dat-96
|
||||||
,author = "Keffer, T."
|
,author = "Keffer, T."
|
||||||
,title = "The Design and Architecture of {T}ools.h{\tt ++}"
|
,title = "The Design and Architecture of {T}ools.h{\tt ++}"
|
||||||
|
|
|
||||||
|
|
@ -1134,7 +1134,7 @@ CGAL_Kernel_obj(Point_3)
|
||||||
return Object();
|
return Object();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This functor selects the i'th element in a vector of T2's
|
// This functor selects the i-th element in a vector of T2's
|
||||||
template <typename T2>
|
template <typename T2>
|
||||||
struct Ith_for_intersection {
|
struct Ith_for_intersection {
|
||||||
typedef T2 result_type;
|
typedef T2 result_type;
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,13 @@
|
||||||
the Delaunay flip algorithm and the construction of a portion of the lift of the triangulation
|
the Delaunay flip algorithm and the construction of a portion of the lift of the triangulation
|
||||||
in the Poincaré disk. A method is offered that generates such domains in genus two.
|
in the Poincaré disk. A method is offered that generates such domains in genus two.
|
||||||
|
|
||||||
|
### 3D Isosurfacing (new package)
|
||||||
|
|
||||||
|
- This package provides algorithms to extract isosurfaces from different inputs. The input is represented
|
||||||
|
as a 3D domain and can be an implicit function or a Cartesian grid. The output is an indexed face
|
||||||
|
set that stores an isosurface in the form of a surface mesh. The provided algorithms include Marching Cubes,
|
||||||
|
topologically correct Marching Cubes, and Dual Contouring.
|
||||||
|
|
||||||
### [Polygon Mesh Processing](https://doc.cgal.org/6.1/Manual/packages.html#PkgPolygonMeshProcessing)
|
### [Polygon Mesh Processing](https://doc.cgal.org/6.1/Manual/packages.html#PkgPolygonMeshProcessing)
|
||||||
- Added the function `CGAL::Polygon_mesh_processing::discrete_mean_curvature` and `CGAL::Polygon_mesh_processing::discrete_Guassian_curvature` to evaluate the discrete curvature at a vertex of a mesh.
|
- Added the function `CGAL::Polygon_mesh_processing::discrete_mean_curvature` and `CGAL::Polygon_mesh_processing::discrete_Guassian_curvature` to evaluate the discrete curvature at a vertex of a mesh.
|
||||||
- Added the function `CGAL::Polygon_mesh_processing::angle_sum` to compute the sum of the angles around a vertex.
|
- Added the function `CGAL::Polygon_mesh_processing::angle_sum` to compute the sum of the angles around a vertex.
|
||||||
|
|
|
||||||
|
|
@ -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_LICENSE_ISOSURFACING_3_H
|
||||||
|
#define CGAL_LICENSE_ISOSURFACING_3_H
|
||||||
|
|
||||||
|
#include <CGAL/config.h>
|
||||||
|
#include <CGAL/license.h>
|
||||||
|
|
||||||
|
#ifdef CGAL_ISOSURFACING_3_COMMERCIAL_LICENSE
|
||||||
|
|
||||||
|
# if CGAL_ISOSURFACING_3_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 3D Isosurfacing package.")
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifdef CGAL_LICENSE_ERROR
|
||||||
|
# error "Your commercial license for CGAL does not cover this release \
|
||||||
|
of the 3D Isosurfacing package. \
|
||||||
|
You get this error, as you defined CGAL_LICENSE_ERROR."
|
||||||
|
# endif // CGAL_LICENSE_ERROR
|
||||||
|
|
||||||
|
# endif // CGAL_ISOSURFACING_3_COMMERCIAL_LICENSE < CGAL_RELEASE_DATE
|
||||||
|
|
||||||
|
#else // no CGAL_ISOSURFACING_3_COMMERCIAL_LICENSE
|
||||||
|
|
||||||
|
# if defined(CGAL_LICENSE_WARNING)
|
||||||
|
CGAL_pragma_warning("\nThe macro CGAL_ISOSURFACING_3_COMMERCIAL_LICENSE is not defined."
|
||||||
|
"\nYou use the CGAL 3D Isosurfacing package under "
|
||||||
|
"the terms of the GPLv3+.")
|
||||||
|
# endif // CGAL_LICENSE_WARNING
|
||||||
|
|
||||||
|
# ifdef CGAL_LICENSE_ERROR
|
||||||
|
# error "The macro CGAL_ISOSURFACING_3_COMMERCIAL_LICENSE is not defined.\
|
||||||
|
You use the CGAL 3D Isosurfacing package under the terms of \
|
||||||
|
the GPLv3+. You get this error, as you defined CGAL_LICENSE_ERROR."
|
||||||
|
# endif // CGAL_LICENSE_ERROR
|
||||||
|
|
||||||
|
#endif // no CGAL_ISOSURFACING_3_COMMERCIAL_LICENSE
|
||||||
|
|
||||||
|
#endif // CGAL_LICENSE_ISOSURFACING_3_H
|
||||||
|
|
@ -25,6 +25,7 @@ Hyperbolic_triangulation_2 2D Hyperbolic Delaunay Triangulations
|
||||||
Inscribed_areas Inscribed Areas
|
Inscribed_areas Inscribed Areas
|
||||||
Interpolation 2D and Surface Function Interpolation
|
Interpolation 2D and Surface Function Interpolation
|
||||||
Interval_skip_list Interval Skip List
|
Interval_skip_list Interval Skip List
|
||||||
|
Isosurfacing_3 3D Isosurfacing
|
||||||
Jet_fitting_3 Estimation of Local Differential Properties of Point-Sampled Surfaces
|
Jet_fitting_3 Estimation of Local Differential Properties of Point-Sampled Surfaces
|
||||||
Kinetic_surface_reconstruction Kinetic Surface Reconstruction
|
Kinetic_surface_reconstruction Kinetic Surface Reconstruction
|
||||||
Matrix_search Monotone and Sorted Matrix Search
|
Matrix_search Monotone and Sorted Matrix Search
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
# Created by the script cgal_create_cmake_script
|
||||||
|
# This is the CMake script for compiling a CGAL application.
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.1...3.23)
|
||||||
|
project( Isosurfacing_3_benchmark )
|
||||||
|
|
||||||
|
find_package(CGAL REQUIRED)
|
||||||
|
|
||||||
|
find_package(Eigen3 3.1.0 QUIET) #(3.1.0 or greater)
|
||||||
|
include(CGAL_Eigen3_support)
|
||||||
|
|
||||||
|
find_package(TBB)
|
||||||
|
include(CGAL_TBB_support)
|
||||||
|
|
||||||
|
if(TARGET CGAL::Eigen3_support)
|
||||||
|
create_single_source_cgal_program("benchmark.cpp" )
|
||||||
|
create_single_source_cgal_program("contouring_seq_vs_parallel_implicit.cpp" )
|
||||||
|
create_single_source_cgal_program("contouring_seq_vs_parallel_image.cpp" )
|
||||||
|
|
||||||
|
target_compile_definitions(benchmark PUBLIC ${SCENARIO})
|
||||||
|
target_compile_definitions(benchmark PUBLIC ${KERNEL})
|
||||||
|
target_compile_definitions(benchmark PUBLIC ${ALGO})
|
||||||
|
target_compile_definitions(benchmark PUBLIC ${TAG})
|
||||||
|
|
||||||
|
target_link_libraries(benchmark PRIVATE CGAL::Eigen3_support)
|
||||||
|
target_link_libraries(contouring_seq_vs_parallel_implicit PRIVATE CGAL::Eigen3_support)
|
||||||
|
target_link_libraries(contouring_seq_vs_parallel_image PRIVATE CGAL::Eigen3_support)
|
||||||
|
|
||||||
|
if(TARGET CGAL::TBB_support)
|
||||||
|
target_link_libraries(benchmark PUBLIC CGAL::TBB_support)
|
||||||
|
target_link_libraries(contouring_seq_vs_parallel_implicit PUBLIC CGAL::TBB_support)
|
||||||
|
target_link_libraries(contouring_seq_vs_parallel_image PUBLIC CGAL::TBB_support)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
message(STATUS "NOTICE: Some benchmarks use Eigen, and will not be compiled.")
|
||||||
|
endif()
|
||||||
|
|
@ -0,0 +1,350 @@
|
||||||
|
#include <CGAL/Cartesian.h>
|
||||||
|
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
||||||
|
#include <CGAL/Simple_cartesian.h>
|
||||||
|
#include <CGAL/Surface_mesh.h>
|
||||||
|
|
||||||
|
#include <CGAL/Isosurfacing_3/Cartesian_grid_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/dual_contouring_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/marching_cubes_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/internal/Isosurfacing_domain_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Value_function_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Gradient_function_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Interpolated_discrete_gradients_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Interpolated_discrete_values_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Finite_difference_gradient_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/boost/graph/IO/OFF.h>
|
||||||
|
#include <CGAL/Image_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/IO/Image_3.h>
|
||||||
|
#include <CGAL/Real_timer.h>
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <iostream>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#ifndef M_PI
|
||||||
|
# define M_PI 3.141592653589793238462643383279502884L
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <class GeomTraits>
|
||||||
|
struct Sphere_value
|
||||||
|
{
|
||||||
|
using FT = typename GeomTraits::FT;
|
||||||
|
using Point = typename GeomTraits::Point_3;
|
||||||
|
|
||||||
|
FT operator()(const Point& point) const
|
||||||
|
{
|
||||||
|
return CGAL::approximate_sqrt((point - CGAL::ORIGIN).squared_length());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class GeomTraits>
|
||||||
|
struct Sphere_gradient
|
||||||
|
{
|
||||||
|
using FT = typename GeomTraits::FT;
|
||||||
|
using Point = typename GeomTraits::Point_3;
|
||||||
|
using Vector = typename GeomTraits::Vector_3;
|
||||||
|
|
||||||
|
Vector operator()(const Point& point) const
|
||||||
|
{
|
||||||
|
Vector g = point - CGAL::ORIGIN;
|
||||||
|
return g / CGAL::approximate_sqrt(g.squared_length());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class GeomTraits>
|
||||||
|
struct Implicit_sphere
|
||||||
|
{
|
||||||
|
using FT = typename GeomTraits::FT;
|
||||||
|
using Vector = typename GeomTraits::Vector_3;
|
||||||
|
|
||||||
|
using Grid = CGAL::Isosurfacing::Cartesian_grid_3<GeomTraits>;
|
||||||
|
using Values = CGAL::Isosurfacing::Value_function_3<Grid>;
|
||||||
|
using Gradients = CGAL::Isosurfacing::Gradient_function_3<Grid>;
|
||||||
|
using Domain = CGAL::Isosurfacing::internal::Isosurfacing_domain_3<Grid, Values, Gradients>;
|
||||||
|
|
||||||
|
Implicit_sphere(const std::size_t N)
|
||||||
|
: res(2. / N, 2. / N, 2. / N),
|
||||||
|
grid { CGAL::Bbox_3 {-1, -1, -1, 1, 1, 1}, CGAL::make_array<std::size_t>(N, N, N) },
|
||||||
|
values { Sphere_value<GeomTraits>{}, grid },
|
||||||
|
gradients { Sphere_gradient<GeomTraits>{}, grid }
|
||||||
|
{ }
|
||||||
|
|
||||||
|
Domain domain() const
|
||||||
|
{
|
||||||
|
return { grid, values, gradients };
|
||||||
|
}
|
||||||
|
|
||||||
|
FT iso() const
|
||||||
|
{
|
||||||
|
return 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Vector res;
|
||||||
|
Grid grid;
|
||||||
|
Values values;
|
||||||
|
Gradients gradients;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class GeomTraits>
|
||||||
|
struct IWPValue
|
||||||
|
{
|
||||||
|
using FT = typename GeomTraits::FT;
|
||||||
|
using Point = typename GeomTraits::Point_3;
|
||||||
|
|
||||||
|
FT operator()(const Point& point) const
|
||||||
|
{
|
||||||
|
const FT alpha = 5.01;
|
||||||
|
// const FT alpha = 1.01;
|
||||||
|
|
||||||
|
const FT x = alpha * (point.x() + 1) * M_PI;
|
||||||
|
const FT y = alpha * (point.y() + 1) * M_PI;
|
||||||
|
const FT z = alpha * (point.z() + 1) * M_PI;
|
||||||
|
|
||||||
|
return cos(x)*cos(y) + cos(y)*cos(z) + cos(z)*cos(x) - cos(x)*cos(y)*cos(z); // isovalue = 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class GeomTraits>
|
||||||
|
struct IWPGradient
|
||||||
|
{
|
||||||
|
using FT = typename GeomTraits::FT;
|
||||||
|
using Point = typename GeomTraits::Point_3;
|
||||||
|
using Vector = typename GeomTraits::Vector_3;
|
||||||
|
|
||||||
|
Vector operator()(const Point& point) const
|
||||||
|
{
|
||||||
|
const FT alpha = 5.01;
|
||||||
|
// const FT alpha = 1.01;
|
||||||
|
|
||||||
|
const FT x = alpha * (point.x() + 1) * M_PI;
|
||||||
|
const FT y = alpha * (point.y() + 1) * M_PI;
|
||||||
|
const FT z = alpha * (point.z() + 1) * M_PI;
|
||||||
|
|
||||||
|
const FT gx = M_PI * alpha * sin(x) * (cos(y) * (cos(z) - 1.0) - cos(z));
|
||||||
|
const FT gy = M_PI * alpha * sin(y) * (cos(x) * (cos(z) - 1.0) - cos(z));
|
||||||
|
const FT gz = M_PI * alpha * sin(z) * (cos(x) * (cos(y) - 1.0) - cos(y));
|
||||||
|
|
||||||
|
return { gx, gy, gz };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class GeomTraits>
|
||||||
|
struct Implicit_iwp
|
||||||
|
{
|
||||||
|
using FT = typename GeomTraits::FT;
|
||||||
|
using Vector = typename GeomTraits::Vector_3;
|
||||||
|
|
||||||
|
using Grid = CGAL::Isosurfacing::Cartesian_grid_3<GeomTraits>;
|
||||||
|
using Values = CGAL::Isosurfacing::Value_function_3<Grid>;
|
||||||
|
using Gradients = CGAL::Isosurfacing::Gradient_function_3<Grid>;
|
||||||
|
using Domain = CGAL::Isosurfacing::internal::Isosurfacing_domain_3<Grid, Values, Gradients>;
|
||||||
|
|
||||||
|
Implicit_iwp(const std::size_t N)
|
||||||
|
: res(2. / N, 2. / N, 2. / N),
|
||||||
|
grid { CGAL::Bbox_3{-1, -1, -1, 1, 1, 1}, CGAL::make_array<std::size_t>(N, N, N) },
|
||||||
|
values { IWPValue<GeomTraits>{}, grid },
|
||||||
|
gradients { IWPGradient<GeomTraits>{}, grid }
|
||||||
|
{ }
|
||||||
|
|
||||||
|
Domain domain() const
|
||||||
|
{
|
||||||
|
return { grid, values, gradients };
|
||||||
|
}
|
||||||
|
|
||||||
|
FT iso() const
|
||||||
|
{
|
||||||
|
return 0.;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Vector res;
|
||||||
|
Grid grid;
|
||||||
|
Values values;
|
||||||
|
Gradients gradients;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class GeomTraits>
|
||||||
|
struct Grid_sphere
|
||||||
|
{
|
||||||
|
using Grid = CGAL::Isosurfacing::Cartesian_grid_3<GeomTraits>;
|
||||||
|
using Values = CGAL::Isosurfacing::Interpolated_discrete_values_3<Grid>;
|
||||||
|
using Gradients = CGAL::Isosurfacing::Interpolated_discrete_gradients_3<Grid>;
|
||||||
|
using Domain = CGAL::Isosurfacing::internal::Isosurfacing_domain_3<Grid, Values, Gradients>;
|
||||||
|
|
||||||
|
using FT = typename GeomTraits::FT;
|
||||||
|
using Point = typename GeomTraits::Point_3;
|
||||||
|
|
||||||
|
Grid_sphere(const std::size_t N)
|
||||||
|
: grid { CGAL::Bbox_3{-1., -1., -1., 1., 1., 1.},
|
||||||
|
CGAL::make_array<std::size_t>(N, N, N) },
|
||||||
|
values { grid },
|
||||||
|
gradients { grid }
|
||||||
|
{
|
||||||
|
const Sphere_value<GeomTraits> sphere_val;
|
||||||
|
const Sphere_gradient<GeomTraits> sphere_grad;
|
||||||
|
const FT resolution = 2.0 / N;
|
||||||
|
|
||||||
|
for(std::size_t x = 0; x < grid.xdim(); x++)
|
||||||
|
{
|
||||||
|
const FT xp = x * resolution - 1.0;
|
||||||
|
for(std::size_t y = 0; y < grid.ydim(); y++)
|
||||||
|
{
|
||||||
|
const FT yp = y * resolution - 1.0;
|
||||||
|
for(std::size_t z = 0; z < grid.zdim(); z++)
|
||||||
|
{
|
||||||
|
const FT zp = z * resolution - 1.0;
|
||||||
|
|
||||||
|
values(x, y, z) = sphere_val(Point(xp, yp, zp));
|
||||||
|
gradients(x, y, z) = sphere_grad(Point(xp, yp, zp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Domain domain() const
|
||||||
|
{
|
||||||
|
return { grid, values, gradients };
|
||||||
|
}
|
||||||
|
|
||||||
|
typename GeomTraits::FT iso() const
|
||||||
|
{
|
||||||
|
return 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Grid grid;
|
||||||
|
Values values;
|
||||||
|
Gradients gradients;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class GeomTraits>
|
||||||
|
struct Skull_image
|
||||||
|
{
|
||||||
|
using FT = typename GeomTraits::FT;
|
||||||
|
using Grid = CGAL::Isosurfacing::Cartesian_grid_3<GeomTraits>;
|
||||||
|
using Values = CGAL::Isosurfacing::Interpolated_discrete_values_3<Grid>;
|
||||||
|
using Gradients = CGAL::Isosurfacing::Finite_difference_gradient_3<GeomTraits>;
|
||||||
|
using Domain = CGAL::Isosurfacing::internal::Isosurfacing_domain_3<Grid, Values, Gradients>;
|
||||||
|
|
||||||
|
Skull_image(const std::size_t N)
|
||||||
|
: grid { },
|
||||||
|
values { grid }
|
||||||
|
{
|
||||||
|
const std::string fname = CGAL::data_file_path("images/skull_2.9.inr");
|
||||||
|
CGAL::Image_3 image;
|
||||||
|
if(!image.read(fname))
|
||||||
|
std::cerr << "Error: Cannot read file " << fname << std::endl;
|
||||||
|
|
||||||
|
Grid grid;
|
||||||
|
Values values { grid };
|
||||||
|
if(!CGAL::Isosurfacing::IO::convert_image_to_grid(image, grid, values))
|
||||||
|
std::cerr << "Error: Cannot convert image to Cartesian grid" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
Domain domain() const
|
||||||
|
{
|
||||||
|
const FT step = CGAL::approximate_sqrt(grid.spacing().squared_length()) * 0.01;
|
||||||
|
Gradients gradients { values, step };
|
||||||
|
return { grid, values, gradients };
|
||||||
|
}
|
||||||
|
|
||||||
|
typename GeomTraits::FT iso() const
|
||||||
|
{
|
||||||
|
return 2.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Grid grid;
|
||||||
|
Values values;
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
std::size_t N = 100;
|
||||||
|
|
||||||
|
const int argc_check = argc - 1;
|
||||||
|
|
||||||
|
for(int i=1; i<argc; ++i)
|
||||||
|
{
|
||||||
|
if(!strcmp("-N", argv[i]) && i < argc_check)
|
||||||
|
N = std::stoi(argv[++i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined KERNEL_SIMPLE_CARTESIAN_DOUBLE
|
||||||
|
std::cout << "KERNEL_SIMPLE_CARTESIAN_DOUBLE" << std::endl;
|
||||||
|
using Kernel = CGAL::Simple_cartesian<double>;
|
||||||
|
#elif defined KERNEL_SIMPLE_CARTESIAN_FLOAT
|
||||||
|
std::cout << "KERNEL_SIMPLE_CARTESIAN_FLOAT" << std::endl;
|
||||||
|
using Kernel = CGAL::Simple_cartesian<float>;
|
||||||
|
#elif defined KERNEL_CARTESIAN_DOUBLE
|
||||||
|
std::cout << "KERNEL_CARTESIAN_DOUBLE" << std::endl;
|
||||||
|
using Kernel = CGAL::Cartesian<double>;
|
||||||
|
#elif defined KERNEL_EPIC
|
||||||
|
std::cout << "KERNEL_EPIC" << std::endl;
|
||||||
|
using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel;
|
||||||
|
#else
|
||||||
|
std::cout << "no kernel selected!" << std::endl;
|
||||||
|
using Kernel = CGAL::Simple_cartesian<double>;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using Point = Kernel::Point_3;
|
||||||
|
|
||||||
|
using Point_range = std::vector<Point>;
|
||||||
|
using Polygon_range = std::vector<std::vector<std::size_t> >;
|
||||||
|
|
||||||
|
#if defined SCENARIO_GRID_SPHERE
|
||||||
|
std::cout << "SCENARIO_GRID_SPHERE" << std::endl;
|
||||||
|
auto scenario = Grid_sphere<Kernel>(N);
|
||||||
|
#elif defined SCENARIO_IMPLICIT_SPHERE
|
||||||
|
std::cout << "SCENARIO_IMPLICIT_SPHERE" << std::endl;
|
||||||
|
auto scenario = Implicit_sphere<Kernel>(N);
|
||||||
|
#elif defined SCENARIO_IMPLICIT_IWP
|
||||||
|
std::cout << "SCENARIO_IMPLICIT_IWP" << std::endl;
|
||||||
|
auto scenario = Implicit_iwp<Kernel>(N);
|
||||||
|
#elif defined SCENARIO_SKULL_IMAGE
|
||||||
|
std::cout << "SCENARIO_SKULL_IMAGE" << std::endl;
|
||||||
|
auto scenario = Skull_image<Kernel>(N);
|
||||||
|
#else
|
||||||
|
std::cout << "no scenario selected!" << std::endl;
|
||||||
|
auto scenario = Implicit_sphere<Kernel>(N);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Point_range points;
|
||||||
|
Polygon_range polygons;
|
||||||
|
|
||||||
|
CGAL::Real_timer timer;
|
||||||
|
timer.start();
|
||||||
|
|
||||||
|
#if defined TAG_PARALLEL
|
||||||
|
std::cout << "TAG_PARALLEL" << std::endl;
|
||||||
|
using Tag = CGAL::Parallel_tag;
|
||||||
|
#elif defined TAG_SEQUENTIAL
|
||||||
|
std::cout << "TAG_SEQUENTIAL" << std::endl;
|
||||||
|
using Tag = CGAL::Sequential_tag;
|
||||||
|
#else
|
||||||
|
std::cout << "no tag selected!" << std::endl;
|
||||||
|
using Tag = CGAL::Sequential_tag;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined ALGO_MARCHING_CUBES
|
||||||
|
std::cout << "ALGO_MARCHING_CUBES" << std::endl;
|
||||||
|
CGAL::Isosurfacing::marching_cubes<Tag>(scenario.domain(), scenario.iso(), points, polygons);
|
||||||
|
#elif defined ALGO_DUAL_CONTOURING
|
||||||
|
std::cout << "ALGO_DUAL_CONTOURING" << std::endl;
|
||||||
|
CGAL::Isosurfacing::dual_contouring<Tag>(scenario.domain(), scenario.iso(), points, polygons);
|
||||||
|
#else
|
||||||
|
std::cout << "no algorithm selected!" << std::endl;
|
||||||
|
CGAL::Isosurfacing::marching_cubes<Tag>(scenario.domain(), scenario.iso(), points, polygons);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
timer.stop();
|
||||||
|
|
||||||
|
if(points.size() > std::numeric_limits<std::size_t>::max() - 2)
|
||||||
|
std::cout << "This should never print and only prevents optimizations" << std::endl;
|
||||||
|
|
||||||
|
std::cout << "internal timer: " << timer.time() << std::endl;
|
||||||
|
std::cout << "internal polygons: " << polygons.size() << std::endl;
|
||||||
|
std::cout << "internal points: " << points.size() << std::endl;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
from benchmark_util import *
|
||||||
|
|
||||||
|
# KERNEL_SIMPLE_CARTESIAN_DOUBLE
|
||||||
|
# KERNEL_SIMPLE_CARTESIAN_FLOAT
|
||||||
|
# KERNEL_CARTESIAN_DOUBLE
|
||||||
|
# KERNEL_EPIC
|
||||||
|
kernel = "KERNEL_CARTESIAN_DOUBLE"
|
||||||
|
|
||||||
|
# SCENARIO_GRID_SPHERE
|
||||||
|
# SCENARIO_IMPLICIT_SPHERE
|
||||||
|
# SCENARIO_IMPLICIT_IWP
|
||||||
|
# SCENARIO_SKULL_IMAGE
|
||||||
|
scenario = "SCENARIO_SKULL_IMAGE"
|
||||||
|
|
||||||
|
# TAG_SEQUENTIAL
|
||||||
|
# TAG_PARALLEL
|
||||||
|
tag = "TAG_PARALLEL"
|
||||||
|
|
||||||
|
# ALGO_MARCHING_CUBES
|
||||||
|
# ALGO_DUAL_CONTOURING
|
||||||
|
algorithm = "ALGO_DUAL_CONTOURING"
|
||||||
|
threads = 1
|
||||||
|
exponent = 1.2
|
||||||
|
min_cells = 100000
|
||||||
|
max_cells = 100000000
|
||||||
|
|
||||||
|
build(scenario, kernel, algorithm, tag)
|
||||||
|
|
||||||
|
data = []
|
||||||
|
|
||||||
|
c = min_cells
|
||||||
|
while c < max_cells:
|
||||||
|
n = int(c ** (1.0 / 3.0))
|
||||||
|
|
||||||
|
res = execute(n, threads, 5)
|
||||||
|
data.append([scenario, kernel, algorithm, tag, threads, int(c), res["time"], res["polygons"], res["points"]])
|
||||||
|
|
||||||
|
c *= exponent
|
||||||
|
|
||||||
|
df = pd.DataFrame(data, columns=["scenario", "kernel", "algorithm", "tag", "threads", "cells", "time", "polygons", "points"])
|
||||||
|
|
||||||
|
df.to_csv("benchmark_size.csv")
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
from benchmark_util import *
|
||||||
|
|
||||||
|
scenario = "SCENARIO_IMPLICIT_IWP"
|
||||||
|
kernel = "KERNEL_SIMPLE_CARTESIAN_DOUBLE"
|
||||||
|
algorithm = "ALGO_MARCHING_CUBES"
|
||||||
|
tag = "TAG_PARALLEL"
|
||||||
|
min_threads = 1
|
||||||
|
max_threads = 26
|
||||||
|
n = 500
|
||||||
|
cells = n ** 3
|
||||||
|
|
||||||
|
build(scenario, kernel, algorithm, tag)
|
||||||
|
|
||||||
|
data = []
|
||||||
|
|
||||||
|
for t in range(min_threads, max_threads + 1):
|
||||||
|
res = execute(n, t, times=5)
|
||||||
|
|
||||||
|
data.append([scenario, kernel, algorithm, tag, t, cells, res["time"], res["bandwidth"]])
|
||||||
|
|
||||||
|
df = pd.DataFrame(data, columns=["scenario", "kernel", "algorithm", "tag", "threads", "cells", "time", "bandwidth"])
|
||||||
|
|
||||||
|
df.to_csv("benchmark_threads.csv")
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
def print_stream(stream):
|
||||||
|
while True:
|
||||||
|
line = stream.readline()
|
||||||
|
if not line:
|
||||||
|
break
|
||||||
|
print(line, end="")
|
||||||
|
|
||||||
|
def run(cmd, output=True):
|
||||||
|
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
|
||||||
|
exit_code = process.wait()
|
||||||
|
if output:
|
||||||
|
print_stream(process.stdout)
|
||||||
|
if exit_code != 0:
|
||||||
|
print_stream(process.stderr)
|
||||||
|
sys.exit(exit_code)
|
||||||
|
return process
|
||||||
|
|
||||||
|
def build(scenario, kernel, algorithm, tag):
|
||||||
|
run(["cmake", "-E", "make_directory", "build"])
|
||||||
|
run(["cmake", "-B", "build", "-DCMAKE_BUILD_TYPE=Release", "-DSCENARIO=" + scenario, "-DKERNEL=" + kernel, "-DALGO=" + algorithm, "-DTAG=" + tag, "-DCGAL_DIR=../../../"])
|
||||||
|
run(["make", "-C", "build"])
|
||||||
|
|
||||||
|
def execute(n, threads, times=1):
|
||||||
|
measurements = {"time" : 0, "polygons" : 0, "points" : 0, "bandwidth" : 0, "transfer" : 0, "performance" : 0, "clock" : 0, "intensity" : 0}
|
||||||
|
|
||||||
|
for i in range(times):
|
||||||
|
process = run(["likwid-perfctr", "-g", "MEM_DP", "-C", "S0:0-" + str(threads - 1), "./build/benchmark", "-N", str(n)], False)
|
||||||
|
|
||||||
|
for line in process.stdout.readlines():
|
||||||
|
print(line, end='')
|
||||||
|
|
||||||
|
m = re.search(r'internal timer:\s*(\d+)', line)
|
||||||
|
if m is not None:
|
||||||
|
measurements["time"] += int(m.group(1))
|
||||||
|
|
||||||
|
m = re.search(r'internal polygons:\s*(\d+)', line)
|
||||||
|
if m is not None:
|
||||||
|
measurements["polygons"] += int(m.group(1))
|
||||||
|
|
||||||
|
m = re.search(r'internal points:\s*(\d+)', line)
|
||||||
|
if m is not None:
|
||||||
|
measurements["points"] += int(m.group(1))
|
||||||
|
|
||||||
|
m = re.search(r'Memory bandwidth.*\s+(\d+(\.\d+)?) \|\s*$', line)
|
||||||
|
if m is not None:
|
||||||
|
measurements["bandwidth"] += float(m.group(1))
|
||||||
|
|
||||||
|
m = re.search(r'Memory data volume.*\s+(\d+(\.\d+)?) \|\s*$', line)
|
||||||
|
if m is not None:
|
||||||
|
measurements["transfer"] += float(m.group(1))
|
||||||
|
|
||||||
|
m = re.search(r'DP.*\s+(\d+(\.\d+)?) \|\s*$', line)
|
||||||
|
if m is not None:
|
||||||
|
measurements["performance"] += float(m.group(1))
|
||||||
|
|
||||||
|
m = re.search(r'Clock.*\s+(\d+(\.\d+)?) \|\s*$', line)
|
||||||
|
if m is not None:
|
||||||
|
measurements["clock"] += float(m.group(1))
|
||||||
|
|
||||||
|
m = re.search(r'Operational intensity.*\s+(\d+(\.\d+)?) \|\s*$', line)
|
||||||
|
if m is not None:
|
||||||
|
measurements["intensity"] += float(m.group(1))
|
||||||
|
|
||||||
|
for item in measurements.items():
|
||||||
|
measurements[item[0]] = item[1] / times
|
||||||
|
|
||||||
|
return measurements
|
||||||
|
|
@ -0,0 +1,156 @@
|
||||||
|
#include <CGAL/Simple_cartesian.h>
|
||||||
|
|
||||||
|
#include <CGAL/Isosurfacing_3/Cartesian_grid_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/dual_contouring_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Dual_contouring_domain_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/marching_cubes_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Marching_cubes_domain_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Value_function_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Finite_difference_gradient_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/Real_timer.h>
|
||||||
|
|
||||||
|
#include <CGAL/Isosurfacing_3/IO/Image_3.h>
|
||||||
|
#include <CGAL/IO/polygon_soup_io.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using Kernel = CGAL::Simple_cartesian<double>;
|
||||||
|
using FT = typename Kernel::FT;
|
||||||
|
using Point = typename Kernel::Point_3;
|
||||||
|
using Vector = typename Kernel::Vector_3;
|
||||||
|
|
||||||
|
using Grid = CGAL::Isosurfacing::Cartesian_grid_3<Kernel>;
|
||||||
|
using Values = CGAL::Isosurfacing::Interpolated_discrete_values_3<Grid>;
|
||||||
|
using Gradients = CGAL::Isosurfacing::Finite_difference_gradient_3<Kernel>;
|
||||||
|
|
||||||
|
using Point_range = std::vector<Point>;
|
||||||
|
using Polygon_range = std::vector<std::vector<std::size_t> >;
|
||||||
|
|
||||||
|
namespace IS = CGAL::Isosurfacing;
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
const std::string fname = (argc > 1) ? argv[1] : CGAL::data_file_path("images/skull_2.9.inr");
|
||||||
|
const FT isovalue = (argc > 2) ? std::stod(argv[2]) : - 2.9;
|
||||||
|
|
||||||
|
// load volumetric image from a file
|
||||||
|
CGAL::Image_3 image;
|
||||||
|
if(!image.read(fname))
|
||||||
|
{
|
||||||
|
std::cerr << "Error: Cannot read image file " << fname << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert image to a Cartesian grid
|
||||||
|
Grid grid;
|
||||||
|
Values values { grid };
|
||||||
|
if(!IS::IO::convert_image_to_grid(image, grid, values))
|
||||||
|
{
|
||||||
|
std::cerr << "Error: Cannot convert image to Cartesian grid" << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Span: " << grid.span() << std::endl;
|
||||||
|
std::cout << "Cell dimensions: " << grid.spacing()[0] << " " << grid.spacing()[1] << " " << grid.spacing()[2] << std::endl;
|
||||||
|
std::cout << "Cell #: " << grid.xdim() << ", " << grid.ydim() << ", " << grid.zdim() << std::endl;
|
||||||
|
|
||||||
|
const FT step = CGAL::approximate_sqrt(grid.spacing().squared_length()) * 0.01;
|
||||||
|
Gradients gradients { values, step };
|
||||||
|
|
||||||
|
const bool triangulate_faces = false;
|
||||||
|
|
||||||
|
// DC sequential
|
||||||
|
{
|
||||||
|
using Domain = CGAL::Isosurfacing::Dual_contouring_domain_3<Grid, Values, Gradients>;
|
||||||
|
Domain domain { grid, values, gradients };
|
||||||
|
|
||||||
|
CGAL::Real_timer timer;
|
||||||
|
timer.start();
|
||||||
|
|
||||||
|
Point_range points;
|
||||||
|
Polygon_range triangles;
|
||||||
|
|
||||||
|
std::cout << "--- Dual Contouring (Sequential)" << std::endl;
|
||||||
|
IS::dual_contouring<CGAL::Sequential_tag>(
|
||||||
|
domain, isovalue, points, triangles, CGAL::parameters::do_not_triangulate_faces(!triangulate_faces));
|
||||||
|
|
||||||
|
timer.stop();
|
||||||
|
|
||||||
|
std::cout << "Output #vertices: " << points.size() << std::endl;
|
||||||
|
std::cout << "Output #triangles: " << triangles.size() << std::endl;
|
||||||
|
std::cout << "Elapsed time: " << timer.time() << " seconds" << std::endl;
|
||||||
|
CGAL::IO::write_polygon_soup("dual_contouring_image_sequential.off", points, triangles);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DC parallel
|
||||||
|
{
|
||||||
|
using Domain = CGAL::Isosurfacing::Dual_contouring_domain_3<Grid, Values, Gradients>;
|
||||||
|
Domain domain { grid, values, gradients };
|
||||||
|
|
||||||
|
CGAL::Real_timer timer;
|
||||||
|
timer.start();
|
||||||
|
|
||||||
|
Point_range points;
|
||||||
|
Polygon_range triangles;
|
||||||
|
|
||||||
|
std::cout << "--- Dual Contouring (Parallel)" << std::endl;
|
||||||
|
IS::dual_contouring<CGAL::Parallel_if_available_tag>(
|
||||||
|
domain, isovalue, points, triangles, CGAL::parameters::do_not_triangulate_faces(!triangulate_faces));
|
||||||
|
|
||||||
|
timer.stop();
|
||||||
|
|
||||||
|
std::cout << "Output #vertices: " << points.size() << std::endl;
|
||||||
|
std::cout << "Output #triangles: " << triangles.size() << std::endl;
|
||||||
|
std::cout << "Elapsed time: " << timer.time() << " seconds" << std::endl;
|
||||||
|
CGAL::IO::write_polygon_soup("dual_contouring_image_parallel.off", points, triangles);
|
||||||
|
}
|
||||||
|
|
||||||
|
// MC Sequential
|
||||||
|
{
|
||||||
|
using Domain = CGAL::Isosurfacing::Marching_cubes_domain_3<Grid, Values>;
|
||||||
|
Domain domain { grid, values };
|
||||||
|
|
||||||
|
CGAL::Real_timer timer;
|
||||||
|
timer.start();
|
||||||
|
|
||||||
|
Point_range points;
|
||||||
|
Polygon_range triangles;
|
||||||
|
|
||||||
|
std::cout << "--- Marching Cubes (Sequential)" << std::endl;
|
||||||
|
IS::marching_cubes<CGAL::Sequential_tag>(domain, isovalue, points, triangles);
|
||||||
|
|
||||||
|
timer.stop();
|
||||||
|
|
||||||
|
std::cout << "Output #vertices: " << points.size() << std::endl;
|
||||||
|
std::cout << "Output #triangles: " << triangles.size() << std::endl;
|
||||||
|
std::cout << "Elapsed time: " << timer.time() << " seconds" << std::endl;
|
||||||
|
CGAL::IO::write_polygon_soup("marching_cubes_image_sequential.off", points, triangles);
|
||||||
|
}
|
||||||
|
|
||||||
|
// MC parallel
|
||||||
|
{
|
||||||
|
using Domain = CGAL::Isosurfacing::Marching_cubes_domain_3<Grid, Values>;
|
||||||
|
Domain domain { grid, values };
|
||||||
|
|
||||||
|
CGAL::Real_timer timer;
|
||||||
|
timer.start();
|
||||||
|
|
||||||
|
Point_range points;
|
||||||
|
Polygon_range triangles;
|
||||||
|
|
||||||
|
std::cout << "--- Marching Cubes (Parallel)" << std::endl;
|
||||||
|
IS::marching_cubes<CGAL::Parallel_if_available_tag>(domain, isovalue, points, triangles);
|
||||||
|
|
||||||
|
timer.stop();
|
||||||
|
|
||||||
|
std::cout << "Output #vertices: " << points.size() << std::endl;
|
||||||
|
std::cout << "Output #triangles: " << triangles.size() << std::endl;
|
||||||
|
std::cout << "Elapsed time: " << timer.time() << " seconds" << std::endl;
|
||||||
|
CGAL::IO::write_polygon_soup("marching_cubes_image_parallel.off", points, triangles);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Done" << std::endl;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,208 @@
|
||||||
|
#include <CGAL/Simple_cartesian.h>
|
||||||
|
|
||||||
|
#include <CGAL/Isosurfacing_3/Cartesian_grid_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/dual_contouring_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Dual_contouring_domain_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/marching_cubes_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Marching_cubes_domain_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Value_function_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Finite_difference_gradient_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/internal/implicit_shapes_helper.h>
|
||||||
|
|
||||||
|
#include <CGAL/Bbox_3.h>
|
||||||
|
#include <CGAL/Real_timer.h>
|
||||||
|
#include <CGAL/IO/polygon_soup_io.h>
|
||||||
|
#include <CGAL/number_utils.h>
|
||||||
|
|
||||||
|
#include <tbb/task_arena.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using Kernel = CGAL::Simple_cartesian<double>;
|
||||||
|
using FT = typename Kernel::FT;
|
||||||
|
using Point = typename Kernel::Point_3;
|
||||||
|
using Vector = typename Kernel::Vector_3;
|
||||||
|
|
||||||
|
using Grid = CGAL::Isosurfacing::Cartesian_grid_3<Kernel>;
|
||||||
|
using Values = CGAL::Isosurfacing::Value_function_3<Grid>;
|
||||||
|
using Gradients = CGAL::Isosurfacing::Finite_difference_gradient_3<Kernel>;
|
||||||
|
|
||||||
|
using Point_range = std::vector<Point>;
|
||||||
|
using Polygon_range = std::vector<std::vector<std::size_t> >;
|
||||||
|
|
||||||
|
namespace IS = CGAL::Isosurfacing;
|
||||||
|
|
||||||
|
auto implicit_function = [](const Point& q) -> FT
|
||||||
|
{
|
||||||
|
auto cyl = [](const Point& q) { return IS::Shapes::infinite_cylinder<Kernel>(Point(0,0,0), Vector(0,0,1), 0.5, q); };
|
||||||
|
auto cube = [](const Point& q) { return IS::Shapes::box<Kernel>(Point(-0.5,-0.5,-0.5), Point(0.5,0.5,0.5), q); };
|
||||||
|
auto cyl_and_cube = [&](const Point& q) { return IS::Shapes::shape_union<Kernel>(cyl, cube, q); };
|
||||||
|
|
||||||
|
auto sphere = [](const Point& q) { return IS::Shapes::sphere<Kernel>(Point(0,0,0.5), 0.75, q); };
|
||||||
|
return IS::Shapes::shape_difference<Kernel>(cyl_and_cube, sphere, q);
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
int num_threads = tbb::this_task_arena::max_concurrency();
|
||||||
|
std::cout << "Number of TBB threads: " << num_threads << std::endl;
|
||||||
|
|
||||||
|
const FT isovalue = (argc > 1) ? std::stod(argv[1]) : 0.;
|
||||||
|
std::cout << "Isovalue: " << isovalue << std::endl;
|
||||||
|
|
||||||
|
// create bounding box and grid
|
||||||
|
const CGAL::Bbox_3 bbox = {-2., -2., -2., 2., 2., 2.};
|
||||||
|
Grid grid { bbox, CGAL::make_array<std::size_t>(500, 500, 500) };
|
||||||
|
|
||||||
|
std::cout << "Span: " << grid.span() << std::endl;
|
||||||
|
std::cout << "Cell dimensions: " << grid.spacing()[0] << " " << grid.spacing()[1] << " " << grid.spacing()[2] << std::endl;
|
||||||
|
std::cout << "Cell #: " << grid.xdim() << ", " << grid.ydim() << ", " << grid.zdim() << std::endl;
|
||||||
|
|
||||||
|
// fill up values and gradients
|
||||||
|
Values values { implicit_function, grid };
|
||||||
|
|
||||||
|
const FT step = CGAL::approximate_sqrt(grid.spacing().squared_length()) * 0.01;
|
||||||
|
Gradients gradients { values, step };
|
||||||
|
|
||||||
|
const bool triangulate_faces = false;
|
||||||
|
|
||||||
|
// DC parallel
|
||||||
|
{
|
||||||
|
using Domain = CGAL::Isosurfacing::Dual_contouring_domain_3<Grid, Values, Gradients>;
|
||||||
|
Domain domain { grid, values, gradients };
|
||||||
|
|
||||||
|
CGAL::Real_timer timer;
|
||||||
|
timer.start();
|
||||||
|
|
||||||
|
Point_range points;
|
||||||
|
Polygon_range triangles;
|
||||||
|
|
||||||
|
std::cout << "--- Dual Contouring (Parallel)" << std::endl;
|
||||||
|
IS::dual_contouring<CGAL::Parallel_tag>(
|
||||||
|
domain, isovalue, points, triangles, CGAL::parameters::do_not_triangulate_faces(!triangulate_faces));
|
||||||
|
|
||||||
|
timer.stop();
|
||||||
|
|
||||||
|
std::cout << "Output #vertices: " << points.size() << std::endl;
|
||||||
|
std::cout << "Output #triangles: " << triangles.size() << std::endl;
|
||||||
|
std::cout << "Elapsed time: " << timer.time() << " seconds" << std::endl;
|
||||||
|
CGAL::IO::write_polygon_soup("dual_contouring_implicit_parallel.off", points, triangles);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DC sequential
|
||||||
|
{
|
||||||
|
using Domain = CGAL::Isosurfacing::Dual_contouring_domain_3<Grid, Values, Gradients>;
|
||||||
|
Domain domain { grid, values, gradients };
|
||||||
|
|
||||||
|
CGAL::Real_timer timer;
|
||||||
|
timer.start();
|
||||||
|
|
||||||
|
Point_range points;
|
||||||
|
Polygon_range triangles;
|
||||||
|
|
||||||
|
std::cout << "--- Dual Contouring (Sequential)" << std::endl;
|
||||||
|
IS::dual_contouring<CGAL::Sequential_tag>(
|
||||||
|
domain, isovalue, points, triangles, CGAL::parameters::do_not_triangulate_faces(!triangulate_faces));
|
||||||
|
|
||||||
|
timer.stop();
|
||||||
|
|
||||||
|
std::cout << "Output #vertices: " << points.size() << std::endl;
|
||||||
|
std::cout << "Output #triangles: " << triangles.size() << std::endl;
|
||||||
|
std::cout << "Elapsed time: " << timer.time() << " seconds" << std::endl;
|
||||||
|
CGAL::IO::write_polygon_soup("dual_contouring_implicit_sequential.off", points, triangles);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TMC parallel
|
||||||
|
{
|
||||||
|
using Domain = CGAL::Isosurfacing::Marching_cubes_domain_3<Grid, Values>;
|
||||||
|
Domain domain { grid, values };
|
||||||
|
|
||||||
|
CGAL::Real_timer timer;
|
||||||
|
timer.start();
|
||||||
|
|
||||||
|
Point_range points;
|
||||||
|
Polygon_range triangles;
|
||||||
|
|
||||||
|
std::cout << "--- TC Marching Cubes (Parallel)" << std::endl;
|
||||||
|
IS::marching_cubes<CGAL::Parallel_tag>(domain, isovalue, points, triangles);
|
||||||
|
|
||||||
|
timer.stop();
|
||||||
|
|
||||||
|
std::cout << "Output #vertices: " << points.size() << std::endl;
|
||||||
|
std::cout << "Output #triangles: " << triangles.size() << std::endl;
|
||||||
|
std::cout << "Elapsed time: " << timer.time() << " seconds" << std::endl;
|
||||||
|
CGAL::IO::write_polygon_soup("toco_marching_cubes_implicit_parallel.off", points, triangles);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TMC Sequential
|
||||||
|
{
|
||||||
|
using Domain = CGAL::Isosurfacing::Marching_cubes_domain_3<Grid, Values>;
|
||||||
|
Domain domain { grid, values };
|
||||||
|
|
||||||
|
CGAL::Real_timer timer;
|
||||||
|
timer.start();
|
||||||
|
|
||||||
|
Point_range points;
|
||||||
|
Polygon_range triangles;
|
||||||
|
|
||||||
|
std::cout << "--- TC Marching Cubes (Sequential)" << std::endl;
|
||||||
|
IS::marching_cubes<CGAL::Sequential_tag>(domain, isovalue, points, triangles);
|
||||||
|
|
||||||
|
timer.stop();
|
||||||
|
|
||||||
|
std::cout << "Output #vertices: " << points.size() << std::endl;
|
||||||
|
std::cout << "Output #triangles: " << triangles.size() << std::endl;
|
||||||
|
std::cout << "Elapsed time: " << timer.time() << " seconds" << std::endl;
|
||||||
|
CGAL::IO::write_polygon_soup("toco_marching_cubes_implicit_sequential.off", points, triangles);
|
||||||
|
}
|
||||||
|
|
||||||
|
// MC parallel
|
||||||
|
{
|
||||||
|
using Domain = CGAL::Isosurfacing::Marching_cubes_domain_3<Grid, Values>;
|
||||||
|
Domain domain { grid, values };
|
||||||
|
|
||||||
|
CGAL::Real_timer timer;
|
||||||
|
timer.start();
|
||||||
|
|
||||||
|
Point_range points;
|
||||||
|
Polygon_range triangles;
|
||||||
|
|
||||||
|
std::cout << "--- Marching Cubes (Parallel)" << std::endl;
|
||||||
|
IS::marching_cubes<CGAL::Parallel_tag>(domain, isovalue, points, triangles,
|
||||||
|
CGAL::parameters::use_topologically_correct_marching_cubes(false));
|
||||||
|
|
||||||
|
timer.stop();
|
||||||
|
|
||||||
|
std::cout << "Output #vertices: " << points.size() << std::endl;
|
||||||
|
std::cout << "Output #triangles: " << triangles.size() << std::endl;
|
||||||
|
std::cout << "Elapsed time: " << timer.time() << " seconds" << std::endl;
|
||||||
|
CGAL::IO::write_polygon_soup("marching_cubes_implicit_parallel.off", points, triangles);
|
||||||
|
}
|
||||||
|
|
||||||
|
// MC Sequential
|
||||||
|
{
|
||||||
|
using Domain = CGAL::Isosurfacing::Marching_cubes_domain_3<Grid, Values>;
|
||||||
|
Domain domain { grid, values };
|
||||||
|
|
||||||
|
CGAL::Real_timer timer;
|
||||||
|
timer.start();
|
||||||
|
|
||||||
|
Point_range points;
|
||||||
|
Polygon_range triangles;
|
||||||
|
|
||||||
|
std::cout << "--- Marching Cubes (Sequential)" << std::endl;
|
||||||
|
IS::marching_cubes<CGAL::Sequential_tag>(domain, isovalue, points, triangles,
|
||||||
|
CGAL::parameters::use_topologically_correct_marching_cubes(false));
|
||||||
|
|
||||||
|
timer.stop();
|
||||||
|
|
||||||
|
std::cout << "Output #vertices: " << points.size() << std::endl;
|
||||||
|
std::cout << "Output #triangles: " << triangles.size() << std::endl;
|
||||||
|
std::cout << "Elapsed time: " << timer.time() << " seconds" << std::endl;
|
||||||
|
CGAL::IO::write_polygon_soup("marching_cubes_implicit_sequential.off", points, triangles);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Done" << std::endl;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
import numpy as np
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
|
||||||
|
def save_svg(file):
|
||||||
|
fig = plt.gcf()
|
||||||
|
fig.set_size_inches((10, 5), forward=False)
|
||||||
|
plt.savefig(file, bbox_inches="tight")
|
||||||
|
|
||||||
|
|
||||||
|
def add_threads_graph(data, label):
|
||||||
|
x = data["threads"]
|
||||||
|
y = data["cells"] / data["time"] / 10 ** 3
|
||||||
|
plt.plot(x, y, label=label)
|
||||||
|
plt.legend()
|
||||||
|
|
||||||
|
|
||||||
|
def add_size_graph(data, label):
|
||||||
|
x = data["cells"]
|
||||||
|
y = data["cells"] / data["time"] / 10 ** 3
|
||||||
|
plt.plot(x, y, label=label)
|
||||||
|
plt.legend()
|
||||||
|
|
||||||
|
|
||||||
|
def add_triangle_graph(data, label, factor):
|
||||||
|
x = data["cells"]
|
||||||
|
y = data["polygons"] * factor
|
||||||
|
plt.plot(x, y, label=label)
|
||||||
|
plt.legend()
|
||||||
|
|
||||||
|
|
||||||
|
def plot_graph(file, name, log, ylabel, xlabel):
|
||||||
|
plt.title(name)
|
||||||
|
plt.xlabel(xlabel)
|
||||||
|
plt.ylabel(ylabel)
|
||||||
|
if log:
|
||||||
|
plt.xscale("log")
|
||||||
|
plt.gca().yaxis.grid(color='#cccccc')
|
||||||
|
plt.gca().xaxis.grid(color='#cccccc')
|
||||||
|
plt.ylim(ymin=0)
|
||||||
|
save_svg(file)
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
|
latex_export = True
|
||||||
|
if latex_export:
|
||||||
|
#plt.rcParams["svg.fonttype"] = "none"
|
||||||
|
plt.rcParams["axes.unicode_minus"] = False
|
||||||
|
plt.rcParams['font.size'] = "17"
|
||||||
|
|
||||||
|
|
||||||
|
data = pd.read_csv("implicit_iwp_mc_1.csv")
|
||||||
|
add_threads_graph(data, "MC")
|
||||||
|
|
||||||
|
#data = pd.read_csv("threads_grid.csv")
|
||||||
|
#add_threads_graph(data, "grid")
|
||||||
|
|
||||||
|
xt = np.arange(0, min(max(data["threads"]), 9) + 0.1, 2)
|
||||||
|
if max(data["threads"]) > 10:
|
||||||
|
print("more")
|
||||||
|
xt = np.concatenate((xt, np.arange(10, max(data["threads"]) + 0.1, 2)))
|
||||||
|
|
||||||
|
yt = np.arange(0, 20 + 0.1, 2)
|
||||||
|
|
||||||
|
print(xt)
|
||||||
|
plt.xticks(xt)
|
||||||
|
plt.yticks(yt)
|
||||||
|
plot_graph("perf_threads.svg", "", False, "performance [10^3 cubes/s]", "cores")
|
||||||
|
|
||||||
|
|
||||||
|
data = pd.read_csv("size_iwp_mc.csv")
|
||||||
|
add_size_graph(data, "MC")
|
||||||
|
|
||||||
|
data = pd.read_csv("size_iwp_dc.csv")
|
||||||
|
add_size_graph(data, "DC")
|
||||||
|
|
||||||
|
plot_graph("perf_size.svg", "", False, "performance [10^3 cubes/s]", "cells")
|
||||||
|
|
||||||
|
data = pd.read_csv("size_iwp_mc.csv")
|
||||||
|
add_triangle_graph(data, "MC", 1)
|
||||||
|
|
||||||
|
data = pd.read_csv("size_iwp_dc.csv")
|
||||||
|
add_triangle_graph(data, "DC", 1)
|
||||||
|
|
||||||
|
plot_graph("triangles_size.svg", "", False, "triangles", "cells")
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*!
|
||||||
|
\ingroup PkgIsosurfacing3Concepts
|
||||||
|
|
||||||
|
\cgalConcept
|
||||||
|
|
||||||
|
\cgalRefines{IsosurfacingDomain_3}
|
||||||
|
|
||||||
|
\brief The concept `IsosurfacingDomainWithGradient_3` describes the set of requirements to be
|
||||||
|
fulfilled by any class used as input data for some isosurfacing algorithms.
|
||||||
|
|
||||||
|
This concept refines `IsosurfacingDomain_3` to add a `gradient()` function which is used
|
||||||
|
by isosurfacing domains to query the domain for the gradient of the values field
|
||||||
|
at a 3D query point (not necessarily a vertex) in space.
|
||||||
|
|
||||||
|
\cgalHasModelsBegin
|
||||||
|
\cgalHasModels{CGAL::Isosurfacing::Dual_contouring_domain_3}
|
||||||
|
\cgalHasModelsEnd
|
||||||
|
*/
|
||||||
|
class IsosurfacingDomainWithGradient_3
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// \name Types
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/*!
|
||||||
|
The geometric traits type.
|
||||||
|
Must be a model of `IsosurfacingTraits_3`.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type Geom_traits;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
The 3D point type.
|
||||||
|
*/
|
||||||
|
typedef Geom_traits::Point_3 Point_3;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
The 3D vector type.
|
||||||
|
*/
|
||||||
|
typedef Geom_traits::Vector_3 Vector_3;
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
/// \name Operations
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/*!
|
||||||
|
returns the gradient at the point `p`.
|
||||||
|
*/
|
||||||
|
Vector_3 gradient(Point_3 p) const;
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,185 @@
|
||||||
|
/*!
|
||||||
|
\ingroup PkgIsosurfacing3Concepts
|
||||||
|
|
||||||
|
\cgalConcept
|
||||||
|
|
||||||
|
\brief The concept `IsosurfacingDomain_3` describes the set of requirements to be
|
||||||
|
fulfilled by any class used as input data for isosurfacing algorithms.
|
||||||
|
|
||||||
|
A model of the concept `IsosurfacingDomain_3` provides a partition of the Euclidean space in cells,
|
||||||
|
and a scalar field defined over the whole partition.
|
||||||
|
The isosurfacing algorithms traverse these cells and query the domain class
|
||||||
|
at the vertices of each cell, using the functions `point()` and `value()`.
|
||||||
|
|
||||||
|
\cgalHasModelsBegin
|
||||||
|
\cgalHasModels{CGAL::Isosurfacing::Marching_cubes_domain_3}
|
||||||
|
\cgalHasModels{CGAL::Isosurfacing::Dual_contouring_domain_3}
|
||||||
|
\cgalHasModelsEnd
|
||||||
|
|
||||||
|
\sa `IsosurfacingDomainWithGradient_3`
|
||||||
|
*/
|
||||||
|
class IsosurfacingDomain_3
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// \name Types
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/*!
|
||||||
|
The geometric traits type.
|
||||||
|
Must be a model of `IsosurfacingTraits_3`.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type Geom_traits;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
The scalar type.
|
||||||
|
*/
|
||||||
|
typedef Geom_traits::FT FT;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
The 3D point type.
|
||||||
|
*/
|
||||||
|
typedef Geom_traits::Point_3 Point_3;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
A descriptor that uniquely identifies a vertex.
|
||||||
|
Must be a model of the concepts `Descriptor` and `Hashable`.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type vertex_descriptor;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
A descriptor that uniquely identifies an edge.
|
||||||
|
Must be a model of the concept `Descriptor` and `Hashable`.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type edge_descriptor;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
A descriptor that uniquely identifies a cell.
|
||||||
|
Must be a model of the concepts `Descriptor` and `Hashable`.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type cell_descriptor;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
A container for the two vertices of an edge.
|
||||||
|
Must be a model of the concept `RandomAccessContainer` of size `2` whose value type is `vertex_descriptor`.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type Edge_vertices;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
A container for the cells incident to an edge.
|
||||||
|
Must be a model of the concept `ForwardRange` whose value type is `cell_descriptor`.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type Cells_incident_to_edge;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
A container for the vertices of a cell.
|
||||||
|
Must be a model of the concept `ForwardRange` whose value type is `vertex_descriptor`.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type Cell_vertices;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
A container for the edges of a cell.
|
||||||
|
Must be a model of the concept `ForwardRange` whose value type is `edge_descriptor`.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type Cell_edges;
|
||||||
|
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
/// \name Operations
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/*!
|
||||||
|
returns the geometric traits.
|
||||||
|
*/
|
||||||
|
Geom_traits geom_traits();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
returns the 3D location of the vertex `v`.
|
||||||
|
*/
|
||||||
|
Point_3 point(vertex_descriptor v) const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
returns the value of the value field at the point `p`.
|
||||||
|
*/
|
||||||
|
FT value(Point_3 p) const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
returns the value of the value field at the vertex `v`.
|
||||||
|
*/
|
||||||
|
FT value(vertex_descriptor v) const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
returns the two vertices incident to the edge `e`.
|
||||||
|
*/
|
||||||
|
Edge_vertices incident_vertices(edge_descriptor e) const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
returns all the cells incident to the edge `e`, in a clockwise or counterclockwise order.
|
||||||
|
*/
|
||||||
|
Cells_incident_to_edge incident_cells(edge_descriptor e) const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
returns all the vertices of the cell `c`.
|
||||||
|
*/
|
||||||
|
Cell_vertices cell_vertices(cell_descriptor c) const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
returns all the edges of the cell `c`.
|
||||||
|
*/
|
||||||
|
Cell_edges cell_edges(cell_descriptor c) const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
iterates over all vertices, and calls the functor `f` on each one.
|
||||||
|
|
||||||
|
\tparam ConcurrencyTag decides if the vertices are iterated sequentially or in parallel.
|
||||||
|
Can be either `CGAL::Sequential_tag`, `CGAL::Parallel_if_available_tag`, or `CGAL::Parallel_tag`.
|
||||||
|
\tparam Functor must implement `void operator()(vertex_descriptor vertex)`
|
||||||
|
|
||||||
|
\param f the functor called on every vertex
|
||||||
|
*/
|
||||||
|
template <typename ConcurrencyTag, typename Functor>
|
||||||
|
void for_each_vertex(Functor& f) const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
iterates over all edges, and calls the functor `f` on each one.
|
||||||
|
|
||||||
|
\tparam ConcurrencyTag decides if the edges are iterated sequentially or in parallel.
|
||||||
|
Can be either `CGAL::Sequential_tag`, `CGAL::Parallel_if_available_tag`, or `CGAL::Parallel_tag`.
|
||||||
|
\tparam Functor must implement `void operator()(edge_descriptor edge)`.
|
||||||
|
|
||||||
|
\param f the functor called on every edge
|
||||||
|
*/
|
||||||
|
template <typename ConcurrencyTag, typename Functor>
|
||||||
|
void for_each_edge(Functor& f) const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
iterates over all cells, and calls the functor `f` on each one.
|
||||||
|
|
||||||
|
\tparam ConcurrencyTag decides if the cells are iterated sequentially or in parallel.
|
||||||
|
Can be either `CGAL::Sequential_tag`, `CGAL::Parallel_if_available_tag`, or `CGAL::Parallel_tag`.
|
||||||
|
\tparam Functor must implement `void operator()(cell_descriptor cell)`.
|
||||||
|
|
||||||
|
\param f the functor called on every cell
|
||||||
|
*/
|
||||||
|
template <typename ConcurrencyTag, typename Functor>
|
||||||
|
void for_each_cell(Functor& f) const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Constructs the intersection - if it exists - between an edge and an isosurface.
|
||||||
|
|
||||||
|
\param p_0 the location of the first vertex of the edge
|
||||||
|
\param p_1 the location of the second vertex of the edge
|
||||||
|
\param val_0 the value at the first vertex of the edge
|
||||||
|
\param val_1 the value at the second vertex of the edge
|
||||||
|
\param isovalue the isovalue defining the isosurface with which we seek an intersection
|
||||||
|
\param p the intersection point, if it exists
|
||||||
|
|
||||||
|
\returns `true` if the intersection point exists, `false` otherwise.
|
||||||
|
*/
|
||||||
|
bool construct_intersection(Point_3 p_0, Point_3 p_1,
|
||||||
|
FT val_0, FT val_1,
|
||||||
|
FT isovalue,
|
||||||
|
Point_3& p) const;
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*!
|
||||||
|
\ingroup PkgIsosurfacing3Concepts
|
||||||
|
|
||||||
|
\cgalConcept
|
||||||
|
|
||||||
|
\cgalRefines{DefaultConstructible, Assignable}
|
||||||
|
|
||||||
|
The concept `IsosurfacingEdgeIntersectionOracle_3` describes the requirements for the edge-isosurface
|
||||||
|
intersection oracle template parameter of the domain classes
|
||||||
|
`CGAL::Isosurfacing::Marching_cubes_domain_3` and
|
||||||
|
`CGAL::Isosurfacing::Dual_contouring_domain_3`.
|
||||||
|
|
||||||
|
\cgalHasModelsBegin
|
||||||
|
\cgalHasModels{CGAL::Isosurfacing::Dichotomy_edge_intersection}
|
||||||
|
\cgalHasModels{CGAL::Isosurfacing::Linear_interpolation_edge_intersection}
|
||||||
|
\cgalHasModelsEnd
|
||||||
|
*/
|
||||||
|
class IsosurfacingEdgeIntersectionOracle_3
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*!
|
||||||
|
* \brief computes the intersection point between an edge and the isosurface.
|
||||||
|
*
|
||||||
|
* \tparam Domain must be a model of `IsosurfacingDomain_3`
|
||||||
|
*
|
||||||
|
* \param p_0 the location of the first vertex of the edge
|
||||||
|
* \param p_1 the location of the second vertex of the edge
|
||||||
|
* \param val_0 the value at the first vertex of the edge
|
||||||
|
* \param val_1 the value at the second vertex of the edge
|
||||||
|
* \param domain the isosurfacing domain
|
||||||
|
* \param isovalue the isovalue defining the isosurface with which we seek an intersection
|
||||||
|
* \param p the intersection point, if it exists
|
||||||
|
*
|
||||||
|
* \returns `true` if the intersection point exists, `false` otherwise.
|
||||||
|
*/
|
||||||
|
template <typename Domain>
|
||||||
|
bool operator()(typename Domain::Geom_traits::Point_3 p_0,
|
||||||
|
typename Domain::Geom_traits::Point_3 p_1,
|
||||||
|
typename Domain::Geom_traits::FT val_0,
|
||||||
|
typename Domain::Geom_traits::FT val_1,
|
||||||
|
Domain domain,
|
||||||
|
typename Domain::Geom_traits::FT isovalue,
|
||||||
|
typename Domain::Geom_traits::Point_3& p) const;
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*!
|
||||||
|
\ingroup PkgIsosurfacing3Concepts
|
||||||
|
|
||||||
|
\cgalConcept
|
||||||
|
|
||||||
|
The concept `IsosurfacingGradientField_3` describes the set of requirements to be fulfilled
|
||||||
|
by the gradient field template parameter of the domain class `CGAL::Isosurfacing::Dual_contouring_domain_3`.
|
||||||
|
|
||||||
|
Gradient fields must be continuous and defined over the geometric span of the
|
||||||
|
space partitioning data structure (also known as "partition") being used.
|
||||||
|
|
||||||
|
\cgalHasModelsBegin
|
||||||
|
\cgalHasModels{CGAL::Isosurfacing::Gradient_function_3}
|
||||||
|
\cgalHasModels{CGAL::Isosurfacing::Finite_difference_gradient_3}
|
||||||
|
\cgalHasModels{CGAL::Isosurfacing::Interpolated_discrete_gradients_3}
|
||||||
|
\cgalHasModelsEnd
|
||||||
|
|
||||||
|
\sa `IsosurfacingTraits_3`
|
||||||
|
\sa `IsosurfacingValueField_3`
|
||||||
|
*/
|
||||||
|
class IsosurfacingGradientField_3
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*!
|
||||||
|
* The 3D point type.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type Point_3;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* The 3D vector type.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type Vector_3;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
returns the gradient at the point `p`.
|
||||||
|
*/
|
||||||
|
Vector_3 operator()(Point_3 p) const;
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*!
|
||||||
|
\ingroup PkgIsosurfacing3Concepts
|
||||||
|
|
||||||
|
\cgalConcept
|
||||||
|
|
||||||
|
\cgalRefines{DefaultConstructible, Assignable}
|
||||||
|
|
||||||
|
The concept `IsosurfacingInterpolationScheme_3` describes the set of requirements to be fulfilled
|
||||||
|
by the interpolation scheme template parameter of the domain classes `CGAL::Isosurfacing::Interpolated_discrete_values_3` and `CGAL::Isosurfacing::Interpolated_discrete_gradients_3`.
|
||||||
|
|
||||||
|
\cgalHasModelsBegin
|
||||||
|
\cgalHasModels{CGAL::Isosurfacing::Trilinear_interpolation}
|
||||||
|
\cgalHasModelsEnd
|
||||||
|
*/
|
||||||
|
class IsosurfacingInterpolationScheme_3
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*!
|
||||||
|
* The geometric traits type.
|
||||||
|
* Must be a model of `IsosurfacingTraits_3`.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type Geom_traits;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* The scalar type.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type FT;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* The 3D point type.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type Point_3;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* The 3D vector type.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type Vector_3;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief interpolates the value of the value field at the point `p` using values defined by `vr`
|
||||||
|
* over the grid `g`.
|
||||||
|
*
|
||||||
|
* \tparam Grid must be `CGAL::Isosurfacing::Cartesian_grid_3`
|
||||||
|
* \tparam ValueRange must be a model of `RandomAccessRange` with `FT` as value type
|
||||||
|
*/
|
||||||
|
template <typename Grid, typename ValueRange>
|
||||||
|
FT interpolated_value(Point_3 p, Grid g, ValueRange vr) const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief interpolates the gradient of the gradient field at the point `p`
|
||||||
|
* using gradients defined by `gr` over the grid `g`.
|
||||||
|
*
|
||||||
|
* \tparam Grid must be `CGAL::Isosurfacing::Cartesian_grid_3`
|
||||||
|
* \tparam GradientRange must be a model of `RandomAccessRange` with `Vector_3` as value type
|
||||||
|
*/
|
||||||
|
template <typename Grid, typename GradientRange>
|
||||||
|
Vector_3 interpolated_gradient(Point_3 p, Grid g, GradientRange gr) const;
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*!
|
||||||
|
\ingroup PkgIsosurfacing3Concepts
|
||||||
|
|
||||||
|
\cgalConcept
|
||||||
|
|
||||||
|
The concept `IsosurfacingPartition_3` describes the set of requirements to be fulfilled
|
||||||
|
by the partition template parameter of the domain classes `CGAL::Isosurfacing::Marching_cubes_domain_3`
|
||||||
|
and `CGAL::Isosurfacing::Dual_contouring_domain_3`.
|
||||||
|
|
||||||
|
A 3D partition is a space partitioning data structure that provides a discrete representation
|
||||||
|
of a subset of 3D space.
|
||||||
|
|
||||||
|
A partial specialization of `CGAL::Isosurfacing::partition_traits` must be provided for all models.
|
||||||
|
This is similar to graph traits in \ref PkgBGL.
|
||||||
|
|
||||||
|
\cgalHasModelsBegin
|
||||||
|
\cgalHasModels{`CGAL::Isosurfacing::Cartesian_grid_3`}
|
||||||
|
\cgalHasModels{`CGAL::Octree`}
|
||||||
|
\cgalHasModelsEnd
|
||||||
|
*/
|
||||||
|
class IsosurfacingPartition_3
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*!
|
||||||
|
* The geometric traits type.
|
||||||
|
* Must be a model of `IsosurfacingTraits_3`.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type Geom_traits;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \returns the geometric traits.
|
||||||
|
*/
|
||||||
|
Geom_traits geom_traits();
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,165 @@
|
||||||
|
/*!
|
||||||
|
\ingroup PkgIsosurfacing3Concepts
|
||||||
|
|
||||||
|
\cgalConcept
|
||||||
|
|
||||||
|
\cgalRefines{DefaultConstructible, Assignable}
|
||||||
|
|
||||||
|
The concept `IsosurfacingTraits_3` describes the set of requirements to be
|
||||||
|
fulfilled by the traits class of a model of `IsosurfacingDomain_3`.
|
||||||
|
|
||||||
|
\cgalHasModelsBegin
|
||||||
|
\cgalHasModelsBare{All models of the concept `Kernel`}
|
||||||
|
\cgalHasModelsEnd
|
||||||
|
*/
|
||||||
|
class IsosurfacingTraits_3
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// \name Types
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/*!
|
||||||
|
The scalar type.
|
||||||
|
Must be a model of `FieldNumberType`.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type FT;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
The 3D point type.
|
||||||
|
Must be a model of `Kernel::Point_3`.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type Point_3;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
The 3D vector type.
|
||||||
|
Must be a model of `Kernel::Vector_3`.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type Vector_3;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
The 3D cuboid type.
|
||||||
|
Must be a model of `Kernel::IsoCuboid_3`
|
||||||
|
*/
|
||||||
|
typedef unspecified_type IsoCuboid_3;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
A construction object that must provide the function operators:
|
||||||
|
|
||||||
|
`FT operator()(Point_3 p)`
|
||||||
|
|
||||||
|
and
|
||||||
|
|
||||||
|
`FT operator()(Vector_3 p)`
|
||||||
|
|
||||||
|
which return the \f$ x\f$-coordinate of the point and the vector, respectively.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type Compute_x_3;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
A construction object that must provide the function operators:
|
||||||
|
|
||||||
|
`FT operator()(Point_3 p)`
|
||||||
|
|
||||||
|
and
|
||||||
|
|
||||||
|
`FT operator()(Vector_3 p)`
|
||||||
|
|
||||||
|
which return the \f$ y\f$-coordinate of the point and the vector, respectively.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type Compute_y_3;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
A construction object that must provide the function operators:
|
||||||
|
|
||||||
|
`FT operator()(Point_3 p)`
|
||||||
|
|
||||||
|
and
|
||||||
|
|
||||||
|
`FT operator()(Vector_3 p)`
|
||||||
|
|
||||||
|
which return the \f$ z\f$-coordinate of the point and the vector, respectively.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type Compute_z_3;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
A construction object that must provide the function operator:
|
||||||
|
|
||||||
|
`Point_3 operator()(FT x, FT y, FT z)`
|
||||||
|
|
||||||
|
which constructs a 3D point from its three coordinates.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type Construct_point_3;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
A construction object that must provide the function operator:
|
||||||
|
|
||||||
|
`Vector_3 operator()(FT x, FT y, FT z)`
|
||||||
|
|
||||||
|
which constructs a 3D vector from its three coordinates.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type Construct_vector_3;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
A construction object that must provide the function operator:
|
||||||
|
|
||||||
|
`IsoCuboid_3 operator()(Point_3 p, Point_3 q)`
|
||||||
|
|
||||||
|
which constructs an iso-oriented cuboid with diagonal opposite vertices `p` and `q`
|
||||||
|
such that `p` is the lexicographically smallest point in the cuboid.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type Construct_iso_cuboid_3;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
A construction object that must provide the function operator:
|
||||||
|
|
||||||
|
`Point_3 operator()(IsoCuboid_3 c, int i)`
|
||||||
|
|
||||||
|
which returns the i-th vertex of an iso-cuboid `c`. See `Kernel::ConstructVertex_3`
|
||||||
|
for the order of vertices.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type Construct_vertex_3;
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
/// \name Operations
|
||||||
|
/// The following functions give access to the predicate and construction objects:
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/*!
|
||||||
|
|
||||||
|
*/
|
||||||
|
Compute_x_3 compute_x_3_object();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
|
||||||
|
*/
|
||||||
|
Compute_y_3 compute_y_3_object();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
|
||||||
|
*/
|
||||||
|
Compute_z_3 compute_z_3_object();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
|
||||||
|
*/
|
||||||
|
Construct_point_3 construct_point_3_object();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
|
||||||
|
*/
|
||||||
|
Construct_vector_3 construct_vector_3_object();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
|
||||||
|
*/
|
||||||
|
Construct_iso_cuboid_3 construct_iso_cuboid_3_object();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
|
||||||
|
*/
|
||||||
|
Construct_vertex_3 construct_vertex_3_object();
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*!
|
||||||
|
\ingroup PkgIsosurfacing3Concepts
|
||||||
|
|
||||||
|
\cgalConcept
|
||||||
|
|
||||||
|
The concept `IsosurfacingValueField_3` describes the set of requirements to be fulfilled
|
||||||
|
by the value field template parameter of the domain classes `CGAL::Isosurfacing::Marching_cubes_domain_3` and `CGAL::Isosurfacing::Dual_contouring_domain_3`.
|
||||||
|
|
||||||
|
Value fields must be continuous and defined over the geometric span of the
|
||||||
|
space partitioning data structure (also known as "partition") being used.
|
||||||
|
|
||||||
|
\cgalHasModelsBegin
|
||||||
|
\cgalHasModels{CGAL::Isosurfacing::Value_function_3}
|
||||||
|
\cgalHasModels{CGAL::Isosurfacing::Interpolated_discrete_values_3}
|
||||||
|
\cgalHasModelsEnd
|
||||||
|
|
||||||
|
\sa `IsosurfacingTraits_3`
|
||||||
|
\sa `IsosurfacingGradientField_3`
|
||||||
|
*/
|
||||||
|
class IsosurfacingValueField_3
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*!
|
||||||
|
* The scalar type.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type FT;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* The 3D point type.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type Point_3;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* A descriptor that uniquely identifies a vertex (see `IsosurfacingPartition_3`).
|
||||||
|
*/
|
||||||
|
typedef unspecified_type vertex_descriptor;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
returns the value of the field at the point `p`.
|
||||||
|
*/
|
||||||
|
FT operator()(Point_3 p);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
returns the value of the field at the vertex `v`.
|
||||||
|
*/
|
||||||
|
FT operator()(vertex_descriptor v);
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,126 @@
|
||||||
|
namespace CGAL {
|
||||||
|
namespace Isosurfacing {
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \ingroup PkgIsosurfacing3Concepts
|
||||||
|
*
|
||||||
|
* \relates IsosurfacingPartition_3
|
||||||
|
*
|
||||||
|
* The class `partition_traits` is the API compatibility layer between a model of `IsosurfacingPartition_3`
|
||||||
|
* and the isosurfacing domain classes `CGAL::Isosurfacing::Marching_cubes_domain_3` and
|
||||||
|
* `CGAL::Isosurfacing::Dual_contouring_domain_3`.
|
||||||
|
*
|
||||||
|
* For each model of `IsosurfacingPartition_3`, a partial specialization of `partition_traits` must be provided,
|
||||||
|
* providing the types and functions listed below. Such a partial specialization is provided
|
||||||
|
* for `CGAL::Isosurfacing::Cartesian_grid_3`.
|
||||||
|
*/
|
||||||
|
template <typename IsosurfacingPartition_3>
|
||||||
|
class partition_traits
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*!
|
||||||
|
* A vertex descriptor corresponds to a unique vertex in an abstract partition instance.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type vertex_descriptor;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* An edge descriptor corresponds to a unique edge in an abstract partition instance.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type edge_descriptor;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* A cell descriptor corresponds to a unique edge in an abstract partition instance.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type cell_descriptor;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* A container for the two vertices of an edge.
|
||||||
|
* Must be a model of `RandomAccessContainer` whose `value_type` must be `vertex_descriptor`.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type Edge_vertices;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* A container for the cells incident to an edge.
|
||||||
|
* Must be a model of `ForwardRange` whose `value_type` must be `cell_descriptor`.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type Cells_incident_to_edge;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* A container for the vertices of a cell.
|
||||||
|
* Must be a model of `ForwardRange` whose `value_type` must be `vertex_descriptor`.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type Cell_vertices;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* A container for the edges of a cell.
|
||||||
|
* Must be a model of `ForwardRange` whose `value_type` must be `edge_descriptor`.
|
||||||
|
*/
|
||||||
|
typedef unspecified_type Cell_edges;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \returns the 3D location of the vertex `v`.
|
||||||
|
*/
|
||||||
|
static Point_3 point(vertex_descriptor v, IsosurfacingPartition_3 partition);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \returns the two vertices incident to the edge `e`.
|
||||||
|
*/
|
||||||
|
static Edge_vertices incident_vertices(edge_descriptor e, IsosurfacingPartition_3 partition);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \returns all the cells incident to the edge `e`, in a geometrically ordered manner around the edge.
|
||||||
|
*/
|
||||||
|
static Cells_incident_to_edge incident_cells(edge_descriptor e, IsosurfacingPartition_3 partition);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \returns all the vertices of the cell `c`.
|
||||||
|
*/
|
||||||
|
static Cell_vertices cell_vertices(cell_descriptor c, IsosurfacingPartition_3 partition);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \returns all the edges of the cell `c`.
|
||||||
|
*/
|
||||||
|
static Cell_edges cell_edges(cell_descriptor c, IsosurfacingPartition_3 partition);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* iterates over all vertices, and calls the functor `f` on each one.
|
||||||
|
*
|
||||||
|
* \tparam ConcurrencyTag decides if the vertices are iterated sequentially or in parallel.
|
||||||
|
* Can be either `CGAL::Sequential_tag`, `CGAL::Parallel_if_available_tag`, or `CGAL::Parallel_tag`.
|
||||||
|
* \tparam Functor must implement `void operator()(vertex_descriptor vertex)`
|
||||||
|
*
|
||||||
|
* \param f the functor called on every vertex
|
||||||
|
* \param partition the partition whose vertices are being iterated over
|
||||||
|
*/
|
||||||
|
template <typename ConcurrencyTag, typename Functor>
|
||||||
|
static void for_each_vertex(Functor& f, IsosurfacingPartition_3 partition);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* iterates over all edges, and calls the functor `f` on each one.
|
||||||
|
*
|
||||||
|
* \tparam ConcurrencyTag decides if the edges are iterated sequentially or in parallel.
|
||||||
|
* Can be either `CGAL::Sequential_tag`, `CGAL::Parallel_if_available_tag`, or `CGAL::Parallel_tag`.
|
||||||
|
* \tparam Functor must implement `void operator()(edge_descriptor edge)`.
|
||||||
|
*
|
||||||
|
* \param f the functor called on every edge
|
||||||
|
* \param partition the partition whose edges are being iterated over
|
||||||
|
*/
|
||||||
|
template <typename ConcurrencyTag, typename Functor>
|
||||||
|
static void for_each_edge(Functor& f, IsosurfacingPartition_3 partition);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* iterates over all cells, and calls the functor `f` on each one.
|
||||||
|
*
|
||||||
|
* \tparam ConcurrencyTag decides if the cells are iterated sequentially or in parallel.
|
||||||
|
* Can be either `CGAL::Sequential_tag`, `CGAL::Parallel_if_available_tag`, or `CGAL::Parallel_tag`.
|
||||||
|
* \tparam Functor must implement `void operator()(cell_descriptor cell)`.
|
||||||
|
*
|
||||||
|
* \param f the functor called on every cell
|
||||||
|
* \param partition the partition whose cells are being iterated over
|
||||||
|
*/
|
||||||
|
template <typename ConcurrencyTag, typename Functor>
|
||||||
|
static void for_each_cell(Functor& f, IsosurfacingPartition_3 partition);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Isosurfacing
|
||||||
|
} // namespace CGAL
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
@INCLUDE = ${CGAL_DOC_PACKAGE_DEFAULTS}
|
||||||
|
|
||||||
|
PROJECT_NAME = "CGAL ${CGAL_DOC_VERSION} - 3D Isosurfacing"
|
||||||
|
|
||||||
|
HTML_EXTRA_FILES = ${CGAL_PACKAGE_DOC_DIR}/fig/isosurfacing_teaser.png \
|
||||||
|
${CGAL_PACKAGE_DOC_DIR}/fig/MC_cases.png \
|
||||||
|
${CGAL_PACKAGE_DOC_DIR}/fig/MC_DC.png \
|
||||||
|
${CGAL_PACKAGE_DOC_DIR}/fig/MC_DC_open.png \
|
||||||
|
${CGAL_PACKAGE_DOC_DIR}/fig/DC.png \
|
||||||
|
${CGAL_PACKAGE_DOC_DIR}/fig/MC.png \
|
||||||
|
${CGAL_PACKAGE_DOC_DIR}/fig/isosurfacing_inrimage.png \
|
||||||
|
${CGAL_PACKAGE_DOC_DIR}/fig/MC_TMC.png \
|
||||||
|
${CGAL_PACKAGE_DOC_DIR}/fig/MC_octree.png \
|
||||||
|
${CGAL_PACKAGE_DOC_DIR}/fig/MC_DC_performance.png
|
||||||
|
|
@ -0,0 +1,370 @@
|
||||||
|
namespace CGAL {
|
||||||
|
/*!
|
||||||
|
|
||||||
|
\mainpage User Manual
|
||||||
|
\anchor Chapter_Isosurfacing3
|
||||||
|
|
||||||
|
\cgalAutoToc
|
||||||
|
\author Mael Rouxel-Labbé, Julian Stahl, Daniel Zint, and Pierre Alliez
|
||||||
|
|
||||||
|
\cgalFigureAnchor{IsosurfacingTeaser}
|
||||||
|
<center>
|
||||||
|
<img src="isosurfacing_teaser.png" style="max-width:50%;"/>
|
||||||
|
</center>
|
||||||
|
\cgalFigureCaptionBegin{IsosurfacingTeaser}
|
||||||
|
Generating a surface from a 3D gray level image using Marching Cubes (3D input image from qim.dk)
|
||||||
|
\cgalFigureCaptionEnd
|
||||||
|
|
||||||
|
\section SecIsoSurfacingIntroduction Introduction
|
||||||
|
|
||||||
|
Given a field of a scalar values, an isosurface is defined as the locus of points where the field
|
||||||
|
has a given constant value; in other words, it is a level set.
|
||||||
|
This constant value is referred to as the "isovalue", and, for well-behaved fields,
|
||||||
|
the level set forms a surface.
|
||||||
|
In the following, we shall refer to the the field of scalar values as the value field.
|
||||||
|
"Isosurfacing", also known as "isosurface extraction" or "contouring", is the process of constructing
|
||||||
|
the isosurface corresponding to a given value field and isovalue.
|
||||||
|
%Isosurfacing is often needed for volume visualization and for the simulation of physical phenomena.
|
||||||
|
|
||||||
|
This \cgal package provides methods to extract isosurfaces from 3D value fields.
|
||||||
|
These contouring techniques rely on the partition of the 3D space and of the field to construct
|
||||||
|
an approximate representation of the isosurface.
|
||||||
|
The 3D value field can be described through various representations: an implicit function,
|
||||||
|
an interpolated set of discrete sampling values, a 3D image, etc. (see \ref SecIsosurfacingExamples).
|
||||||
|
The isovalue is user-defined.
|
||||||
|
The output is a polygon soup, made either of triangles or quads depending on the method,
|
||||||
|
and may consist of a single connected component, or multiple, disjoint components.
|
||||||
|
Note that due to the inherent approximate nature of these discrete methods that only sample 3D value fields,
|
||||||
|
parts of the "true" isosurface may be missing from the output, and the output may contain artifacts
|
||||||
|
that are not present in the true isosurface.
|
||||||
|
|
||||||
|
\section SecIsosurfacingMethods Isosurfacing Methods
|
||||||
|
|
||||||
|
The scientific literature abounds with algorithms for extracting isosurfaces, each coming
|
||||||
|
with different properties for the output and requirements for the input \cgalCite{cgal:dljjaw-sisp-15}.
|
||||||
|
This package offers the following methods
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>\ref SubSecMarchingCubes : a simple and efficient method that generates a triangle mesh, with almost no guarantees.</li>
|
||||||
|
<li>\ref SubSecTMC : an extension to Marching Cubes that provides additional guarantees for the output.</li>
|
||||||
|
<li>\ref SubSecDualContouring : a method that generates a polygon mesh, with a focus on sharp features recovery.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
\subsection SubSecMarchingCubes Marching Cubes (MC)
|
||||||
|
|
||||||
|
Marching Cubes (MC) \cgalCite{LC87} uses a volumetric grid, i.e., a 3D iso-cuboid partitioned into hexahedral cells.
|
||||||
|
All cells of the grid are processed individually using values of the value field sampled at the grid corners.
|
||||||
|
Each cell corner is assigned a sign (+/-) to indicate whether its value field value is above
|
||||||
|
or below the user-defined isovalue.
|
||||||
|
A vertex is created along each grid edge where a sign change occurs, i.e., where the edge intersects the isosurface.
|
||||||
|
More specifically, the vertex location is computed using linear interpolation of
|
||||||
|
the value field values evaluated at the cell corners forming the edge.
|
||||||
|
These vertices are connected to form triangles within the cell, depending on the configuration
|
||||||
|
of signs at the cell corners. \cgalFigureRef{IsosurfacingMCCases} illustrates the configurations in 2D.
|
||||||
|
In 3D, there are no less than 33 configurations (not shown) \cgalCite{cgal:c-mcctci-95}.
|
||||||
|
|
||||||
|
\cgalFigureAnchor{IsosurfacingMCCases}
|
||||||
|
<center>
|
||||||
|
<img src="MC_cases.png" style="max-width:70%;"/>
|
||||||
|
</center>
|
||||||
|
\cgalFigureCaptionBegin{IsosurfacingMCCases}
|
||||||
|
Examples of some configurations for 2D Marching Cubes.
|
||||||
|
\cgalFigureCaptionEnd
|
||||||
|
|
||||||
|
The implementation within \cgal is generic in the sense that it can process any grid-like data structure
|
||||||
|
that consists of hexahedral cells. When the hexahedral grid is a conforming grid (meaning that the
|
||||||
|
intersection of two hexahedral cells is a face, an edge, or a vertex), the Marching Cubes algorithm
|
||||||
|
generates as output a surface triangle mesh that is almost always combinatorially 2-manifold,
|
||||||
|
yet can still exhibit topological issues such as holes or non-conforming edges.
|
||||||
|
|
||||||
|
If the mesh is 2-manifold and the isosurface does not intersect the domain boundary,
|
||||||
|
then the output mesh is watertight. As the Marching Cubes algorithm uses linear interpolation
|
||||||
|
of the sampled value field along the grid edges, it can miss details or components
|
||||||
|
that are not captured by the sampling of the value field.
|
||||||
|
|
||||||
|
Compared to other meshing approaches such as Delaunay refinement, Marching Cubes is substantially faster,
|
||||||
|
but often tends to generate more triangle facets for an equivalent desired sizing field.
|
||||||
|
In addition, the quality of the triangle facets is in general poor, with many needle or cap-shaped triangles.
|
||||||
|
|
||||||
|
Furthermore, Marching Cubes does not preserve the sharp features present in the isovalue of the input value field (see \cgalFigureRef{IsosurfacingMCDC}).
|
||||||
|
|
||||||
|
\subsection SubSecTMC Topologically Correct Marching Cubes (TMC)
|
||||||
|
|
||||||
|
Topologically Correct Marching Cubes is an extension to the Marching Cubes algorithm
|
||||||
|
which provides additional guarantees for the output \cgalCite{cgal:g-ctcmi-16}.
|
||||||
|
More specifically, it generates as output a mesh that is homeomorphic to the trilinear interpolant of the input value field inside each cube. This means that the output mesh can accurately represent small complex features.
|
||||||
|
For example, a tunnel of the isosurface within a single cell is topologically resolved.
|
||||||
|
To achieve this, the algorithm can insert additional vertices within cells.
|
||||||
|
Furthermore, the mesh is guaranteed to be 2-manifold and watertight, as long as the isosurface
|
||||||
|
does not intersect the domain boundaries.
|
||||||
|
|
||||||
|
\cgalFigureAnchor{IsosurfacingMCTMC}
|
||||||
|
<center>
|
||||||
|
<img src="MC_TMC.png" style="max-width:70%;"/>
|
||||||
|
</center>
|
||||||
|
\cgalFigureCaptionBegin{IsosurfacingMCTMC}
|
||||||
|
Marching Cubes vs Topologically Correct Marching Cubes.
|
||||||
|
Some vertex values can represent complex surfaces within a single cell: on the left,
|
||||||
|
the values at the vertices of the cell have been interpolated within the cell and correspond
|
||||||
|
to a tunnel-like surface. This surface is not correctly recovered when applying the cases of
|
||||||
|
the Marching Cubes algorithm to the cell: 2 independent disk-like sheets are created (middle).
|
||||||
|
However, by inserting new vertices, TMC correctly captures the topology within a single cell (right).
|
||||||
|
\cgalFigureCaptionEnd
|
||||||
|
|
||||||
|
\subsection SubSecDualContouring Dual Contouring (DC)
|
||||||
|
|
||||||
|
%Dual Contouring (DC) \cgalCite{cgal:jlsw-dchd-02} is a method that does not generate vertices
|
||||||
|
on the grid edges, but within cells instead. A facet is created for each edge that intersects the isosurface
|
||||||
|
by connecting the vertices of the incident cells. For a uniform hexahedral grid, this results
|
||||||
|
in a quadrilateral surface mesh. %Dual Contouring can deal with any domain but guarantees
|
||||||
|
neither a 2-manifold nor a watertight mesh. On the other hand, it generates fewer faces
|
||||||
|
and higher quality faces than Marching Cubes, in general. Finally, its main advantage over Marching Cubes
|
||||||
|
is its ability to recover sharp creases and corners.
|
||||||
|
|
||||||
|
\cgalFigureAnchor{IsosurfacingMCDC}
|
||||||
|
<center>
|
||||||
|
<img src="MC_DC.png" style="max-width:70%;"/>
|
||||||
|
</center>
|
||||||
|
\cgalFigureCaptionBegin{IsosurfacingMCDC}
|
||||||
|
Comparison between a mesh of a CSG shape generated by Marching Cubes (left) and %Dual Contouring (right).
|
||||||
|
\cgalFigureCaptionEnd
|
||||||
|
|
||||||
|
In addition to the 3D value field, %Dual Contouring requires knowledge about the gradient of the value field.
|
||||||
|
|
||||||
|
The \cgal implementation uses a vertex positioning strategy based on *Quadric Error Metrics* \cgalCite{gh-ssqem-97} :
|
||||||
|
for a cell, the vertex location is computed by minimizing the error to the sum of the quadrics
|
||||||
|
defined at each edge-isosurface intersection.
|
||||||
|
Using this approach, the vertex may be located outside the cell, which is a desirable property
|
||||||
|
to improve the odds of recovering sharp features, but it might also create self-intersections.
|
||||||
|
Users can choose to constrain the vertex location inside the cell.
|
||||||
|
|
||||||
|
By default, %Dual Contouring generates quads, but using edge-isosurface intersections,
|
||||||
|
one can "star" these quads to generate four triangles. Triangulating the quads is the default behavior in \cgal,
|
||||||
|
but this can be changed using a named parameter.
|
||||||
|
|
||||||
|
\subsection SubSecIsosurfacingComparison Comparisons
|
||||||
|
|
||||||
|
The following table summarizes the differences between the algorithms in terms of constraints
|
||||||
|
over the input 3D domain, the facets of the output surface mesh, and the properties
|
||||||
|
of the output surface mesh.
|
||||||
|
|
||||||
|
<center>
|
||||||
|
| Algorithm | Facets | 2-Manifold | Watertight* | Topologically Correct | Recovery of Sharp Features |
|
||||||
|
| ---- | ---- | ---- | ---- | ---- | ---- |
|
||||||
|
MC | Triangles | no | no | no | no |
|
||||||
|
TMC | Triangles | yes | yes | yes | no |
|
||||||
|
DC | Quads** | no | no | no | yes (not guaranteed) |
|
||||||
|
|
||||||
|
(* assuming the isosurface does not exit the specified bounding box of the input 3D domain)
|
||||||
|
|
||||||
|
(** which can be triangulated on-the-fly)
|
||||||
|
</center>
|
||||||
|
|
||||||
|
Note that the output mesh has boundaries when the isosurface intersects the domain boundaries,
|
||||||
|
regardless of the method (see \cgalFigureRef{IsosurfacingOpen}).
|
||||||
|
|
||||||
|
\cgalFigureAnchor{IsosurfacingOpen}
|
||||||
|
<center>
|
||||||
|
<img src="MC_DC_open.png" style="max-width:70%;"/>
|
||||||
|
</center>
|
||||||
|
\cgalFigureCaptionBegin{IsosurfacingOpen}
|
||||||
|
Outputs of Marching Cubes (left) and %Dual Contouring (right) for an implicit sphere
|
||||||
|
of radius `1.1` and a domain of size `2x2x2`, both centered at the origin.
|
||||||
|
Output meshes can have boundaries when the isosurface intersects the domain boundary.
|
||||||
|
\cgalFigureCaptionEnd
|
||||||
|
|
||||||
|
\section SecInterface Interface
|
||||||
|
|
||||||
|
The following functions are the main entry points to the isosurfacing algorithms of this package:
|
||||||
|
<ul>
|
||||||
|
<li>Marching Cubes</li>: `CGAL::Isosurfacing::marching_cubes()`, using the named parameter: `use_topologically_correct_marching_cubes` set to `false`;
|
||||||
|
<li>Topologically Correct Marching Cubes</li>: `CGAL::Isosurfacing::marching_cubes()`, using the named parameter: `use_topologically_correct_marching_cubes` set to `true`;
|
||||||
|
<li>%Dual Contouring</li>: `CGAL::Isosurfacing::dual_contouring()`.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
All these free functions share the same signature:
|
||||||
|
|
||||||
|
\code{.cpp}
|
||||||
|
template <typename ConcurrencyTag = CGAL::Sequential_tag,
|
||||||
|
typename Domain,
|
||||||
|
typename PointRange,
|
||||||
|
typename PolygonRange>
|
||||||
|
void ...(const Domain& domain,
|
||||||
|
const typename Domain::FT isovalue,
|
||||||
|
PointRange& points,
|
||||||
|
PolygonRange& polygons,
|
||||||
|
...);
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
The input (space partition, value field, gradient field) is provided in the form of a `domain`,
|
||||||
|
see \ref SubSecIsosurfacingDomains for a complete description.
|
||||||
|
|
||||||
|
The `isovalue` scalar parameter is the value that defines the isosurface being approximated.
|
||||||
|
|
||||||
|
The output discrete surface is provided in the form of a polygon soup, which is stored into
|
||||||
|
two containers: `points` and `polygons`. Depending on the algorithm, the polygon soup may
|
||||||
|
store either unorganized polygons with no relationship to one another (i.e., no connectivity is shared between them)
|
||||||
|
or polygons sharing points (the same point in adjacent polygons will be the same point in the point range).
|
||||||
|
|
||||||
|
All isosurfacing algorithms can run either sequentially in one thread or in parallel using multithreading.
|
||||||
|
The template parameter `ConcurrencyTag` is used to specify how the algorithm is executed.
|
||||||
|
To enable parallelism, \cgal must be linked with the <a href="https://github.com/oneapi-src/oneTBB">Intel TBB library</a> (see the CMakeLists.txt file in the examples folder).
|
||||||
|
|
||||||
|
\subsection SubSecIsosurfacingDomains Domains
|
||||||
|
|
||||||
|
A domain is an object that provides functions to access the partition of the 3D volume,
|
||||||
|
the value field, and, optionally, the gradient field at a given point query.
|
||||||
|
These requirements are described through two concepts: `IsosurfacingDomain_3` and `IsosurfacingDomainWithGradient_3`.
|
||||||
|
|
||||||
|
Two domains, `CGAL::Isosurfacing::Marching_cubes_domain_3` and `CGAL::Isosurfacing::Dual_contouring_domain_3`,
|
||||||
|
are provided as the respective default class models that fulfill the requirements of the concepts.
|
||||||
|
Both these domain models have template parameters enabling the user to customize the domain:
|
||||||
|
- <b>Partition:</b> this must be a class that describes the partition of the 3D volume into cells.
|
||||||
|
The most basic example of such a class is `CGAL::Isosurfacing::Cartesian_grid_3`, but users
|
||||||
|
can pass their own partition, provided it meets the requirements described
|
||||||
|
by the concept `IsosurfacingPartition_3`.
|
||||||
|
- <b>ValueField:</b> this must be a class that provides the 3D value field at the vertices of the partition.
|
||||||
|
A few classes are provided, such as `CGAL::Isosurfacing::Value_function_3` and
|
||||||
|
`CGAL::Isosurfacing::Interpolated_discrete_values_3`. Users can pass their own value class,
|
||||||
|
provided it meets the requirements described by the concept `IsosurfacingValueField_3`.
|
||||||
|
- <b>GradientField:</b> (`CGAL::Isosurfacing::Dual_contouring_domain_3` only) this must be a class that provides the gradient
|
||||||
|
of the value field at the vertices of the partition.
|
||||||
|
A few classes are provided by default, such as
|
||||||
|
`CGAL::Isosurfacing::Finite_difference_gradient_3` and
|
||||||
|
`CGAL::Isosurfacing::Interpolated_discrete_gradients_3`.
|
||||||
|
Users can pass their own gradient class,
|
||||||
|
provided it meets the requirements described
|
||||||
|
by the concept `IsosurfacingGradientField_3`.
|
||||||
|
- <b>EdgeIntersectionOracle:</b> this must be a class that provides a function to compute the intersection
|
||||||
|
between an edge and the isosurface. The default is linear interpolation
|
||||||
|
for `CGAL::Isosurfacing::Marching_cubes_domain_3`, and a dichotomy
|
||||||
|
for `CGAL::Isosurfacing::Dual_contouring_domain_3`. This parameter should
|
||||||
|
be adjusted depending on how the value field is defined: there is for
|
||||||
|
example no point incorporating a dichotomy in %Dual Contouring if the value field is defined
|
||||||
|
through linear interpolation. Users can pass their own edge intersection
|
||||||
|
oracle, provided it meets the requirements described by the concept
|
||||||
|
`IsosurfacingEdgeIntersectionOracle_3`.
|
||||||
|
|
||||||
|
<!-- comment this for now. MC and TMC timings are probably not good
|
||||||
|
|
||||||
|
\section SecPerformance Performance
|
||||||
|
|
||||||
|
Due to their cell-based nature, the isosurfacing algorithms are well-suited for parallel execution.
|
||||||
|
|
||||||
|
\cgalFigureAnchor{IsosurfacingPerf}
|
||||||
|
<center>
|
||||||
|
<img src="MC_DC_performance.png" style="max-width:70%;"/>
|
||||||
|
</center>
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
\section SecIsosurfacingExamples Examples
|
||||||
|
|
||||||
|
The first two examples are very basic examples for Marching Cubes and %Dual Contouring.
|
||||||
|
Afterwards, the focus is shifted from the method to the type of input data, and examples
|
||||||
|
run both methods on different types of input data.
|
||||||
|
|
||||||
|
\subsection SubSecMCExample Marching Cubes
|
||||||
|
|
||||||
|
The following example illustrates a basic run of the Marching Cubes algorithm, and in particular
|
||||||
|
the free function to create a domain from a %Cartesian grid, and the named parameter
|
||||||
|
that enables the user to switch from Marching Cubes to Topologically Correct Marching Cubes.
|
||||||
|
The resulting triangle soup is converted to a triangle mesh, and is remeshed using
|
||||||
|
the isotropic remeshing algorithm.
|
||||||
|
|
||||||
|
\cgalExample{Isosurfacing_3/marching_cubes.cpp}
|
||||||
|
|
||||||
|
\cgalFigureAnchor{IsosurfacingMC}
|
||||||
|
<center>
|
||||||
|
<img src="MC.png" style="max-width:70%;"/>
|
||||||
|
</center>
|
||||||
|
\cgalFigureCaptionBegin{IsosurfacingMC}
|
||||||
|
Results of the Marching Cubes algorithm, and the final result after remeshing.
|
||||||
|
\cgalFigureCaptionEnd
|
||||||
|
|
||||||
|
\subsection SubSecDCExample Dual Contouring
|
||||||
|
|
||||||
|
The following example illustrates a basic run of the %Dual Contouring algorithm, and in particular
|
||||||
|
the free function to create a domain from a %Cartesian grid, and the named parameters
|
||||||
|
that enable (or disable) triangulation of the output, and to constrain the vertex location within the cell.
|
||||||
|
|
||||||
|
\cgalExample{Isosurfacing_3/dual_contouring.cpp}
|
||||||
|
|
||||||
|
\cgalFigureAnchor{IsosurfacingDC}
|
||||||
|
<center>
|
||||||
|
<img src="DC.png" style="max-width:70%;"/>
|
||||||
|
</center>
|
||||||
|
\cgalFigureCaptionBegin{IsosurfacingDC}
|
||||||
|
Results of the %Dual Contouring algorithm: untriangulated (left column) or triangulated (right column),
|
||||||
|
unconstrained vertex location (top row) or constrained vertex location (bottom row).
|
||||||
|
\cgalFigureCaptionEnd
|
||||||
|
|
||||||
|
\subsection SubSecImplicitDataExample Implicit Data
|
||||||
|
|
||||||
|
The following example shows the usage of Marching Cubes and %Dual Contouring algorithms to extract
|
||||||
|
an isosurface. The domain is implicit and describes the unit sphere
|
||||||
|
by the distance to its center (set to the origin) as an implicit 3D value field.
|
||||||
|
|
||||||
|
\cgalExample{Isosurfacing_3/contouring_implicit_data.cpp}
|
||||||
|
|
||||||
|
\subsection SubSecDiscreteDataExample Discrete Data
|
||||||
|
|
||||||
|
In the following example, the input data is sampled at the vertices of a grid, and interpolated.
|
||||||
|
|
||||||
|
\cgalExample{Isosurfacing_3/contouring_discrete_data.cpp}
|
||||||
|
|
||||||
|
\subsection SubSecImageDataExample 3D Image
|
||||||
|
|
||||||
|
The following example shows how to load data from an `Image_3`, and generates an isosurface
|
||||||
|
from this voxel data.
|
||||||
|
|
||||||
|
\cgalExample{Isosurfacing_3/contouring_inrimage.cpp}
|
||||||
|
|
||||||
|
\cgalFigureAnchor{IsosurfacingDCEx}
|
||||||
|
<center>
|
||||||
|
<img src="isosurfacing_inrimage.png" style="max-width:70%;"/>
|
||||||
|
</center>
|
||||||
|
\cgalFigureCaptionBegin{IsosurfacingDCEx}
|
||||||
|
Results of the Topologically Correct Marching Cubes algorithm for different isovalues (1, 2, and 2.9)
|
||||||
|
on the skull model.
|
||||||
|
\cgalFigureCaptionEnd
|
||||||
|
|
||||||
|
\subsection SubSecOffsetDataExample Offset Mesh
|
||||||
|
|
||||||
|
The following example illustrates how to generate a mesh approximating a signed offset to an input
|
||||||
|
closed surface mesh. The input mesh is stored into an `AABB_tree` data structure to provide fast
|
||||||
|
distance queries. Via the `Side_of_triangle_mesh` functor, the sign of the distance field is made
|
||||||
|
negative inside the mesh.
|
||||||
|
|
||||||
|
\cgalExample{Isosurfacing_3/contouring_mesh_offset.cpp}
|
||||||
|
|
||||||
|
\subsection SubSecContouringOctreeExample Contouring using Octrees
|
||||||
|
|
||||||
|
The following example shows the use of an octree as discretization instead of a %Cartesian grid
|
||||||
|
for both %Dual Contouring and Marching Cubes. Note that in this configuration, all methods
|
||||||
|
lose guarantees, and cracks can easily appear in the surface for Marching Cubes if the octree
|
||||||
|
is not adapted to the surface being extracted.
|
||||||
|
|
||||||
|
\cgalExample{Isosurfacing_3/contouring_octree.cpp}
|
||||||
|
|
||||||
|
\cgalFigureAnchor{IsosurfacingMCOctree}
|
||||||
|
<center>
|
||||||
|
<img src="MC_octree.png" style="max-width:70%;"/>
|
||||||
|
</center>
|
||||||
|
\cgalFigureCaptionBegin{IsosurfacingMCOctree}
|
||||||
|
Running Marching Cubes with an octree: left, the octree (blue wireframe) has been applied arbitrary
|
||||||
|
refinement in one eighth of the domain and the resulting mesh exhibits cracks (boundaries) at
|
||||||
|
the regions where octree levels are discontinuous. Right, the octree was constructed to be dense
|
||||||
|
around the isosurface, and the surface is manifold.
|
||||||
|
\cgalFigureCaptionEnd
|
||||||
|
|
||||||
|
\section SecIsosurfacingHistory Design and Implementation History
|
||||||
|
|
||||||
|
The development of this package started during the 2022 Google Summer of Code, with the contribution
|
||||||
|
of Julian Stahl, mentored by Daniel Zint and Pierre Alliez, providing a first implementation
|
||||||
|
of Marching Cubes, Topologically Correct Marching Cubes, and %Dual Contouring. Marching Cubes tables
|
||||||
|
were provided by Roberto Grosso (FAU Erlangen-Nürnberg). Mael Rouxel-Labbé worked on improving
|
||||||
|
the initial %Dual Contouring implementation, and on the first complete version of the package.
|
||||||
|
|
||||||
|
*/
|
||||||
|
} /* namespace CGAL */
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
/// \defgroup PkgIsosurfacing3Ref 3D Isosurfacing Reference
|
||||||
|
/// \defgroup PkgIsosurfacing3Concepts Concepts
|
||||||
|
/// \ingroup PkgIsosurfacing3Ref
|
||||||
|
|
||||||
|
/// \defgroup IS_Partitions_grp Space Partitioning Data Structures
|
||||||
|
/// \ingroup PkgIsosurfacing3Ref
|
||||||
|
///
|
||||||
|
/// This group encapsulates classes that represent a spatial discretization of space,
|
||||||
|
/// which will be the scaffolding for the construction of the isosurface.
|
||||||
|
|
||||||
|
/// \defgroup IS_Partitions_helpers_grp Space Partitioning Data Structures Helpers
|
||||||
|
/// \ingroup IS_Partitions_grp
|
||||||
|
|
||||||
|
/// \defgroup IS_Fields_helpers_grp Value and Gradient Fields Helpers
|
||||||
|
/// \ingroup PkgIsosurfacing3Ref
|
||||||
|
///
|
||||||
|
/// The following classes and functions are parameters or template parameters of value and gradient fields.
|
||||||
|
|
||||||
|
/// \defgroup IS_Fields_grp Value and Gradient Fields
|
||||||
|
/// \ingroup PkgIsosurfacing3Ref
|
||||||
|
///
|
||||||
|
/// The following classes represent the data that defines the isosurface.
|
||||||
|
|
||||||
|
/// \defgroup IS_Domains_grp Isosurfacing Domains
|
||||||
|
/// \ingroup PkgIsosurfacing3Ref
|
||||||
|
///
|
||||||
|
/// This group encapsulates the classes that can be used to represent a complete domain (partition
|
||||||
|
/// and fields), to be used by the isosurfacing methods of this package.
|
||||||
|
|
||||||
|
/// \defgroup IS_Domain_helpers_grp Isosurfacing Domain Helpers
|
||||||
|
/// \ingroup IS_Domains_grp
|
||||||
|
|
||||||
|
/// \defgroup IS_Methods_grp Isosurfacing Methods
|
||||||
|
/// \ingroup PkgIsosurfacing3Ref
|
||||||
|
|
||||||
|
/// \defgroup IS_IO_functions_grp I/O Functions
|
||||||
|
/// \ingroup PkgIsosurfacing3Ref
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\addtogroup PkgIsosurfacing3Ref
|
||||||
|
\cgalPkgDescriptionBegin{3D Isosurfacing,PkgIsosurfacing3}
|
||||||
|
\cgalPkgPicture{isosurfacing3_ico.png}
|
||||||
|
\cgalPkgSummaryBegin
|
||||||
|
\cgalPkgAuthor{Mael Rouxel-Labbé, Julian Stahl, Daniel Zint, and Pierre Alliez}
|
||||||
|
\cgalPkgDesc{This package implements several grid-based isosurfacing algorithms (Marching Cubes,
|
||||||
|
its topologically correct variant, and Dual Contouring) that enable generating surface meshes
|
||||||
|
from 3D value and gradient fields. The methods are generic with respect to the definition
|
||||||
|
of the grid and the fields, and all methods offer parallel implementations.
|
||||||
|
The output is a polygon soup (i.e., a container of 3D point coordinates and indexed faces).}
|
||||||
|
\cgalPkgManuals{Chapter_Isosurfacing3,PkgIsosurfacing3Ref}
|
||||||
|
\cgalPkgSummaryEnd
|
||||||
|
\cgalPkgShortInfoBegin
|
||||||
|
\cgalPkgSince{6.1}
|
||||||
|
\cgalPkgBib{cgal:sz-mc}
|
||||||
|
\cgalPkgLicense{\ref licensesGPL "GPL"}
|
||||||
|
\cgalPkgDemo{Polyhedron demo,polyhedron_3.zip}
|
||||||
|
\cgalPkgShortInfoEnd
|
||||||
|
\cgalPkgDescriptionEnd
|
||||||
|
|
||||||
|
\cgalClassifedRefPages
|
||||||
|
|
||||||
|
\cgalCRPSection{Concepts}
|
||||||
|
- `IsosurfacingTraits_3`
|
||||||
|
- `IsosurfacingPartition_3`
|
||||||
|
- `IsosurfacingValueField_3`
|
||||||
|
- `IsosurfacingGradientField_3`
|
||||||
|
- `IsosurfacingInterpolationScheme_3`
|
||||||
|
- `IsosurfacingEdgeIntersectionOracle_3`
|
||||||
|
- `IsosurfacingDomain_3`
|
||||||
|
- `IsosurfacingDomainWithGradient_3`
|
||||||
|
|
||||||
|
\cgalCRPSection{Space Partitioning Data Structures}
|
||||||
|
- `CGAL::Isosurfacing::Cartesian_grid_3`
|
||||||
|
|
||||||
|
\cgalCRPSection{Value and Gradient Fields}
|
||||||
|
- `CGAL::Isosurfacing::Value_function_3`
|
||||||
|
- `CGAL::Isosurfacing::Gradient_function_3`
|
||||||
|
- `CGAL::Isosurfacing::Finite_difference_gradient_3`
|
||||||
|
- `CGAL::Isosurfacing::Interpolated_discrete_values_3`
|
||||||
|
- `CGAL::Isosurfacing::Interpolated_discrete_gradients_3`
|
||||||
|
- `CGAL::Isosurfacing::Trilinear_interpolation`
|
||||||
|
|
||||||
|
\cgalCRPSection{Isosurfacing Domains Helpers}
|
||||||
|
- `CGAL::Isosurfacing::Dichotomy_edge_intersection`
|
||||||
|
- `CGAL::Isosurfacing::Linear_interpolation_edge_intersection`
|
||||||
|
|
||||||
|
\cgalCRPSection{Isosurfacing Domains}
|
||||||
|
- `CGAL::Isosurfacing::Marching_cubes_domain_3`
|
||||||
|
- `CGAL::Isosurfacing::Dual_contouring_domain_3`
|
||||||
|
- `CGAL::Isosurfacing::create_marching_cubes_domain_3()`
|
||||||
|
- `CGAL::Isosurfacing::create_dual_contouring_domain_3()`
|
||||||
|
|
||||||
|
\cgalCRPSection{Isosurfacing Methods}
|
||||||
|
|
||||||
|
- `CGAL::Isosurfacing::marching_cubes()`
|
||||||
|
- `CGAL::Isosurfacing::dual_contouring()`
|
||||||
|
|
||||||
|
\cgalCRPSection{I/O}
|
||||||
|
- `CGAL::Isosurfacing::IO::convert_image_to_grid()`
|
||||||
|
- `CGAL::Isosurfacing::IO::convert_grid_to_image()`
|
||||||
|
*/
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
Manual
|
||||||
|
Kernel_23
|
||||||
|
BGL
|
||||||
|
STL_Extension
|
||||||
|
Algebraic_foundations
|
||||||
|
Circulator
|
||||||
|
Stream_support
|
||||||
|
AABB_tree
|
||||||
|
Polygon_mesh_processing
|
||||||
|
Mesh_3
|
||||||
|
Orthtree
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
/*!
|
||||||
|
\example Isosurfacing_3/contouring_discrete_data.cpp
|
||||||
|
\example Isosurfacing_3/contouring_inrimage.cpp
|
||||||
|
\example Isosurfacing_3/contouring_implicit_data.cpp
|
||||||
|
\example Isosurfacing_3/contouring_mesh_offset.cpp
|
||||||
|
\example Isosurfacing_3/contouring_vtk_image.cpp
|
||||||
|
\example Isosurfacing_3/dual_contouring.cpp
|
||||||
|
\example Isosurfacing_3/contouring_octree.cpp
|
||||||
|
\example Isosurfacing_3/marching_cubes.cpp
|
||||||
|
*/
|
||||||
|
After Width: | Height: | Size: 601 KiB |
|
After Width: | Height: | Size: 374 KiB |
|
After Width: | Height: | Size: 232 KiB |
|
After Width: | Height: | Size: 179 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 104 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 309 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
|
After Width: | Height: | Size: 800 KiB |
|
After Width: | Height: | Size: 628 KiB |
|
|
@ -0,0 +1,73 @@
|
||||||
|
# Created by the script cgal_create_cmake_script
|
||||||
|
# This is the CMake script for compiling a CGAL application.
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.1...3.23)
|
||||||
|
project( Isosurfacing_3_Examples )
|
||||||
|
|
||||||
|
find_package(CGAL REQUIRED COMPONENTS ImageIO)
|
||||||
|
|
||||||
|
find_package(Eigen3 3.1.0 QUIET) #(3.1.0 or greater)
|
||||||
|
include(CGAL_Eigen3_support)
|
||||||
|
|
||||||
|
find_package(TBB QUIET)
|
||||||
|
include(CGAL_TBB_support)
|
||||||
|
|
||||||
|
find_package(VTK QUIET COMPONENTS vtkImagingGeneral vtkIOImage vtkIOXML vtkIOMINC vtkIOLegacy NO_MODULE)
|
||||||
|
if(VTK_FOUND)
|
||||||
|
message(STATUS "VTK was found")
|
||||||
|
else()
|
||||||
|
message(STATUS "NOTICE: VTK was not found")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
create_single_source_cgal_program("marching_cubes.cpp")
|
||||||
|
|
||||||
|
# undocumented
|
||||||
|
create_single_source_cgal_program("marching_cubes_strategies.cpp")
|
||||||
|
|
||||||
|
if(TARGET CGAL::Eigen3_support)
|
||||||
|
create_single_source_cgal_program("dual_contouring.cpp")
|
||||||
|
create_single_source_cgal_program("contouring_discrete_data.cpp")
|
||||||
|
create_single_source_cgal_program("contouring_inrimage.cpp")
|
||||||
|
create_single_source_cgal_program("contouring_implicit_data.cpp")
|
||||||
|
create_single_source_cgal_program("contouring_mesh_offset.cpp")
|
||||||
|
create_single_source_cgal_program("contouring_octree.cpp")
|
||||||
|
|
||||||
|
target_link_libraries(dual_contouring PRIVATE CGAL::Eigen3_support)
|
||||||
|
target_link_libraries(contouring_discrete_data PRIVATE CGAL::Eigen3_support)
|
||||||
|
target_link_libraries(contouring_inrimage PRIVATE CGAL::Eigen3_support)
|
||||||
|
target_link_libraries(contouring_implicit_data PRIVATE CGAL::Eigen3_support)
|
||||||
|
target_link_libraries(contouring_mesh_offset PRIVATE CGAL::Eigen3_support)
|
||||||
|
target_link_libraries(contouring_octree PRIVATE CGAL::Eigen3_support)
|
||||||
|
|
||||||
|
if(TARGET CGAL::TBB_support)
|
||||||
|
target_link_libraries(dual_contouring PRIVATE CGAL::TBB_support)
|
||||||
|
target_link_libraries(contouring_discrete_data PRIVATE CGAL::TBB_support)
|
||||||
|
target_link_libraries(contouring_inrimage PRIVATE CGAL::TBB_support)
|
||||||
|
target_link_libraries(contouring_implicit_data PRIVATE CGAL::TBB_support)
|
||||||
|
target_link_libraries(contouring_mesh_offset PRIVATE CGAL::TBB_support)
|
||||||
|
target_link_libraries(contouring_octree PRIVATE CGAL::TBB_support)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
message(STATUS "NOTICE: Some examples use Eigen, and will not be compiled.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(TARGET CGAL::TBB_support)
|
||||||
|
target_link_libraries(marching_cubes PRIVATE CGAL::TBB_support)
|
||||||
|
target_link_libraries(marching_cubes_strategies PRIVATE CGAL::TBB_support)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(TARGET CGAL::CGAL_ImageIO)
|
||||||
|
if(TARGET CGAL::Eigen3_support)
|
||||||
|
if(VTK_FOUND)
|
||||||
|
create_single_source_cgal_program("contouring_vtk_image.cpp")
|
||||||
|
target_link_libraries(contouring_vtk_image PRIVATE CGAL::Eigen3_support
|
||||||
|
CGAL::CGAL_ImageIO
|
||||||
|
${VTK_LIBRARIES})
|
||||||
|
if(TARGET CGAL::TBB_support)
|
||||||
|
target_link_libraries(contouring_vtk_image PRIVATE CGAL::TBB_support)
|
||||||
|
endif() # TBB
|
||||||
|
endif() # VTK
|
||||||
|
endif() # Eigen
|
||||||
|
else() # ImageIO
|
||||||
|
message(STATUS "NOTICE: Some examples need the CGAL_ImageIO library, and will not be compiled.")
|
||||||
|
endif()
|
||||||
|
|
@ -0,0 +1,129 @@
|
||||||
|
#include <CGAL/Simple_cartesian.h>
|
||||||
|
|
||||||
|
#include <CGAL/Isosurfacing_3/Cartesian_grid_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/dual_contouring_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Dual_contouring_domain_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Interpolated_discrete_gradients_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Interpolated_discrete_values_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/marching_cubes_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Marching_cubes_domain_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/IO/polygon_soup_io.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using Kernel = CGAL::Simple_cartesian<double>;
|
||||||
|
using FT = typename Kernel::FT;
|
||||||
|
using Point = typename Kernel::Point_3;
|
||||||
|
using Vector = typename Kernel::Vector_3;
|
||||||
|
|
||||||
|
using Grid = CGAL::Isosurfacing::Cartesian_grid_3<Kernel>;
|
||||||
|
using Values = CGAL::Isosurfacing::Interpolated_discrete_values_3<Grid>;
|
||||||
|
using Gradients = CGAL::Isosurfacing::Interpolated_discrete_gradients_3<Grid>;
|
||||||
|
|
||||||
|
using Point_range = std::vector<Point>;
|
||||||
|
using Polygon_range = std::vector<std::vector<std::size_t> >;
|
||||||
|
|
||||||
|
namespace IS = CGAL::Isosurfacing;
|
||||||
|
|
||||||
|
void run_marching_cubes(const Grid& grid,
|
||||||
|
const FT isovalue)
|
||||||
|
{
|
||||||
|
using Domain = IS::Marching_cubes_domain_3<Grid, Values, IS::Linear_interpolation_edge_intersection>;
|
||||||
|
|
||||||
|
std::cout << "\n ---- " << std::endl;
|
||||||
|
std::cout << "Running Marching Cubes with isovalue = " << isovalue << std::endl;
|
||||||
|
|
||||||
|
// fill up values
|
||||||
|
Values values { grid };
|
||||||
|
|
||||||
|
for(std::size_t i=0; i<grid.xdim(); ++i) {
|
||||||
|
for(std::size_t j=0; j<grid.ydim(); ++j) {
|
||||||
|
for(std::size_t k=0; k<grid.zdim(); ++k)
|
||||||
|
{
|
||||||
|
const Point& p = grid.point(i,j,k);
|
||||||
|
const FT d = sqrt(CGAL::squared_distance(p, Point(CGAL::ORIGIN)));
|
||||||
|
values(i,j,k) = d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Domain domain { grid, values };
|
||||||
|
|
||||||
|
Point_range points;
|
||||||
|
Polygon_range triangles;
|
||||||
|
|
||||||
|
// run marching cubes isosurfacing
|
||||||
|
IS::marching_cubes<CGAL::Parallel_if_available_tag>(domain, isovalue, points, triangles);
|
||||||
|
|
||||||
|
std::cout << "Output #vertices: " << points.size() << std::endl;
|
||||||
|
std::cout << "Output #triangles: " << triangles.size() << std::endl;
|
||||||
|
CGAL::IO::write_polygon_soup("marching_cubes_discrete.off", points, triangles);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_dual_contouring(const Grid& grid,
|
||||||
|
const FT isovalue)
|
||||||
|
{
|
||||||
|
using Domain = IS::Dual_contouring_domain_3<Grid, Values, Gradients, IS::Linear_interpolation_edge_intersection>;
|
||||||
|
|
||||||
|
std::cout << "\n ---- " << std::endl;
|
||||||
|
std::cout << "Running Dual Contouring with isovalue = " << isovalue << std::endl;
|
||||||
|
|
||||||
|
// fill up values and gradients
|
||||||
|
Values values { grid };
|
||||||
|
Gradients gradients { grid };
|
||||||
|
|
||||||
|
for(std::size_t i=0; i<grid.xdim(); ++i) {
|
||||||
|
for(std::size_t j=0; j<grid.ydim(); ++j) {
|
||||||
|
for(std::size_t k=0; k<grid.zdim(); ++k)
|
||||||
|
{
|
||||||
|
const Point& p = grid.point(i,j,k);
|
||||||
|
const FT d = sqrt(CGAL::squared_distance(p, Point(CGAL::ORIGIN)));
|
||||||
|
values(i,j,k) = d;
|
||||||
|
|
||||||
|
if(d != 0)
|
||||||
|
gradients(i,j,k) = Vector(CGAL::ORIGIN, p) / d;
|
||||||
|
else
|
||||||
|
gradients(i,j,k) = CGAL::NULL_VECTOR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the gradient was not known analytically, we could use:
|
||||||
|
// - Finite_difference_gradient_3 to use finite difference to compute it from values;
|
||||||
|
// - the function below to use finite difference _and_ store the values at the grid vertices
|
||||||
|
// gradients.compute_discrete_gradients(values);
|
||||||
|
|
||||||
|
Domain domain { grid, values, gradients };
|
||||||
|
|
||||||
|
Point_range points;
|
||||||
|
Polygon_range triangles;
|
||||||
|
|
||||||
|
// run dual contouring isosurfacing
|
||||||
|
IS::dual_contouring<CGAL::Parallel_if_available_tag>(domain, isovalue, points, triangles);
|
||||||
|
|
||||||
|
std::cout << "Output #vertices: " << points.size() << std::endl;
|
||||||
|
std::cout << "Output #triangles: " << triangles.size() << std::endl;
|
||||||
|
CGAL::IO::write_polygon_soup("dual_contouring_discrete.off", points, triangles);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
const FT isovalue = (argc > 1) ? std::stod(argv[1]) : 0.8;
|
||||||
|
|
||||||
|
// create bounding box and grid
|
||||||
|
const CGAL::Bbox_3 bbox { -1., -1., -1., 1., 1., 1. };
|
||||||
|
Grid grid { bbox, CGAL::make_array<std::size_t>(30, 30, 30) };
|
||||||
|
|
||||||
|
std::cout << "Span: " << grid.span() << std::endl;
|
||||||
|
std::cout << "Cell dimensions: " << grid.spacing()[0] << " " << grid.spacing()[1] << " " << grid.spacing()[2] << std::endl;
|
||||||
|
std::cout << "Cell #: " << grid.xdim() << ", " << grid.ydim() << ", " << grid.zdim() << std::endl;
|
||||||
|
|
||||||
|
run_marching_cubes(grid, isovalue);
|
||||||
|
|
||||||
|
run_dual_contouring(grid, isovalue);
|
||||||
|
|
||||||
|
std::cout << "Done" << std::endl;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,136 @@
|
||||||
|
#include <CGAL/Simple_cartesian.h>
|
||||||
|
|
||||||
|
#include <CGAL/Isosurfacing_3/Cartesian_grid_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/dual_contouring_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Dual_contouring_domain_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Value_function_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Gradient_function_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/marching_cubes_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Marching_cubes_domain_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/Bbox_3.h>
|
||||||
|
#include <CGAL/Real_timer.h>
|
||||||
|
#include <CGAL/IO/polygon_soup_io.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using Kernel = CGAL::Simple_cartesian<double>;
|
||||||
|
using FT = typename Kernel::FT;
|
||||||
|
using Point = typename Kernel::Point_3;
|
||||||
|
using Vector = typename Kernel::Vector_3;
|
||||||
|
|
||||||
|
// using Grid = CGAL::Isosurfacing::Cartesian_grid_3<Kernel, CGAL::Isosurfacing::Do_not_cache_vertex_locations>;
|
||||||
|
using Grid = CGAL::Isosurfacing::Cartesian_grid_3<Kernel, CGAL::Isosurfacing::Cache_vertex_locations>;
|
||||||
|
using Values = CGAL::Isosurfacing::Value_function_3<Grid>;
|
||||||
|
using Gradients = CGAL::Isosurfacing::Gradient_function_3<Grid>;
|
||||||
|
|
||||||
|
using Point_range = std::vector<Point>;
|
||||||
|
using Polygon_range = std::vector<std::vector<std::size_t> >;
|
||||||
|
|
||||||
|
// ---
|
||||||
|
const FT alpha = 5.01;
|
||||||
|
|
||||||
|
auto iwp_value = [](const Point& point)
|
||||||
|
{
|
||||||
|
const FT x = alpha * (point.x() + FT(1.0)) * CGAL_PI;
|
||||||
|
const FT y = alpha * (point.y() + FT(1.0)) * CGAL_PI;
|
||||||
|
const FT z = alpha * (point.z() + FT(1.0)) * CGAL_PI;
|
||||||
|
return cos(x)*cos(y) + cos(y)*cos(z) + cos(z)*cos(x) - cos(x)*cos(y)*cos(z); // isovalue = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
auto iwp_gradient = [](const Point& point)
|
||||||
|
{
|
||||||
|
const FT x = alpha * (point.x() + FT(1.0)) * CGAL_PI;
|
||||||
|
const FT y = alpha * (point.y() + FT(1.0)) * CGAL_PI;
|
||||||
|
const FT z = alpha * (point.z() + FT(1.0)) * CGAL_PI;
|
||||||
|
|
||||||
|
const FT gx = CGAL_PI * alpha * sin(x) * (cos(y) * (cos(z) - FT(1.0)) - cos(z));
|
||||||
|
const FT gy = CGAL_PI * alpha * sin(y) * (cos(x) * (cos(z) - FT(1.0)) - cos(z));
|
||||||
|
const FT gz = CGAL_PI * alpha * sin(z) * (cos(x) * (cos(y) - FT(1.0)) - cos(y));
|
||||||
|
return Vector(gx, gy, gz);
|
||||||
|
};
|
||||||
|
|
||||||
|
void run_marching_cubes(const Grid& grid,
|
||||||
|
const FT isovalue)
|
||||||
|
{
|
||||||
|
using Domain = CGAL::Isosurfacing::Marching_cubes_domain_3<Grid, Values>;
|
||||||
|
|
||||||
|
std::cout << "\n ---- " << std::endl;
|
||||||
|
std::cout << "Running Marching Cubes with isovalue = " << isovalue << std::endl;
|
||||||
|
|
||||||
|
CGAL::Real_timer timer;
|
||||||
|
timer.start();
|
||||||
|
|
||||||
|
// fill up values
|
||||||
|
Values values { iwp_value, grid };
|
||||||
|
Domain domain { grid, values };
|
||||||
|
|
||||||
|
// output containers
|
||||||
|
Point_range points;
|
||||||
|
Polygon_range triangles;
|
||||||
|
|
||||||
|
// run Marching Cubes
|
||||||
|
CGAL::Isosurfacing::marching_cubes<CGAL::Parallel_if_available_tag>(domain, isovalue, points, triangles);
|
||||||
|
|
||||||
|
timer.stop();
|
||||||
|
|
||||||
|
std::cout << "Output #vertices (MC): " << points.size() << std::endl;
|
||||||
|
std::cout << "Output #triangles (MC): " << triangles.size() << std::endl;
|
||||||
|
std::cout << "Elapsed time: " << timer.time() << " seconds" << std::endl;
|
||||||
|
CGAL::IO::write_polygon_soup("marching_cubes_implicit.off", points, triangles);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_dual_contouring(const Grid& grid,
|
||||||
|
const FT isovalue)
|
||||||
|
{
|
||||||
|
using Domain = CGAL::Isosurfacing::Dual_contouring_domain_3<Grid, Values, Gradients>;
|
||||||
|
|
||||||
|
std::cout << "\n ---- " << std::endl;
|
||||||
|
std::cout << "Running Dual Contouring with isovalue = " << isovalue << std::endl;
|
||||||
|
|
||||||
|
CGAL::Real_timer timer;
|
||||||
|
timer.start();
|
||||||
|
|
||||||
|
// fill up values and gradients
|
||||||
|
Values values { iwp_value, grid };
|
||||||
|
Gradients gradients { iwp_gradient, grid };
|
||||||
|
Domain domain { grid, values, gradients };
|
||||||
|
|
||||||
|
// output containers
|
||||||
|
Point_range points;
|
||||||
|
Polygon_range triangles;
|
||||||
|
|
||||||
|
// run Dual Contouring
|
||||||
|
CGAL::Isosurfacing::dual_contouring<CGAL::Parallel_if_available_tag>(domain, isovalue, points, triangles,
|
||||||
|
CGAL::parameters::do_not_triangulate_faces(true));
|
||||||
|
|
||||||
|
timer.stop();
|
||||||
|
|
||||||
|
std::cout << "Output #vertices (DC): " << points.size() << std::endl;
|
||||||
|
std::cout << "Output #triangles (DC): " << triangles.size() << std::endl;
|
||||||
|
std::cout << "Elapsed time: " << timer.time() << " seconds" << std::endl;
|
||||||
|
CGAL::IO::write_polygon_soup("dual_contouring_implicit.off", points, triangles);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
const FT isovalue = (argc > 1) ? std::stod(argv[1]) : 0.;
|
||||||
|
|
||||||
|
const CGAL::Bbox_3 bbox{-1, -1, -1, 1, 1, 1};
|
||||||
|
const FT step = 0.02;
|
||||||
|
const Vector spacing { step, step, step };
|
||||||
|
Grid grid { bbox, spacing };
|
||||||
|
|
||||||
|
std::cout << "Span: " << grid.span() << std::endl;
|
||||||
|
std::cout << "Cell dimensions: " << grid.spacing()[0] << " " << grid.spacing()[1] << " " << grid.spacing()[2] << std::endl;
|
||||||
|
std::cout << "Cell #: " << grid.xdim() << ", " << grid.ydim() << ", " << grid.zdim() << std::endl;
|
||||||
|
|
||||||
|
run_marching_cubes(grid, isovalue);
|
||||||
|
|
||||||
|
run_dual_contouring(grid, isovalue);
|
||||||
|
|
||||||
|
std::cout << "Done" << std::endl;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
#include <CGAL/Simple_cartesian.h>
|
||||||
|
|
||||||
|
#include <CGAL/Isosurfacing_3/Cartesian_grid_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/dual_contouring_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Dual_contouring_domain_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Finite_difference_gradient_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Interpolated_discrete_values_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/marching_cubes_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Marching_cubes_domain_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/Image_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/Isosurfacing_3/IO/Image_3.h>
|
||||||
|
#include <CGAL/IO/polygon_soup_io.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using Kernel = CGAL::Simple_cartesian<double>;
|
||||||
|
using FT = typename Kernel::FT;
|
||||||
|
using Point = typename Kernel::Point_3;
|
||||||
|
|
||||||
|
using Grid = CGAL::Isosurfacing::Cartesian_grid_3<Kernel>;
|
||||||
|
using Values = CGAL::Isosurfacing::Interpolated_discrete_values_3<Grid>;
|
||||||
|
using Gradients = CGAL::Isosurfacing::Finite_difference_gradient_3<Kernel>;
|
||||||
|
|
||||||
|
using Point_range = std::vector<Point>;
|
||||||
|
using Polygon_range = std::vector<std::vector<std::size_t> >;
|
||||||
|
|
||||||
|
namespace IS = CGAL::Isosurfacing;
|
||||||
|
|
||||||
|
void run_marching_cubes(const Grid& grid,
|
||||||
|
const FT isovalue,
|
||||||
|
const Values& values)
|
||||||
|
{
|
||||||
|
using Domain = IS::Marching_cubes_domain_3<Grid, Values, IS::Linear_interpolation_edge_intersection>;
|
||||||
|
|
||||||
|
std::cout << "\n ---- " << std::endl;
|
||||||
|
std::cout << "Running Marching Cubes with isovalue = " << isovalue << std::endl;
|
||||||
|
|
||||||
|
// fill up values
|
||||||
|
|
||||||
|
// create a domain from the grid
|
||||||
|
Domain domain { grid, values };
|
||||||
|
|
||||||
|
// prepare collections for the output indexed soup
|
||||||
|
Point_range points;
|
||||||
|
Polygon_range triangles;
|
||||||
|
|
||||||
|
// execute marching cubes
|
||||||
|
IS::marching_cubes<CGAL::Parallel_if_available_tag>(domain, isovalue, points, triangles);
|
||||||
|
|
||||||
|
std::cout << "Output #vertices: " << points.size() << std::endl;
|
||||||
|
std::cout << "Output #triangles: " << triangles.size() << std::endl;
|
||||||
|
|
||||||
|
// save output indexed mesh to a file, in the OFF format
|
||||||
|
CGAL::IO::write_polygon_soup("marching_cubes_inrimage.off", points, triangles);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_dual_contouring(const Grid& grid,
|
||||||
|
const FT isovalue,
|
||||||
|
const Values& values)
|
||||||
|
{
|
||||||
|
using Domain = IS::Dual_contouring_domain_3<Grid, Values, Gradients, IS::Linear_interpolation_edge_intersection>;
|
||||||
|
|
||||||
|
std::cout << "\n ---- " << std::endl;
|
||||||
|
std::cout << "Running Dual Contouring with isovalue = " << isovalue << std::endl;
|
||||||
|
|
||||||
|
// fill up values and gradients
|
||||||
|
const FT step = CGAL::approximate_sqrt(grid.spacing().squared_length()) * 0.01; // finite difference step
|
||||||
|
Gradients gradients { values, step };
|
||||||
|
Domain domain { grid, values, gradients };
|
||||||
|
|
||||||
|
Point_range points;
|
||||||
|
Polygon_range triangles;
|
||||||
|
|
||||||
|
// run dual contouring isosurfacing
|
||||||
|
IS::dual_contouring<CGAL::Parallel_if_available_tag>(domain, isovalue, points, triangles);
|
||||||
|
|
||||||
|
std::cout << "Output #vertices: " << points.size() << std::endl;
|
||||||
|
std::cout << "Output #triangles: " << triangles.size() << std::endl;
|
||||||
|
CGAL::IO::write_polygon_soup("dual_contouring_inrimage.off", points, triangles);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
const std::string fname = (argc > 1) ? argv[1] : CGAL::data_file_path("images/skull_2.9.inr");
|
||||||
|
const FT isovalue = (argc > 2) ? std::stod(argv[2]) : - 2.9;
|
||||||
|
|
||||||
|
// load volumetric image from a file
|
||||||
|
CGAL::Image_3 image;
|
||||||
|
if(!image.read(fname))
|
||||||
|
{
|
||||||
|
std::cerr << "Error: Cannot read image file " << fname << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert image to a Cartesian grid
|
||||||
|
Grid grid;
|
||||||
|
Values values { grid }; // 'values' keeps a reference to the grid
|
||||||
|
if(!IS::IO::convert_image_to_grid(image, grid, values))
|
||||||
|
{
|
||||||
|
std::cerr << "Error: Cannot convert image to Cartesian grid" << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Span: " << grid.span() << std::endl;
|
||||||
|
std::cout << "Cell dimensions: " << grid.spacing()[0] << " " << grid.spacing()[1] << " " << grid.spacing()[2] << std::endl;
|
||||||
|
std::cout << "Cell #: " << grid.xdim() << ", " << grid.ydim() << ", " << grid.zdim() << std::endl;
|
||||||
|
|
||||||
|
run_marching_cubes(grid, isovalue, values);
|
||||||
|
|
||||||
|
run_dual_contouring(grid, isovalue, values);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,177 @@
|
||||||
|
#include <CGAL/Simple_cartesian.h>
|
||||||
|
#include <CGAL/Surface_mesh.h>
|
||||||
|
|
||||||
|
#include <CGAL/Isosurfacing_3/Cartesian_grid_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/dual_contouring_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Dual_contouring_domain_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Value_function_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Finite_difference_gradient_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/marching_cubes_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Marching_cubes_domain_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/AABB_face_graph_triangle_primitive.h>
|
||||||
|
#include <CGAL/AABB_traits_3.h>
|
||||||
|
#include <CGAL/AABB_tree.h>
|
||||||
|
#include <CGAL/Bbox_3.h>
|
||||||
|
#include <CGAL/Polygon_mesh_processing/bbox.h>
|
||||||
|
#include <CGAL/Side_of_triangle_mesh.h>
|
||||||
|
|
||||||
|
#include <CGAL/boost/graph/IO/polygon_mesh_io.h>
|
||||||
|
#include <CGAL/IO/polygon_soup_io.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using Kernel = CGAL::Simple_cartesian<double>;
|
||||||
|
using FT = typename Kernel::FT;
|
||||||
|
using Point = typename Kernel::Point_3;
|
||||||
|
using Vector = typename Kernel::Vector_3;
|
||||||
|
|
||||||
|
using Mesh = CGAL::Surface_mesh<Point>;
|
||||||
|
|
||||||
|
using Grid = CGAL::Isosurfacing::Cartesian_grid_3<Kernel>;
|
||||||
|
using Values = CGAL::Isosurfacing::Value_function_3<Grid>;
|
||||||
|
using Gradients = CGAL::Isosurfacing::Finite_difference_gradient_3<Kernel>;
|
||||||
|
|
||||||
|
using Point_range = std::vector<Point>;
|
||||||
|
using Polygon_range = std::vector<std::vector<std::size_t> >;
|
||||||
|
|
||||||
|
struct Offset_oracle
|
||||||
|
{
|
||||||
|
using Primitive = CGAL::AABB_face_graph_triangle_primitive<Mesh>;
|
||||||
|
using Traits = CGAL::AABB_traits_3<Kernel, Primitive>;
|
||||||
|
using Tree = CGAL::AABB_tree<Traits>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const bool is_closed;
|
||||||
|
const Tree tree;
|
||||||
|
CGAL::Side_of_triangle_mesh<Mesh, Kernel> sotm;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Offset_oracle(const Mesh& mesh)
|
||||||
|
: is_closed(CGAL::is_closed(mesh)), tree(mesh.faces_begin(), mesh.faces_end(), mesh), sotm(mesh)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
FT distance(const Point& p) const
|
||||||
|
{
|
||||||
|
const Point cp = tree.closest_point(p);
|
||||||
|
FT d = sqrt((p - cp).squared_length());
|
||||||
|
|
||||||
|
if(is_closed && sotm(p) == (CGAL::ON_BOUNDED_SIDE))
|
||||||
|
d *= -1.0;
|
||||||
|
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void run_marching_cubes(const Grid& grid,
|
||||||
|
const FT offset_value,
|
||||||
|
const Offset_oracle& offset_oracle)
|
||||||
|
{
|
||||||
|
using Domain = CGAL::Isosurfacing::Marching_cubes_domain_3<Grid, Values>;
|
||||||
|
|
||||||
|
std::cout << "\n ---- " << std::endl;
|
||||||
|
std::cout << "Running Marching Cubes with offset value = " << offset_value << std::endl;
|
||||||
|
|
||||||
|
// fill up values
|
||||||
|
auto mesh_distance = [&offset_oracle](const Point& p) { return offset_oracle.distance(p); };
|
||||||
|
Values values { mesh_distance, grid };
|
||||||
|
Domain domain { grid, values };
|
||||||
|
|
||||||
|
Point_range points;
|
||||||
|
Polygon_range triangles;
|
||||||
|
|
||||||
|
// run marching cubes
|
||||||
|
std::cout << "Running Marching Cubes with isovalue = " << offset_value << std::endl;
|
||||||
|
CGAL::Isosurfacing::marching_cubes<CGAL::Parallel_if_available_tag>(domain, offset_value, points, triangles);
|
||||||
|
|
||||||
|
std::cout << "Output #vertices (MC): " << points.size() << std::endl;
|
||||||
|
std::cout << "Output #triangles (MC): " << triangles.size() << std::endl;
|
||||||
|
CGAL::IO::write_polygon_soup("marching_cubes_offsets.off", points, triangles);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_dual_contouring(const Grid& grid,
|
||||||
|
const FT offset_value,
|
||||||
|
const Offset_oracle& offset_oracle)
|
||||||
|
{
|
||||||
|
using Domain = CGAL::Isosurfacing::Dual_contouring_domain_3<Grid, Values, Gradients>;
|
||||||
|
|
||||||
|
std::cout << "\n ---- " << std::endl;
|
||||||
|
std::cout << "Running Dual Contouring with offset value = " << offset_value << std::endl;
|
||||||
|
|
||||||
|
// fill up values and gradients
|
||||||
|
auto mesh_distance = [&offset_oracle](const Point& p) { return offset_oracle.distance(p); };
|
||||||
|
|
||||||
|
const FT step = CGAL::approximate_sqrt(grid.spacing().squared_length()) * 0.01;
|
||||||
|
|
||||||
|
Values values { mesh_distance, grid };
|
||||||
|
Gradients gradients { values, step };
|
||||||
|
Domain domain { grid, values, gradients };
|
||||||
|
|
||||||
|
// output containers
|
||||||
|
Point_range points;
|
||||||
|
Polygon_range triangles;
|
||||||
|
|
||||||
|
// run dual contouring
|
||||||
|
std::cout << "Running Dual Contouring with isovalue = " << offset_value << std::endl;
|
||||||
|
CGAL::Isosurfacing::dual_contouring<CGAL::Parallel_if_available_tag>(
|
||||||
|
domain, offset_value, points, triangles,
|
||||||
|
CGAL::parameters::do_not_triangulate_faces(true));
|
||||||
|
|
||||||
|
std::cout << "Output #vertices (DC): " << points.size() << std::endl;
|
||||||
|
std::cout << "Output #triangles (DC): " << triangles.size() << std::endl;
|
||||||
|
CGAL::IO::write_polygon_soup("dual_contouring_mesh_offset.off", points, triangles);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
const std::string filename = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/cross.off");
|
||||||
|
const FT offset_value = (argc > 2) ? std::stod(argv[2]) : 0.2;
|
||||||
|
|
||||||
|
if(offset_value < 0)
|
||||||
|
{
|
||||||
|
std::cerr << "Offset value must be positive" << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mesh mesh;
|
||||||
|
if(!CGAL::IO::read_polygon_mesh(filename, mesh) || is_empty(mesh))
|
||||||
|
{
|
||||||
|
std::cerr << "Could not read input mesh" << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(CGAL::is_closed(mesh))
|
||||||
|
std::cout << "Input mesh is closed - using signed distance offset" << std::endl;
|
||||||
|
else
|
||||||
|
std::cout << "Input mesh is not closed - using unsigned distance offset" << std::endl;
|
||||||
|
|
||||||
|
// construct loose bounding box from input mesh
|
||||||
|
CGAL::Bbox_3 bbox = CGAL::Polygon_mesh_processing::bbox(mesh);
|
||||||
|
|
||||||
|
const FT diag_length = sqrt(CGAL::square(bbox.xmax() - bbox.xmin()) +
|
||||||
|
CGAL::square(bbox.ymax() - bbox.ymin()) +
|
||||||
|
CGAL::square(bbox.zmax() - bbox.zmin()));
|
||||||
|
const FT loose_offset = offset_value + 0.1 * diag_length;
|
||||||
|
|
||||||
|
Vector aabb_increase_vec = Vector(loose_offset, loose_offset, loose_offset);
|
||||||
|
bbox += (Point(bbox.xmax(), bbox.ymax(), bbox.zmax()) + aabb_increase_vec).bbox();
|
||||||
|
bbox += (Point(bbox.xmin(), bbox.ymin(), bbox.zmin()) - aabb_increase_vec).bbox();
|
||||||
|
|
||||||
|
const int nv = 15;
|
||||||
|
|
||||||
|
Grid grid { bbox, CGAL::make_array<std::size_t>(nv, nv, nv) };
|
||||||
|
|
||||||
|
std::cout << "Span: " << grid.span() << std::endl;
|
||||||
|
std::cout << "Cell dimensions: " << grid.spacing()[0] << " " << grid.spacing()[1] << " " << grid.spacing()[2] << std::endl;
|
||||||
|
std::cout << "Cell #: " << grid.xdim() << ", " << grid.ydim() << ", " << grid.zdim() << std::endl;
|
||||||
|
|
||||||
|
Offset_oracle offset_oracle(mesh);
|
||||||
|
|
||||||
|
run_marching_cubes(grid, offset_value, offset_oracle);
|
||||||
|
|
||||||
|
run_dual_contouring(grid, offset_value, offset_oracle);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,288 @@
|
||||||
|
#include <CGAL/Simple_cartesian.h>
|
||||||
|
|
||||||
|
#include <CGAL/Isosurfacing_3/dual_contouring_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Dual_contouring_domain_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/marching_cubes_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Marching_cubes_domain_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Value_function_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Gradient_function_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Octree_partition.h>
|
||||||
|
|
||||||
|
#include <CGAL/Bbox_3.h>
|
||||||
|
#include <CGAL/IO/polygon_soup_io.h>
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using Kernel = CGAL::Simple_cartesian<double>;
|
||||||
|
using FT = typename Kernel::FT;
|
||||||
|
using Vector = typename Kernel::Vector_3;
|
||||||
|
using Point = typename Kernel::Point_3;
|
||||||
|
|
||||||
|
using Point_range = std::vector<Point>;
|
||||||
|
using Polygon_range = std::vector<std::vector<std::size_t> >;
|
||||||
|
|
||||||
|
using Octree = CGAL::Octree<Kernel, std::vector<Point> >;
|
||||||
|
using Values = CGAL::Isosurfacing::Value_function_3<Octree>;
|
||||||
|
using Gradients = CGAL::Isosurfacing::Gradient_function_3<Octree>;
|
||||||
|
using MC_Domain = CGAL::Isosurfacing::Marching_cubes_domain_3<Octree, Values>;
|
||||||
|
using Domain = CGAL::Isosurfacing::Dual_contouring_domain_3<Octree, Values, Gradients>;
|
||||||
|
|
||||||
|
namespace IS = CGAL::Isosurfacing;
|
||||||
|
|
||||||
|
auto sphere_function = [](const Point& p) -> FT
|
||||||
|
{
|
||||||
|
return std::sqrt(p.x()*p.x() + p.y()*p.y() + p.z()*p.z());
|
||||||
|
};
|
||||||
|
|
||||||
|
auto sphere_gradient = [](const Point& p) -> Vector
|
||||||
|
{
|
||||||
|
const Vector g = p - CGAL::ORIGIN;
|
||||||
|
return g / std::sqrt(g.squared_length());
|
||||||
|
};
|
||||||
|
|
||||||
|
auto blobby_function = [](const Point& p) -> FT
|
||||||
|
{
|
||||||
|
return std::exp(-1.5 * ((p.x() - 0.2) * (p.x() - 0.2) + (p.y() - 0.2) * (p.y() - 0.2) + (p.z() - 0.2) * (p.z() - 0.2))) +
|
||||||
|
std::exp(-1.5 * ((p.x() + 0.2) * (p.x() + 0.2) + (p.y() + 0.2) * (p.y() + 0.2) + (p.z() + 0.2) * (p.z() + 0.2))) +
|
||||||
|
std::exp(-1.5 * ((p.x() - 0.4) * (p.x() - 0.4) + (p.y() + 0.4) * (p.y() + 0.4) + (p.z() - 0.4) * (p.z() - 0.4))) +
|
||||||
|
std::exp(-6 * ((p.x() - 0.1) * (p.x() - 0.1) + (p.y() - 0.1) * (p.y() - 0.1))) +
|
||||||
|
std::exp(-6 * ((p.y() + 0.1) * (p.y() + 0.1) + (p.z() + 0.1) * (p.z() + 0.1))) +
|
||||||
|
std::exp(-6 * ((p.x() + 0.1) * (p.x() + 0.1) + (p.z() - 0.1) * (p.z() - 0.1))) -
|
||||||
|
0.3;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto blobby_gradient = [](const Point& p) -> Vector
|
||||||
|
{
|
||||||
|
const FT g1 = -3 * std::exp(-1.5 * ((p.x() - 0.2) * (p.x() - 0.2) + (p.y() - 0.2) * (p.y() - 0.2) + (p.z() - 0.2) * (p.z() - 0.2)));
|
||||||
|
const FT g2 = -3 * std::exp(-1.5 * ((p.x() + 0.2) * (p.x() + 0.2) + (p.y() + 0.2) * (p.y() + 0.2) + (p.z() + 0.2) * (p.z() + 0.2)));
|
||||||
|
const FT g3 = -3 * std::exp(-1.5 * ((p.x() - 0.4) * (p.x() - 0.4) + (p.y() + 0.4) * (p.y() + 0.4) + (p.z() - 0.4) * (p.z() - 0.4)));
|
||||||
|
const FT g4 = -12 * std::exp(-6 * ((p.x() - 0.1) * (p.x() - 0.1) + (p.y() - 0.1) * (p.y() - 0.1)));
|
||||||
|
const FT g5 = -12 * std::exp(-6 * ((p.y() + 0.1) * (p.y() + 0.1) + (p.z() + 0.1) * (p.z() + 0.1)));
|
||||||
|
const FT g6 = -12 * std::exp(-6 * ((p.x() + 0.1) * (p.x() + 0.1) + (p.z() - 0.1) * (p.z() - 0.1)));
|
||||||
|
|
||||||
|
return Vector(g1 * (p.x() - 0.2) + g2 * (p.x() + 0.2) + g3 * (p.x() - 0.4) + g4 * (p.x() - 0.1) + g6 * (p.x() + 0.1),
|
||||||
|
g1 * (p.y() - 0.2) + g2 * (p.y() + 0.2) + g3 * (p.y() + 0.4) + g4 * (p.y() - 0.1) + g5 * (p.y() + 0.1),
|
||||||
|
g1 * (p.z() - 0.2) + g2 * (p.z() + 0.2) + g3 * (p.z() - 0.4) + g5 * (p.z() + 0.1) + g6 * (p.z() - 0.1));
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is a naive refinement that is adapted to the isosurface:
|
||||||
|
// This refines:
|
||||||
|
// - at the minimum till minimum depth
|
||||||
|
// - at the maximum till maximum depth
|
||||||
|
// - we split if the the isovalue goes through the voxel, i.e. if not all vertices of the cell
|
||||||
|
// are on the same side of the isosurface defined by a function
|
||||||
|
// It's not a great refinement technique because the surface can enter and leave a cell
|
||||||
|
// without involving the cell's vertex. In practice, that means a hole if at nearby adjacent
|
||||||
|
// cells the voxels did get refined and registered the surface.
|
||||||
|
struct Refine_around_isovalue
|
||||||
|
{
|
||||||
|
std::size_t min_depth_;
|
||||||
|
std::size_t max_depth_;
|
||||||
|
std::function<FT(const Point&)> function_;
|
||||||
|
FT isovalue_;
|
||||||
|
|
||||||
|
Refine_around_isovalue(std::size_t min_depth,
|
||||||
|
std::size_t max_depth,
|
||||||
|
std::function<FT(const Point&)> function,
|
||||||
|
FT isovalue)
|
||||||
|
: min_depth_(min_depth),
|
||||||
|
max_depth_(max_depth),
|
||||||
|
function_(function),
|
||||||
|
isovalue_(isovalue)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool operator()(const Octree::Node_index& ni, const Octree& octree) const
|
||||||
|
{
|
||||||
|
// Ensure minimum depth refinement
|
||||||
|
if (octree.depth(ni) < min_depth_)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Stop refinement at maximum depth
|
||||||
|
if (octree.depth(ni) >= max_depth_)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Get the bounding box of the node
|
||||||
|
auto bbox = octree.bbox(ni);
|
||||||
|
|
||||||
|
// Evaluate the function at the corners of the bounding box
|
||||||
|
std::array<FT, 8> corner_values;
|
||||||
|
int index = 0;
|
||||||
|
for (FT x : {bbox.xmin(), bbox.xmax()})
|
||||||
|
for (FT y : {bbox.ymin(), bbox.ymax()})
|
||||||
|
for (FT z : {bbox.zmin(), bbox.zmax()})
|
||||||
|
corner_values[index++] = function_(Point(x, y, z));
|
||||||
|
|
||||||
|
// Check if the function values cross the isovalue
|
||||||
|
bool has_positive = false, has_negative = false;
|
||||||
|
for (const auto& value : corner_values)
|
||||||
|
{
|
||||||
|
if (value > isovalue_)
|
||||||
|
has_positive = true;
|
||||||
|
if (value < isovalue_)
|
||||||
|
has_negative = true;
|
||||||
|
if (has_positive && has_negative)
|
||||||
|
return true; // Refine if the isosurface intersects the voxel
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; // No refinement needed
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is a refinement that is NOT adapted to the isosurface
|
||||||
|
struct Refine_one_eighth
|
||||||
|
{
|
||||||
|
std::size_t min_depth_;
|
||||||
|
std::size_t max_depth_;
|
||||||
|
|
||||||
|
std::size_t octree_dim_;
|
||||||
|
|
||||||
|
Refine_one_eighth(std::size_t min_depth,
|
||||||
|
std::size_t max_depth)
|
||||||
|
: min_depth_(min_depth),
|
||||||
|
max_depth_(max_depth)
|
||||||
|
{
|
||||||
|
octree_dim_ = std::size_t(1) << max_depth_;
|
||||||
|
}
|
||||||
|
|
||||||
|
Octree::Global_coordinates uniform_coordinates(const Octree::Node_index& node_index, const Octree& octree) const
|
||||||
|
{
|
||||||
|
auto coords = octree.global_coordinates(node_index);
|
||||||
|
const std::size_t depth_factor = std::size_t(1) << (max_depth_ - octree.depth(node_index));
|
||||||
|
for(int i=0; i < 3; ++i)
|
||||||
|
coords[i] *= uint32_t(depth_factor);
|
||||||
|
|
||||||
|
return coords;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(const Octree::Node_index& ni, const Octree& octree) const
|
||||||
|
{
|
||||||
|
if(octree.depth(ni) < min_depth_)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if(octree.depth(ni) == max_depth_)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto leaf_coords = uniform_coordinates(ni, octree);
|
||||||
|
|
||||||
|
if(leaf_coords[0] >= octree_dim_ / 2)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(leaf_coords[1] >= octree_dim_ / 2)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(leaf_coords[2] >= octree_dim_ / 2)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Splitter>
|
||||||
|
void run_DC_octree(const CGAL::Bbox_3 bbox,
|
||||||
|
const Splitter& split_predicate,
|
||||||
|
const std::function<FT(const Point&)> function,
|
||||||
|
const std::function<Vector(const Point&)> gradient,
|
||||||
|
const FT isovalue,
|
||||||
|
const std::string& name)
|
||||||
|
{
|
||||||
|
std::vector<Point> bbox_points { { bbox.xmin(), bbox.ymin(), bbox.zmin() },
|
||||||
|
{ bbox.xmax(), bbox.ymax(), bbox.zmax() } };
|
||||||
|
|
||||||
|
Octree octree(bbox_points);
|
||||||
|
octree.refine(split_predicate);
|
||||||
|
|
||||||
|
|
||||||
|
auto leaf_range = octree.traverse(CGAL::Orthtrees::Leaves_traversal<Octree>(octree));
|
||||||
|
std::size_t leaf_counter = std::distance(leaf_range.begin(), leaf_range.end());
|
||||||
|
|
||||||
|
std::cout << "octree has " << leaf_counter << " leaves" << std::endl;
|
||||||
|
|
||||||
|
// fill up values and gradients
|
||||||
|
Values values { function, octree };
|
||||||
|
Gradients gradients { gradient, octree };
|
||||||
|
Domain domain { octree, values, gradients };
|
||||||
|
|
||||||
|
// output containers
|
||||||
|
Point_range points;
|
||||||
|
Polygon_range triangles;
|
||||||
|
|
||||||
|
std::cout << "Running Dual Contouring with isovalue = " << isovalue << std::endl;
|
||||||
|
|
||||||
|
// run Dual Contouring
|
||||||
|
IS::dual_contouring<CGAL::Parallel_if_available_tag>(domain, isovalue, points, triangles,
|
||||||
|
CGAL::parameters::do_not_triangulate_faces(true)
|
||||||
|
.constrain_to_cell(false));
|
||||||
|
|
||||||
|
std::cout << "Output #vertices (DC): " << points.size() << std::endl;
|
||||||
|
std::cout << "Output #triangles (DC): " << triangles.size() << std::endl;
|
||||||
|
|
||||||
|
std::ofstream oo("octree_DC_" + name + ".polylines.txt");
|
||||||
|
oo.precision(17);
|
||||||
|
octree.dump_to_polylines(oo);
|
||||||
|
|
||||||
|
CGAL::IO::write_polygon_soup("DC_" + name + ".off", points, triangles);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Splitter>
|
||||||
|
void run_MC_octree(const CGAL::Bbox_3 bbox,
|
||||||
|
const Splitter& split_predicate,
|
||||||
|
const std::function<FT(const Point&)> function,
|
||||||
|
const FT isovalue,
|
||||||
|
const std::string& name)
|
||||||
|
{
|
||||||
|
std::vector<Point> bbox_points { { bbox.xmin(), bbox.ymin(), bbox.zmin() },
|
||||||
|
{ bbox.xmax(), bbox.ymax(), bbox.zmax() } };
|
||||||
|
|
||||||
|
Octree octree(bbox_points);
|
||||||
|
octree.refine(split_predicate);
|
||||||
|
|
||||||
|
Values values { function, octree };
|
||||||
|
|
||||||
|
Point_range points;
|
||||||
|
Polygon_range triangles;
|
||||||
|
MC_Domain mcdomain { octree, values };
|
||||||
|
|
||||||
|
std::cout << "Running MC" << std::endl;
|
||||||
|
|
||||||
|
CGAL::Isosurfacing::marching_cubes<CGAL::Parallel_if_available_tag>(mcdomain, isovalue, points, triangles);
|
||||||
|
|
||||||
|
std::cout << "Output #vertices (MC): " << points.size() << std::endl;
|
||||||
|
std::cout << "Output #triangles (MC): " << triangles.size() << std::endl;
|
||||||
|
|
||||||
|
std::ofstream oo("octree_MC_" + name + ".polylines.txt");
|
||||||
|
oo.precision(17);
|
||||||
|
octree.dump_to_polylines(oo);
|
||||||
|
|
||||||
|
CGAL::IO::write_polygon_soup("MC_" + name + ".off", points, triangles);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whether you are using MC, TMC, or DC, there is no guarantee for an octree:
|
||||||
|
// it should behave well if your nodes are split with a uniform size around the surface,
|
||||||
|
// but it is sure to produce cracks if you have varying depths around the isosurface.
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
const FT isovalue = (argc > 1) ? std::stod(argv[1]) : 0.3;
|
||||||
|
|
||||||
|
const CGAL::Bbox_3 bbox { -1., -1., -1., 1., 1., 1. };
|
||||||
|
|
||||||
|
Refine_one_eighth one_eight_splitter(3, 5);
|
||||||
|
run_DC_octree(bbox, one_eight_splitter, sphere_function, sphere_gradient, isovalue, "one_eight");
|
||||||
|
|
||||||
|
// This is
|
||||||
|
Refine_around_isovalue isovalue_splitter(3, 5, sphere_function, isovalue);
|
||||||
|
run_DC_octree(bbox, isovalue_splitter, sphere_function, sphere_gradient, isovalue, "sphere_adapted");
|
||||||
|
|
||||||
|
Refine_around_isovalue isvalue_splitter_2(3, 5, blobby_function, isovalue);
|
||||||
|
run_DC_octree(bbox, isvalue_splitter_2, blobby_function, blobby_gradient, isovalue, "blobby_adapted");
|
||||||
|
|
||||||
|
// to illustrate cracks
|
||||||
|
run_MC_octree(bbox, isovalue_splitter, sphere_function, isovalue, "adapted");
|
||||||
|
|
||||||
|
run_MC_octree(bbox, one_eight_splitter, sphere_function, isovalue, "one_eight");
|
||||||
|
|
||||||
|
std::cout << "Done" << std::endl;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,145 @@
|
||||||
|
#include <CGAL/Simple_cartesian.h>
|
||||||
|
|
||||||
|
#include <CGAL/Isosurfacing_3/Cartesian_grid_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/dual_contouring_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Dual_contouring_domain_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Finite_difference_gradient_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Interpolated_discrete_values_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/marching_cubes_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Marching_cubes_domain_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/Image_3.h>
|
||||||
|
#include <CGAL/IO/read_vtk_image_data.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/IO/Image_3.h>
|
||||||
|
#include <CGAL/IO/polygon_soup_io.h>
|
||||||
|
|
||||||
|
#include <vtkNew.h>
|
||||||
|
#include <vtkImageData.h>
|
||||||
|
#include <vtkMetaImageReader.h>
|
||||||
|
#include <vtkXMLImageDataReader.h>
|
||||||
|
#include <vtkTIFFReader.h>
|
||||||
|
#include <vtkNrrdReader.h>
|
||||||
|
#include <vtkMINCImageReader.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using Kernel = CGAL::Simple_cartesian<double>;
|
||||||
|
using FT = typename Kernel::FT;
|
||||||
|
using Point = typename Kernel::Point_3;
|
||||||
|
|
||||||
|
using Grid = CGAL::Isosurfacing::Cartesian_grid_3<Kernel>;
|
||||||
|
using Values = CGAL::Isosurfacing::Interpolated_discrete_values_3<Grid>;
|
||||||
|
using Gradients = CGAL::Isosurfacing::Finite_difference_gradient_3<Kernel>;
|
||||||
|
|
||||||
|
using Point_range = std::vector<Point>;
|
||||||
|
using Polygon_range = std::vector<std::vector<std::size_t> >;
|
||||||
|
|
||||||
|
namespace IS = CGAL::Isosurfacing;
|
||||||
|
|
||||||
|
void run_marching_cubes(const Grid& grid,
|
||||||
|
const FT isovalue,
|
||||||
|
const Values& values)
|
||||||
|
{
|
||||||
|
using Domain = IS::Marching_cubes_domain_3<Grid, Values, IS::Linear_interpolation_edge_intersection>;
|
||||||
|
|
||||||
|
std::cout << "\n ---- " << std::endl;
|
||||||
|
std::cout << "Running Marching Cubes with isovalue = " << isovalue << std::endl;
|
||||||
|
|
||||||
|
// fill up values
|
||||||
|
|
||||||
|
// create a domain from the grid
|
||||||
|
Domain domain { grid, values };
|
||||||
|
|
||||||
|
// prepare collections for the output indexed soup
|
||||||
|
Point_range points;
|
||||||
|
Polygon_range triangles;
|
||||||
|
|
||||||
|
// execute marching cubes
|
||||||
|
IS::marching_cubes<CGAL::Parallel_if_available_tag>(domain, isovalue, points, triangles);
|
||||||
|
|
||||||
|
std::cout << "Output #vertices: " << points.size() << std::endl;
|
||||||
|
std::cout << "Output #triangles: " << triangles.size() << std::endl;
|
||||||
|
|
||||||
|
// save output indexed mesh to a file, in the OFF format
|
||||||
|
CGAL::IO::write_polygon_soup("marching_cubes_vtk_image.off", points, triangles);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_dual_contouring(const Grid& grid,
|
||||||
|
const FT isovalue,
|
||||||
|
const Values& values)
|
||||||
|
{
|
||||||
|
using Domain = IS::Dual_contouring_domain_3<Grid, Values, Gradients, IS::Linear_interpolation_edge_intersection>;
|
||||||
|
|
||||||
|
std::cout << "\n ---- " << std::endl;
|
||||||
|
std::cout << "Running Dual Contouring with isovalue = " << isovalue << std::endl;
|
||||||
|
|
||||||
|
// fill up values and gradients
|
||||||
|
const FT step = CGAL::approximate_sqrt(grid.spacing().squared_length()) * 0.01; // finite difference step
|
||||||
|
Gradients gradients { values, step };
|
||||||
|
Domain domain { grid, values, gradients };
|
||||||
|
|
||||||
|
Point_range points;
|
||||||
|
Polygon_range triangles;
|
||||||
|
|
||||||
|
// run dual contouring isosurfacing
|
||||||
|
IS::dual_contouring<CGAL::Parallel_if_available_tag>(domain, isovalue, points, triangles);
|
||||||
|
|
||||||
|
std::cout << "Output #vertices: " << points.size() << std::endl;
|
||||||
|
std::cout << "Output #triangles: " << triangles.size() << std::endl;
|
||||||
|
CGAL::IO::write_polygon_soup("dual_contouring_vtk_image.off", points, triangles);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename VtkReader>
|
||||||
|
void run(const char* filename,
|
||||||
|
const FT isovalue)
|
||||||
|
{
|
||||||
|
vtkNew<VtkReader> reader;
|
||||||
|
reader->SetFileName(filename);
|
||||||
|
reader->Update();
|
||||||
|
CGAL::Image_3 image = CGAL::IO::read_vtk_image_data(reader->GetOutput());
|
||||||
|
|
||||||
|
// convert image to a Cartesian grid
|
||||||
|
Grid grid;
|
||||||
|
Values values { grid }; // 'values' keeps a reference to the grid
|
||||||
|
if(!IS::IO::convert_image_to_grid(image, grid, values))
|
||||||
|
{
|
||||||
|
std::cerr << "Error: Cannot convert image to Cartesian grid" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Span: " << grid.span() << std::endl;
|
||||||
|
std::cout << "Cell dimensions: " << grid.spacing()[0] << " " << grid.spacing()[1] << " " << grid.spacing()[2] << std::endl;
|
||||||
|
std::cout << "Cell #: " << grid.xdim() << ", " << grid.ydim() << ", " << grid.zdim() << std::endl;
|
||||||
|
|
||||||
|
run_marching_cubes(grid, isovalue, values);
|
||||||
|
|
||||||
|
run_dual_contouring(grid, isovalue, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
const std::string fname = (argc>1) ? argv[1] : CGAL::data_file_path("images/torus_gray_image.vti");
|
||||||
|
|
||||||
|
const char* filename = fname.c_str();
|
||||||
|
const FT isovalue = (argc > 2) ? std::stod(argv[2]) : 3;
|
||||||
|
|
||||||
|
const std::string ext = CGAL::IO::internal::get_file_extension(filename);
|
||||||
|
if(ext == "mhd" || ext == "mha")
|
||||||
|
run<vtkMetaImageReader>(filename, isovalue);
|
||||||
|
else if(ext == "vti")
|
||||||
|
run<vtkXMLImageDataReader>(filename, isovalue);
|
||||||
|
else if(ext == "tif")
|
||||||
|
run<vtkTIFFReader>(filename, isovalue);
|
||||||
|
else if(ext == "nrrd")
|
||||||
|
run<vtkNrrdReader>(filename, isovalue);
|
||||||
|
else if(ext == "mnc")
|
||||||
|
run<vtkMINCImageReader>(filename, isovalue);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << "Error: Unsupported file format" << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
#include <CGAL/Simple_cartesian.h>
|
||||||
|
#include <CGAL/Surface_mesh.h>
|
||||||
|
|
||||||
|
#include <CGAL/Isosurfacing_3/Cartesian_grid_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/dual_contouring_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Dual_contouring_domain_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Value_function_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Gradient_function_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/Bbox_3.h>
|
||||||
|
#include <CGAL/IO/polygon_mesh_io.h>
|
||||||
|
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using Kernel = CGAL::Simple_cartesian<double>;
|
||||||
|
using FT = typename Kernel::FT;
|
||||||
|
using Point = typename Kernel::Point_3;
|
||||||
|
using Vector = typename Kernel::Vector_3;
|
||||||
|
|
||||||
|
using Grid = CGAL::Isosurfacing::Cartesian_grid_3<Kernel>;
|
||||||
|
using Values = CGAL::Isosurfacing::Value_function_3<Grid>;
|
||||||
|
using Gradients = CGAL::Isosurfacing::Gradient_function_3<Grid>;
|
||||||
|
using Domain = CGAL::Isosurfacing::Dual_contouring_domain_3<Grid, Values, Gradients>;
|
||||||
|
|
||||||
|
using Point_range = std::vector<Point>;
|
||||||
|
using Polygon_range = std::vector<std::vector<std::size_t> >;
|
||||||
|
|
||||||
|
using Mesh = CGAL::Surface_mesh<Point>;
|
||||||
|
|
||||||
|
// "Devil" - https://www-sop.inria.fr/galaad/surface/
|
||||||
|
auto devil_value = [](const Point& point)
|
||||||
|
{
|
||||||
|
const FT x = point.x(), y = point.y(), z = point.z();
|
||||||
|
return x*x*x*x + 2*x*x*z*z - 0.36*x*x - y*y*y*y + 0.25*y*y + z*z*z*z;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto devil_gradient = [](const Point& point)
|
||||||
|
{
|
||||||
|
const FT x = point.x(), y = point.y(), z = point.z();
|
||||||
|
|
||||||
|
const FT gx = 4*x*x*x + 4*x*z*z - 0.72*x;
|
||||||
|
const FT gy = -4*y*y*y + 0.5*y;
|
||||||
|
const FT gz = 4*x*x*z + 4*z*z*z;
|
||||||
|
Vector g(gx, gy, gz);
|
||||||
|
return g / std::sqrt(gx*gx + gy*gy + gz*gz);
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
const FT isovalue = (argc > 1) ? std::stod(argv[1]) : 0;
|
||||||
|
const FT box_c = (argc > 2) ? std::abs(std::stod(argv[2])) : 1.;
|
||||||
|
const std::size_t grid_n = (argc > 3) ? std::stoi(argv[3]) : 50;
|
||||||
|
|
||||||
|
// create bounding box and grid
|
||||||
|
const CGAL::Bbox_3 bbox { -box_c, -box_c, -box_c, box_c, box_c, box_c };
|
||||||
|
Grid grid { bbox, CGAL::make_array<std::size_t>(grid_n, grid_n, grid_n) };
|
||||||
|
|
||||||
|
std::cout << "Span: " << grid.span() << std::endl;
|
||||||
|
std::cout << "Cell dimensions: " << grid.spacing()[0] << " " << grid.spacing()[1] << " " << grid.spacing()[2] << std::endl;
|
||||||
|
std::cout << "Cell #: " << grid.xdim() << ", " << grid.ydim() << ", " << grid.zdim() << std::endl;
|
||||||
|
|
||||||
|
// fill up values & gradients
|
||||||
|
Values values { devil_value, grid };
|
||||||
|
Gradients gradients { devil_gradient, grid };
|
||||||
|
|
||||||
|
// Below is equivalent to:
|
||||||
|
// Domain domain { grid, values, gradients };
|
||||||
|
Domain domain = CGAL::Isosurfacing::create_dual_contouring_domain_3(grid, values, gradients);
|
||||||
|
|
||||||
|
Point_range points;
|
||||||
|
Polygon_range triangles;
|
||||||
|
|
||||||
|
// run dual contouring isosurfacing
|
||||||
|
std::cout << "Running Dual Contouring with isovalue = " << isovalue << std::endl;
|
||||||
|
CGAL::Isosurfacing::dual_contouring<CGAL::Parallel_if_available_tag>(domain, isovalue, points, triangles,
|
||||||
|
CGAL::parameters::do_not_triangulate_faces(true)
|
||||||
|
.constrain_to_cell(false));
|
||||||
|
|
||||||
|
std::cout << "Soup #vertices: " << points.size() << std::endl;
|
||||||
|
std::cout << "Soup #triangles: " << triangles.size() << std::endl;
|
||||||
|
|
||||||
|
if(!CGAL::Polygon_mesh_processing::is_polygon_soup_a_polygon_mesh(triangles)) {
|
||||||
|
std::cerr << "Warning: the soup is not a 2-manifold surface, non-manifoldness?..." << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Create the mesh..." << std::endl;
|
||||||
|
Mesh mesh;
|
||||||
|
CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh(points, triangles, mesh);
|
||||||
|
|
||||||
|
CGAL::IO::write_polygon_mesh("dual_contouring.off", mesh, CGAL::parameters::stream_precision(17));
|
||||||
|
|
||||||
|
std::cout << "Done" << std::endl;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,95 @@
|
||||||
|
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
||||||
|
#include <CGAL/Surface_mesh.h>
|
||||||
|
|
||||||
|
#include <CGAL/Isosurfacing_3/Cartesian_grid_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Value_function_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/marching_cubes_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Marching_cubes_domain_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/Bbox_3.h>
|
||||||
|
#include <CGAL/IO/polygon_mesh_io.h>
|
||||||
|
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
|
||||||
|
#include <CGAL/Polygon_mesh_processing/remesh.h>
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel;
|
||||||
|
using FT = typename Kernel::FT;
|
||||||
|
using Point = typename Kernel::Point_3;
|
||||||
|
using Vector = typename Kernel::Vector_3;
|
||||||
|
|
||||||
|
using Grid = CGAL::Isosurfacing::Cartesian_grid_3<Kernel>;
|
||||||
|
using Values = CGAL::Isosurfacing::Value_function_3<Grid>;
|
||||||
|
using Domain = CGAL::Isosurfacing::Marching_cubes_domain_3<Grid, Values>;
|
||||||
|
|
||||||
|
using Point_range = std::vector<Point>;
|
||||||
|
using Polygon_range = std::vector<std::vector<std::size_t> >;
|
||||||
|
|
||||||
|
using Mesh = CGAL::Surface_mesh<Point>;
|
||||||
|
|
||||||
|
auto value_fn = [](const Point& p) -> FT
|
||||||
|
{
|
||||||
|
const FT& x = p.x(), y = p.y(), z = p.z();
|
||||||
|
|
||||||
|
// "Klein Bottle" - https://www-sop.inria.fr/galaad/surface/
|
||||||
|
return -1-4*y*z*z*x*x-2*y+6*z*z*x*x*y*y-16*x*z+16*x*z*y*y+3*x*x+7*y*y+11*z*z-11*z*z*z*z+z*z*z*z*z*z-3*x*x*x*x-7*y*y*y*y+x*x*x*x*x*x+y*y*y*y*y*y-14*z*z*x*x-18*z*z*y*y+3*z*z*z*z*x*x+3*z*z*z*z*y*y-10*x*x*y*y-4*y*y*y*z*z+3*z*z*x*x*x*x+3*z*z*y*y*y*y+16*x*x*x*z+3*x*x*x*x*y*y+3*x*x*y*y*y*y+4*x*x*y-12*z*z*y-2*x*x*x*x*y-4*x*x*y*y*y-2*z*z*z*z*y+16*x*z*z*z+12*y*y*y-2*y*y*y*y*y-32*x*z*y;
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
const FT isovalue = (argc > 1) ? std::stod(argv[1]) : 0.1;
|
||||||
|
const FT box_c = (argc > 2) ? std::abs(std::stod(argv[2])) : 5.;
|
||||||
|
const std::size_t grid_n = (argc > 3) ? std::stoi(argv[3]) : 50;
|
||||||
|
|
||||||
|
// create bounding box and grid
|
||||||
|
const CGAL::Bbox_3 bbox { -box_c, -box_c, -box_c, box_c, box_c, box_c };
|
||||||
|
Grid grid { bbox, CGAL::make_array<std::size_t>(grid_n, grid_n, grid_n) };
|
||||||
|
|
||||||
|
std::cout << "Span: " << grid.span() << std::endl;
|
||||||
|
std::cout << "Cell dimensions: " << grid.spacing()[0] << " " << grid.spacing()[1] << " " << grid.spacing()[2] << std::endl;
|
||||||
|
std::cout << "Cell #: " << grid.xdim() << ", " << grid.ydim() << ", " << grid.zdim() << std::endl;
|
||||||
|
|
||||||
|
// fill up values
|
||||||
|
Values values { value_fn, grid };
|
||||||
|
|
||||||
|
// Below is equivalent to:
|
||||||
|
// Domain domain { grid, values };
|
||||||
|
Domain domain = CGAL::Isosurfacing::create_marching_cubes_domain_3(grid, values);
|
||||||
|
|
||||||
|
Point_range points;
|
||||||
|
Polygon_range triangles;
|
||||||
|
|
||||||
|
// run marching cubes isosurfacing
|
||||||
|
std::cout << "Running Marching Cubes with isovalue = " << isovalue << std::endl;
|
||||||
|
CGAL::Isosurfacing::marching_cubes<CGAL::Parallel_if_available_tag>(domain, isovalue, points, triangles,
|
||||||
|
CGAL::parameters::use_topologically_correct_marching_cubes(true));
|
||||||
|
|
||||||
|
std::cout << "Soup #vertices: " << points.size() << std::endl;
|
||||||
|
std::cout << "Soup #triangles: " << triangles.size() << std::endl;
|
||||||
|
|
||||||
|
if(!CGAL::Polygon_mesh_processing::is_polygon_soup_a_polygon_mesh(triangles)) {
|
||||||
|
std::cerr << "Warning: the soup is not a 2-manifold surface, non-manifoldness?..." << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Convert the soup to a triangle mesh..." << std::endl;
|
||||||
|
Mesh mesh;
|
||||||
|
CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh(points, triangles, mesh);
|
||||||
|
|
||||||
|
CGAL::IO::write_polygon_mesh("marching_cubes.off", mesh, CGAL::parameters::stream_precision(17));
|
||||||
|
|
||||||
|
// Let's remesh it to something nicer looking
|
||||||
|
std::cout << "Remeshing..." << std::endl;
|
||||||
|
const FT target_edge_length = box_c / 50;
|
||||||
|
CGAL::Polygon_mesh_processing::isotropic_remeshing(faces(mesh), target_edge_length, mesh,
|
||||||
|
CGAL::parameters::number_of_iterations(5)
|
||||||
|
.number_of_relaxation_steps(5));
|
||||||
|
|
||||||
|
CGAL::IO::write_polygon_mesh("marching_cubes-remeshed.off", mesh, CGAL::parameters::stream_precision(17));
|
||||||
|
|
||||||
|
std::cout << "Done" << std::endl;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
#include <CGAL/Simple_cartesian.h>
|
||||||
|
|
||||||
|
#include <CGAL/Isosurfacing_3/Cartesian_grid_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Value_function_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/marching_cubes_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Marching_cubes_domain_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/Real_timer.h>
|
||||||
|
#include <CGAL/IO/polygon_soup_io.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using Kernel = CGAL::Simple_cartesian<double>;
|
||||||
|
using FT = typename Kernel::FT;
|
||||||
|
using Point = typename Kernel::Point_3;
|
||||||
|
using Vector = typename Kernel::Vector_3;
|
||||||
|
|
||||||
|
using Grid = CGAL::Isosurfacing::Cartesian_grid_3<Kernel>;
|
||||||
|
using Values = CGAL::Isosurfacing::Value_function_3<Grid>;
|
||||||
|
using Domain = CGAL::Isosurfacing::Marching_cubes_domain_3<Grid, Values>;
|
||||||
|
|
||||||
|
using Point_range = std::vector<Point>;
|
||||||
|
using Polygon_range = std::vector<std::vector<std::size_t> >;
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
const FT isovalue = (argc > 1) ? std::stod(argv[1]) : 0.8;
|
||||||
|
|
||||||
|
// create bounding box and grid
|
||||||
|
const CGAL::Bbox_3 bbox { -1., -1., -1., 1., 1., 1. };
|
||||||
|
Grid grid { bbox, CGAL::make_array<std::size_t>(30, 30, 30) };
|
||||||
|
|
||||||
|
std::cout << "Span: " << grid.span() << std::endl;
|
||||||
|
std::cout << "Cell dimensions: " << grid.spacing()[0] << " " << grid.spacing()[1] << " " << grid.spacing()[2] << std::endl;
|
||||||
|
std::cout << "Cell #: " << grid.xdim() << ", " << grid.ydim() << ", " << grid.zdim() << std::endl;
|
||||||
|
|
||||||
|
// fill up values
|
||||||
|
auto sphere_value_fn = [](const Point& p) -> FT
|
||||||
|
{
|
||||||
|
return sqrt(p.x()*p.x() + p.y()*p.y() + p.z()*p.z());
|
||||||
|
};
|
||||||
|
|
||||||
|
Values values { sphere_value_fn, grid };
|
||||||
|
Domain domain { grid, values };
|
||||||
|
|
||||||
|
// MC base version
|
||||||
|
{
|
||||||
|
Point_range points;
|
||||||
|
Polygon_range triangles;
|
||||||
|
|
||||||
|
CGAL::Real_timer timer;
|
||||||
|
timer.start();
|
||||||
|
|
||||||
|
// run marching cubes isosurfacing
|
||||||
|
std::cout << "Running Marching Cubes with isovalue = " << isovalue << std::endl;
|
||||||
|
CGAL::Isosurfacing::marching_cubes<CGAL::Parallel_if_available_tag>(domain, isovalue, points, triangles,
|
||||||
|
CGAL::parameters::use_topologically_correct_marching_cubes(false));
|
||||||
|
|
||||||
|
timer.stop();
|
||||||
|
|
||||||
|
std::cout << "Output #vertices: " << points.size() << std::endl;
|
||||||
|
std::cout << "Output #triangles: " << triangles.size() << std::endl;
|
||||||
|
std::cout << "Elapsed time: " << timer.time() << " seconds" << std::endl;
|
||||||
|
CGAL::IO::write_polygon_soup("marching_cubes.off", points, triangles);
|
||||||
|
}
|
||||||
|
|
||||||
|
// MC topologically correct version
|
||||||
|
{
|
||||||
|
Point_range points;
|
||||||
|
Polygon_range triangles;
|
||||||
|
|
||||||
|
CGAL::Real_timer timer;
|
||||||
|
timer.start();
|
||||||
|
|
||||||
|
// run marching cubes isosurfacing
|
||||||
|
std::cout << "Running Marching Cubes (TMC) with isovalue = " << isovalue << std::endl;
|
||||||
|
CGAL::Isosurfacing::marching_cubes<CGAL::Parallel_if_available_tag>(domain, isovalue, points, triangles,
|
||||||
|
CGAL::parameters::use_topologically_correct_marching_cubes(true));
|
||||||
|
|
||||||
|
timer.stop();
|
||||||
|
|
||||||
|
std::cout << "Output #vertices: " << points.size() << std::endl;
|
||||||
|
std::cout << "Output #triangles: " << triangles.size() << std::endl;
|
||||||
|
std::cout << "Elapsed time: " << timer.time() << " seconds" << std::endl;
|
||||||
|
CGAL::IO::write_polygon_soup("marching_cubes_TMC.off", points, triangles);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Done" << std::endl;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,409 @@
|
||||||
|
// Copyright (c) 2022-2024 INRIA Sophia-Antipolis (France), GeometryFactory (France).
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of CGAL (www.cgal.org).
|
||||||
|
//
|
||||||
|
// $URL$
|
||||||
|
// $Id$
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
|
||||||
|
//
|
||||||
|
// Author(s) : Julian Stahl
|
||||||
|
// Mael Rouxel-Labbé
|
||||||
|
|
||||||
|
#ifndef CGAL_ISOSURFACING_3_CARTESIAN_GRID_3_H
|
||||||
|
#define CGAL_ISOSURFACING_3_CARTESIAN_GRID_3_H
|
||||||
|
|
||||||
|
#include <CGAL/license/Isosurfacing_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/Isosurfacing_3/internal/partition_traits_Cartesian_grid.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/IO/OBJ.h>
|
||||||
|
|
||||||
|
#include <CGAL/assertions.h>
|
||||||
|
#include <CGAL/boost/graph/named_params_helper.h>
|
||||||
|
#include <CGAL/Named_function_parameters.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace CGAL {
|
||||||
|
namespace Isosurfacing {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup IS_Partitions_helpers_grp
|
||||||
|
*
|
||||||
|
* A policy to choose whether grid vertex locations should be cached, or recomputed at each access.
|
||||||
|
*
|
||||||
|
* \tparam Tag a tag that is either `Tag_true` (locations are cached) or `Tag_false` (locations are not cached).
|
||||||
|
*/
|
||||||
|
template <typename Tag>
|
||||||
|
struct Grid_vertex_memory_policy : public Tag { };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup IS_Partitions_helpers_grp
|
||||||
|
*
|
||||||
|
* A convenience alias for the policy that caches grid vertex locations.
|
||||||
|
*/
|
||||||
|
using Cache_vertex_locations = Grid_vertex_memory_policy<Tag_true>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup IS_Partitions_helpers_grp
|
||||||
|
*
|
||||||
|
* A convenience alias for the policy that does not cache grid vertex locations.
|
||||||
|
*/
|
||||||
|
using Do_not_cache_vertex_locations = Grid_vertex_memory_policy<Tag_false>;
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template <typename GeomTraits,
|
||||||
|
typename MemoryPolicy = Do_not_cache_vertex_locations>
|
||||||
|
struct Cartesian_grid_location
|
||||||
|
{
|
||||||
|
using Point_3 = typename GeomTraits::Point_3;
|
||||||
|
using Vector_3 = typename GeomTraits::Vector_3;
|
||||||
|
using Iso_cuboid_3 = typename GeomTraits::Iso_cuboid_3;
|
||||||
|
|
||||||
|
Cartesian_grid_location() { } // just for compilation
|
||||||
|
|
||||||
|
Cartesian_grid_location(const Iso_cuboid_3& /*span*/,
|
||||||
|
const std::array<std::size_t, 3>& /*dims*/,
|
||||||
|
const Vector_3& /*spacing*/)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
template <typename Grid>
|
||||||
|
Point_3 operator()(const std::size_t i,
|
||||||
|
const std::size_t j,
|
||||||
|
const std::size_t k,
|
||||||
|
const Grid& g) const
|
||||||
|
{
|
||||||
|
typename GeomTraits::Compute_x_3 x_coord = g.geom_traits().compute_x_3_object();
|
||||||
|
typename GeomTraits::Compute_y_3 y_coord = g.geom_traits().compute_y_3_object();
|
||||||
|
typename GeomTraits::Compute_z_3 z_coord = g.geom_traits().compute_z_3_object();
|
||||||
|
typename GeomTraits::Construct_point_3 point = g.geom_traits().construct_point_3_object();
|
||||||
|
typename GeomTraits::Construct_vertex_3 vertex = g.geom_traits().construct_vertex_3_object();
|
||||||
|
|
||||||
|
const Point_3& min_p = vertex(g.span(), 0);
|
||||||
|
return point(x_coord(min_p) + typename GeomTraits::FT(i) * g.spacing()[0],
|
||||||
|
y_coord(min_p) + typename GeomTraits::FT(j) * g.spacing()[1],
|
||||||
|
z_coord(min_p) + typename GeomTraits::FT(k) * g.spacing()[2]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename GeomTraits>
|
||||||
|
struct Cartesian_grid_location<GeomTraits, Cache_vertex_locations>
|
||||||
|
{
|
||||||
|
using Point_3 = typename GeomTraits::Point_3;
|
||||||
|
using Vector_3 = typename GeomTraits::Vector_3;
|
||||||
|
using Iso_cuboid_3 = typename GeomTraits::Iso_cuboid_3;
|
||||||
|
|
||||||
|
std::vector<Point_3> m_points;
|
||||||
|
|
||||||
|
Cartesian_grid_location() { } // just for compilation
|
||||||
|
|
||||||
|
Cartesian_grid_location(const Iso_cuboid_3& span,
|
||||||
|
const std::array<std::size_t, 3>& dims,
|
||||||
|
const Vector_3& spacing)
|
||||||
|
{
|
||||||
|
m_points.reserve(dims[0] * dims[1] * dims[2]);
|
||||||
|
for(std::size_t k=0; k<dims[2]; ++k)
|
||||||
|
for(std::size_t j=0; j<dims[1]; ++j)
|
||||||
|
for(std::size_t i=0; i<dims[0]; ++i)
|
||||||
|
m_points.emplace_back((span.min)() + Vector_3(i * spacing[0], j * spacing[1], k * spacing[2]));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Grid>
|
||||||
|
const Point_3& operator()(const std::size_t i,
|
||||||
|
const std::size_t j,
|
||||||
|
const std::size_t k,
|
||||||
|
const Grid& g) const
|
||||||
|
{
|
||||||
|
const std::size_t linear_index = g.linear_index(i, j, k);
|
||||||
|
CGAL_precondition(linear_index < m_points.size());
|
||||||
|
return m_points[linear_index];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup IS_Partitions_grp
|
||||||
|
*
|
||||||
|
* \cgalModels{IsosurfacingPartition_3}
|
||||||
|
*
|
||||||
|
* \brief The class `Cartesian_grid_3` represents a 3D %Cartesian grid, that is the partition of
|
||||||
|
* an iso-cuboid into identical iso-cuboidal cells.
|
||||||
|
*
|
||||||
|
* The class `Cartesian_grid_3` is one of the possible space partitioning data structures
|
||||||
|
* that can be used along with value and gradient fields to make up a domain.
|
||||||
|
*
|
||||||
|
* \tparam GeomTraits must be a model of `IsosurfacingTraits_3`.
|
||||||
|
* \tparam MemoryPolicy whether the geometric locations of the grid vertices are stored or not.
|
||||||
|
* Possible values are `CGAL::Isosurfacing::Cache_vertex_locations` and `CGAL::Isosurfacing::Do_not_cache_vertex_locations`.
|
||||||
|
*
|
||||||
|
* \sa `CGAL::Isosurfacing::Marching_cubes_domain_3()`
|
||||||
|
* \sa `CGAL::Isosurfacing::Dual_contouring_domain_3()`
|
||||||
|
*/
|
||||||
|
template <typename GeomTraits,
|
||||||
|
typename MemoryPolicy = Do_not_cache_vertex_locations>
|
||||||
|
class Cartesian_grid_3
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Geom_traits = GeomTraits;
|
||||||
|
using FT = typename Geom_traits::FT;
|
||||||
|
using Point_3 = typename Geom_traits::Point_3;
|
||||||
|
using Vector_3 = typename Geom_traits::Vector_3;
|
||||||
|
using Iso_cuboid_3 = typename Geom_traits::Iso_cuboid_3;
|
||||||
|
|
||||||
|
using Positioner = internal::Cartesian_grid_location<GeomTraits, MemoryPolicy>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Iso_cuboid_3 m_span;
|
||||||
|
std::array<std::size_t, 3> m_dims;
|
||||||
|
Vector_3 m_spacing;
|
||||||
|
|
||||||
|
Positioner m_positioner;
|
||||||
|
Geom_traits m_gt;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void initialize_spacing()
|
||||||
|
{
|
||||||
|
typename Geom_traits::Compute_x_3 x_coord = m_gt.compute_x_3_object();
|
||||||
|
typename Geom_traits::Compute_y_3 y_coord = m_gt.compute_y_3_object();
|
||||||
|
typename Geom_traits::Compute_z_3 z_coord = m_gt.compute_z_3_object();
|
||||||
|
typename Geom_traits::Construct_vector_3 vector = m_gt.construct_vector_3_object();
|
||||||
|
typename Geom_traits::Construct_vertex_3 vertex = m_gt.construct_vertex_3_object();
|
||||||
|
|
||||||
|
// calculate grid spacing
|
||||||
|
const Point_3& min_p = vertex(m_span, 0);
|
||||||
|
const Point_3& max_p = vertex(m_span, 7);
|
||||||
|
const FT x_span = x_coord(max_p) - x_coord(min_p);
|
||||||
|
const FT y_span = y_coord(max_p) - y_coord(min_p);
|
||||||
|
const FT z_span = z_coord(max_p) - z_coord(min_p);
|
||||||
|
|
||||||
|
const FT d_x = x_span / FT(m_dims[0] - 1);
|
||||||
|
const FT d_y = y_span / FT(m_dims[1] - 1);
|
||||||
|
const FT d_z = z_span / FT(m_dims[2] - 1);
|
||||||
|
|
||||||
|
m_spacing = vector(d_x, d_y, d_z);
|
||||||
|
|
||||||
|
m_positioner = Positioner { m_span, m_dims, m_spacing };
|
||||||
|
}
|
||||||
|
|
||||||
|
void initialize_dimensions()
|
||||||
|
{
|
||||||
|
typename Geom_traits::Compute_x_3 x_coord = m_gt.compute_x_3_object();
|
||||||
|
typename Geom_traits::Compute_y_3 y_coord = m_gt.compute_y_3_object();
|
||||||
|
typename Geom_traits::Compute_z_3 z_coord = m_gt.compute_z_3_object();
|
||||||
|
typename Geom_traits::Construct_vertex_3 vertex = m_gt.construct_vertex_3_object();
|
||||||
|
|
||||||
|
const Point_3& min_p = vertex(m_span, 0);
|
||||||
|
const Point_3& max_p = vertex(m_span, 7);
|
||||||
|
const FT x_span = x_coord(max_p) - x_coord(min_p);
|
||||||
|
const FT y_span = y_coord(max_p) - y_coord(min_p);
|
||||||
|
const FT z_span = z_coord(max_p) - z_coord(min_p);
|
||||||
|
|
||||||
|
m_dims[0] = static_cast<std::size_t>(std::ceil(CGAL::to_double(x_span / m_spacing[0]))) + 1;
|
||||||
|
m_dims[1] = static_cast<std::size_t>(std::ceil(CGAL::to_double(y_span / m_spacing[1]))) + 1;
|
||||||
|
m_dims[2] = static_cast<std::size_t>(std::ceil(CGAL::to_double(z_span / m_spacing[2]))) + 1;
|
||||||
|
|
||||||
|
m_positioner = Positioner { m_span, m_dims, m_spacing };
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*!
|
||||||
|
* \brief Default constructor
|
||||||
|
*/
|
||||||
|
Cartesian_grid_3()
|
||||||
|
: m_span{Point_3{0, 0, 0}, Point_3{0, 0, 0}},
|
||||||
|
m_dims{2, 2, 2},
|
||||||
|
m_spacing{0, 0, 0},
|
||||||
|
m_gt{Geom_traits()}
|
||||||
|
{ }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief creates a %Cartesian grid with `dimensions[0]*dimensions[1]*dimensions[2]` grid vertices.
|
||||||
|
*
|
||||||
|
* The grid covers the space described by the iso-cuboid `span`.
|
||||||
|
*
|
||||||
|
* \param span the geometric span of the grid
|
||||||
|
* \param dimensions the number of grid vertices in the `x`, `y`, and `z` directions
|
||||||
|
* \param gt the geometric traits
|
||||||
|
*
|
||||||
|
* \pre all dimensions are strictly positive.
|
||||||
|
*/
|
||||||
|
Cartesian_grid_3(const Iso_cuboid_3& span,
|
||||||
|
const std::array<std::size_t, 3>& dimensions,
|
||||||
|
const Geom_traits& gt = Geom_traits())
|
||||||
|
: m_span{span},
|
||||||
|
m_dims{dimensions},
|
||||||
|
m_gt{gt}
|
||||||
|
{
|
||||||
|
initialize_spacing();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief creates a %Cartesian grid with `dimensions[0]*dimensions[1]*dimensions[2]` grid vertices.
|
||||||
|
*
|
||||||
|
* The grid covers the space described by the iso-cuboid span,
|
||||||
|
* itself described through two diagonal corners `p` and `q`.
|
||||||
|
*
|
||||||
|
* \param p the lexicographically smallest corner of the iso-cuboid
|
||||||
|
* \param q the lexicographically largest corner of the iso-cuboid
|
||||||
|
* \param dimensions the number of grid vertices in the `x`, `y`, and `z` directions
|
||||||
|
* \param gt the geometric traits
|
||||||
|
*
|
||||||
|
* \pre `p` is lexicographically strictly smaller than `q`
|
||||||
|
* \pre all dimensions are strictly positive.
|
||||||
|
*/
|
||||||
|
Cartesian_grid_3(const Point_3& p, const Point_3& q,
|
||||||
|
const std::array<std::size_t, 3>& dimensions,
|
||||||
|
const Geom_traits& gt = Geom_traits())
|
||||||
|
: Cartesian_grid_3{Iso_cuboid_3{p, q}, dimensions, gt}
|
||||||
|
{ }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief creates a %Cartesian grid using a prescribed grid step `spacing`.
|
||||||
|
*
|
||||||
|
* The grid covers the space described by the iso-cuboid `span`.
|
||||||
|
*
|
||||||
|
* \param span the geometric span of the grid
|
||||||
|
* \param spacing the dimension of the paving cell, in the `x`, `y`, and `z` directions
|
||||||
|
* \param gt the geometric traits
|
||||||
|
*
|
||||||
|
* \pre the diagonal of `span` has length a multiple of `spacing`
|
||||||
|
*/
|
||||||
|
Cartesian_grid_3(const Iso_cuboid_3& span,
|
||||||
|
const Vector_3& spacing,
|
||||||
|
const Geom_traits& gt = Geom_traits())
|
||||||
|
: m_span{span},
|
||||||
|
m_spacing{spacing},
|
||||||
|
m_gt{gt}
|
||||||
|
{
|
||||||
|
initialize_dimensions();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief creates a %Cartesian grid using a prescribed grid step.
|
||||||
|
*
|
||||||
|
* The grid covers the space described by an iso-cuboid, itself described through two diagonal corners.
|
||||||
|
*
|
||||||
|
* \param p the lexicographically smallest corner of the iso-cuboid
|
||||||
|
* \param q the lexicographically largest corner of the iso-cuboid
|
||||||
|
* \param spacing the dimension of the paving cell, in the `x`, `y`, and `z` directions, respectively.
|
||||||
|
* \param gt the geometric traits
|
||||||
|
*
|
||||||
|
* \pre `p` is lexicographically strictly smaller than `q`
|
||||||
|
* \pre the diagonal of the iso-cuboid has length a multiple of `spacing`
|
||||||
|
*/
|
||||||
|
Cartesian_grid_3(const Point_3& p, const Point_3& q,
|
||||||
|
const Vector_3& spacing,
|
||||||
|
const Geom_traits& gt = Geom_traits())
|
||||||
|
: Cartesian_grid_3{Iso_cuboid_3{p, q}, spacing, gt}
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* returns the geometric traits class.
|
||||||
|
*/
|
||||||
|
const Geom_traits& geom_traits() const
|
||||||
|
{
|
||||||
|
return m_gt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns an iso-cuboid representing the geometric span of the %Cartesian grid.
|
||||||
|
*/
|
||||||
|
const Iso_cuboid_3& span() const { return m_span; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the number of grid vertices in the `x` direction.
|
||||||
|
*/
|
||||||
|
std::size_t xdim() const { return m_dims[0]; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the number of grid vertices in the `y` direction.
|
||||||
|
*/
|
||||||
|
std::size_t ydim() const { return m_dims[1]; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the number of grid vertices in the `z` direction.
|
||||||
|
*/
|
||||||
|
std::size_t zdim() const { return m_dims[2]; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the spacing of the %Cartesian grid, that is a vector whose coordinates are
|
||||||
|
* the grid steps in the `x`, `y`, and `z` directions, respectively.
|
||||||
|
*/
|
||||||
|
const Vector_3& spacing() const { return m_spacing; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* returns the index of a grid cell given its indices (i.e., `(k * y_dim + j) * x_dim + i`).
|
||||||
|
*/
|
||||||
|
std::size_t linear_index(const std::size_t i,
|
||||||
|
const std::size_t j,
|
||||||
|
const std::size_t k) const
|
||||||
|
{
|
||||||
|
CGAL_precondition(i < m_dims[0] && j < m_dims[1] && k < m_dims[2]);
|
||||||
|
return (k * m_dims[1] + j) * m_dims[0] + i;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \brief returns the coordinates of the grid cell that contains a given point.
|
||||||
|
*
|
||||||
|
* For points on the boundary between two cells, the smaller index is returned.
|
||||||
|
*
|
||||||
|
* \param p the point to be located
|
||||||
|
*
|
||||||
|
* \pre `p` is inside the grid.
|
||||||
|
*/
|
||||||
|
std::array<std::size_t, 3> index(const Point_3& p) const
|
||||||
|
{
|
||||||
|
typename Geom_traits::Compute_x_3 x_coord = m_gt.compute_x_3_object();
|
||||||
|
typename Geom_traits::Compute_y_3 y_coord = m_gt.compute_y_3_object();
|
||||||
|
typename Geom_traits::Compute_z_3 z_coord = m_gt.compute_z_3_object();
|
||||||
|
typename Geom_traits::Construct_vertex_3 vertex = m_gt.construct_vertex_3_object();
|
||||||
|
|
||||||
|
const Point_3& min_p = vertex(m_span, 0);
|
||||||
|
std::size_t i = std::size_t((x_coord(p) - x_coord(min_p)) / x_coord(m_spacing));
|
||||||
|
std::size_t j = std::size_t((y_coord(p) - y_coord(min_p)) / y_coord(m_spacing));
|
||||||
|
std::size_t k = std::size_t((z_coord(p) - z_coord(min_p)) / z_coord(m_spacing));
|
||||||
|
|
||||||
|
if(i == xdim() - 1)
|
||||||
|
--i;
|
||||||
|
if(j == ydim() - 1)
|
||||||
|
--j;
|
||||||
|
if(k == zdim() - 1)
|
||||||
|
--k;
|
||||||
|
|
||||||
|
return {i, j, k};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Geometry
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \brief returns the geometric location of the grid vertex described by its three indices.
|
||||||
|
*
|
||||||
|
* Depending on the value of the template parameter `MemoryPolicy`, locations might not be stored
|
||||||
|
* but calculated on-the-fly.
|
||||||
|
*
|
||||||
|
* \param i the index in the `x` direction
|
||||||
|
* \param j the index in the `y` direction
|
||||||
|
* \param k the index in the `z` direction
|
||||||
|
*
|
||||||
|
* \pre `i < xdim()` and `j < ydim()` and `k < zdim()`
|
||||||
|
*
|
||||||
|
* \returns a const reference or a newly constructed point, depending on the caching policy.
|
||||||
|
*/
|
||||||
|
decltype(auto) /*Point_3*/ point(const std::size_t i,
|
||||||
|
const std::size_t j,
|
||||||
|
const std::size_t k) const
|
||||||
|
{
|
||||||
|
return m_positioner(i, j, k, *this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Isosurfacing
|
||||||
|
} // namespace CGAL
|
||||||
|
|
||||||
|
#endif // CGAL_ISOSURFACING_3_CARTESIAN_GRID_3_H
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
// Copyright (c) 2022-2024 INRIA Sophia-Antipolis (France), GeometryFactory (France).
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of CGAL (www.cgal.org).
|
||||||
|
//
|
||||||
|
// $URL$
|
||||||
|
// $Id$
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
|
||||||
|
//
|
||||||
|
// Author(s) : Julian Stahl
|
||||||
|
// Mael Rouxel-Labbé
|
||||||
|
|
||||||
|
#ifndef CGAL_ISOSURFACING_3_DUAL_CONTOURING_DOMAIN_3_H
|
||||||
|
#define CGAL_ISOSURFACING_3_DUAL_CONTOURING_DOMAIN_3_H
|
||||||
|
|
||||||
|
#include <CGAL/license/Isosurfacing_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/Isosurfacing_3/internal/Isosurfacing_domain_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/edge_intersection_oracles_3.h>
|
||||||
|
|
||||||
|
namespace CGAL {
|
||||||
|
namespace Isosurfacing {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup IS_Domains_grp
|
||||||
|
*
|
||||||
|
* \cgalModels{IsosurfacingDomainWithGradient_3}
|
||||||
|
*
|
||||||
|
* \brief A domain that can be used as input in the %Dual Contouring algorithm.
|
||||||
|
*
|
||||||
|
* \details This class is essentially a wrapper around the different bricks provided by its
|
||||||
|
* template parameters: `Partition` provides the spatial partitioning, `ValueField` and `GradientField`
|
||||||
|
* the values and gradients that define the isosurface. The optional template parameter
|
||||||
|
* `EdgeIntersectionOracle` gives control over the method used to compute edge-isosurface intersection points.
|
||||||
|
*
|
||||||
|
* \tparam Partition must be a model of `IsosurfacingPartition_3`
|
||||||
|
* \tparam ValueField must be a model of `IsosurfacingValueField_3`
|
||||||
|
* \tparam GradientField must be a model of `IsosurfacingGradientField_3`
|
||||||
|
* \tparam EdgeIntersectionOracle must be a model of `IsosurfacingEdgeIntersectionOracle_3`
|
||||||
|
*
|
||||||
|
* \sa `CGAL::Isosurfacing::dual_contouring()`
|
||||||
|
* \sa `CGAL::Isosurfacing::Marching_cubes_domain_3()`
|
||||||
|
*/
|
||||||
|
template <typename Partition,
|
||||||
|
typename ValueField,
|
||||||
|
typename GradientField,
|
||||||
|
typename EdgeIntersectionOracle = Dichotomy_edge_intersection>
|
||||||
|
class Dual_contouring_domain_3
|
||||||
|
#ifndef DOXYGEN_RUNNING
|
||||||
|
: public internal::Isosurfacing_domain_3<Partition, ValueField, GradientField, EdgeIntersectionOracle>
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
using Base = internal::Isosurfacing_domain_3<Partition, ValueField, GradientField, EdgeIntersectionOracle>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \brief constructs a domain that can be used with the %Dual Contouring algorithm.
|
||||||
|
*
|
||||||
|
* \param partition the space partitioning data structure
|
||||||
|
* \param values a continuous field of scalar values, defined over the geometric span of `partition`
|
||||||
|
* \param gradients a continuous field of normalized vectors, defined over the geometric span of `partition`
|
||||||
|
* \param intersection_oracle the oracle for edge-isosurface intersection computation
|
||||||
|
*
|
||||||
|
* \warning the domain class keeps a reference to the `partition`, `values` and `gradients` objects.
|
||||||
|
* As such, users must ensure that the lifetimes of these objects exceed that of the domain object.
|
||||||
|
*/
|
||||||
|
Dual_contouring_domain_3(const Partition& partition,
|
||||||
|
const ValueField& values,
|
||||||
|
const GradientField& gradients,
|
||||||
|
const EdgeIntersectionOracle& intersection_oracle = EdgeIntersectionOracle())
|
||||||
|
: Base(partition, values, gradients, intersection_oracle)
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup IS_Domains_grp
|
||||||
|
*
|
||||||
|
* \brief creates a new instance of a domain that can be used with the %Dual Contouring algorithm.
|
||||||
|
*
|
||||||
|
* \tparam Partition must be a model of `IsosurfacingPartition_3`
|
||||||
|
* \tparam ValueField must be a model of `IsosurfacingValueField_3`
|
||||||
|
* \tparam GradientField must be a model of `IsosurfacingGradientField_3`
|
||||||
|
* \tparam EdgeIntersectionOracle must be a model of `IsosurfacingEdgeIntersectionOracle_3`
|
||||||
|
*
|
||||||
|
* \param partition the space partitioning data structure
|
||||||
|
* \param values a continuous field of scalar values, defined over the geometric span of `partition`
|
||||||
|
* \param gradients a continuous field of normalized vectors, defined over the geometric span of `partition`
|
||||||
|
* \param intersection_oracle the oracle for edge-isosurface intersection computation
|
||||||
|
*
|
||||||
|
* \warning the domain class keeps a reference to the `partition`, `values` and `gradients` objects.
|
||||||
|
* As such, users must ensure that the lifetimes of these objects exceed that of the domain object.
|
||||||
|
*/
|
||||||
|
template <typename Partition,
|
||||||
|
typename ValueField,
|
||||||
|
typename GradientField,
|
||||||
|
typename EdgeIntersectionOracle = Dichotomy_edge_intersection>
|
||||||
|
Dual_contouring_domain_3<Partition, ValueField, GradientField, EdgeIntersectionOracle>
|
||||||
|
create_dual_contouring_domain_3(const Partition& partition,
|
||||||
|
const ValueField& values,
|
||||||
|
const GradientField& gradients,
|
||||||
|
const EdgeIntersectionOracle& intersection_oracle = EdgeIntersectionOracle())
|
||||||
|
{
|
||||||
|
return { partition, values, gradients, intersection_oracle };
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Isosurfacing
|
||||||
|
} // namespace CGAL
|
||||||
|
|
||||||
|
#endif // CGAL_ISOSURFACING_3_DUAL_CONTOURING_DOMAIN_3_H
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
// Copyright (c) 2022-2024 INRIA Sophia-Antipolis (France), GeometryFactory (France).
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of CGAL (www.cgal.org).
|
||||||
|
//
|
||||||
|
// $URL$
|
||||||
|
// $Id$
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
|
||||||
|
//
|
||||||
|
// Author(s) : Julian Stahl
|
||||||
|
// Mael Rouxel-Labbé
|
||||||
|
|
||||||
|
#ifndef CGAL_ISOSURFACING_3_FINITE_DIFFERENCE_GRADIENT_3_H
|
||||||
|
#define CGAL_ISOSURFACING_3_FINITE_DIFFERENCE_GRADIENT_3_H
|
||||||
|
|
||||||
|
#include <CGAL/license/Isosurfacing_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/number_utils.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace CGAL {
|
||||||
|
namespace Isosurfacing {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup IS_Fields_grp
|
||||||
|
*
|
||||||
|
* \cgalModels{IsosurfacingGradientField_3}
|
||||||
|
*
|
||||||
|
* \brief Class template for a gradient that is calculated using finite differences.
|
||||||
|
*
|
||||||
|
* \details This gradient function evaluates a value function at six points that are
|
||||||
|
* a given distance `delta` away from the queried point along the %Cartesian axes.
|
||||||
|
*
|
||||||
|
* \tparam GeomTraits must be a model of `IsosurfacingTraits_3`.
|
||||||
|
*/
|
||||||
|
template <typename GeomTraits>
|
||||||
|
class Finite_difference_gradient_3
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Geom_traits = GeomTraits;
|
||||||
|
using FT = typename Geom_traits::FT;
|
||||||
|
using Point_3 = typename Geom_traits::Point_3;
|
||||||
|
using Vector_3 = typename Geom_traits::Vector_3;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::function<FT(const Point_3&)> m_function;
|
||||||
|
const FT m_delta, m_half_step_inv;
|
||||||
|
|
||||||
|
GeomTraits m_gt;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \brief creates a new instance of this gradient class.
|
||||||
|
*
|
||||||
|
* \tparam ValueFunction must be a model of `IsosurfacingValueField_3`.
|
||||||
|
*
|
||||||
|
* \param function the function giving the scalar value at each point
|
||||||
|
* \param delta the distance between samples for calculating the finite differences
|
||||||
|
* \param gt the geometric traits class
|
||||||
|
*/
|
||||||
|
template <typename ValueFunction>
|
||||||
|
Finite_difference_gradient_3(const ValueFunction& function,
|
||||||
|
const FT delta,
|
||||||
|
const Geom_traits& gt = Geom_traits())
|
||||||
|
: m_function{function},
|
||||||
|
m_delta{delta},
|
||||||
|
m_half_step_inv{FT{1} / (FT{2} * m_delta)},
|
||||||
|
m_gt{gt}
|
||||||
|
{ }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief returns the value the gradient at a point in 3D space.
|
||||||
|
*
|
||||||
|
* \param p the point at which the gradient is computed.
|
||||||
|
*/
|
||||||
|
Vector_3 operator()(const Point_3& p) const
|
||||||
|
{
|
||||||
|
typename Geom_traits::Compute_x_3 x_coord = m_gt.compute_x_3_object();
|
||||||
|
typename Geom_traits::Compute_y_3 y_coord = m_gt.compute_y_3_object();
|
||||||
|
typename Geom_traits::Compute_z_3 z_coord = m_gt.compute_z_3_object();
|
||||||
|
typename Geom_traits::Construct_point_3 point = m_gt.construct_point_3_object();
|
||||||
|
typename Geom_traits::Construct_vector_3 vector = m_gt.construct_vector_3_object();
|
||||||
|
|
||||||
|
// compute the gradient by sampling the function with finite differences
|
||||||
|
// at six points with distance delta around the query point
|
||||||
|
const FT x = x_coord(p), y = y_coord(p), z = z_coord(p);
|
||||||
|
|
||||||
|
const Point_3 p0 = point(x + m_delta, y, z);
|
||||||
|
const Point_3 p1 = point(x - m_delta, y, z);
|
||||||
|
const Point_3 p2 = point(x, y + m_delta, z);
|
||||||
|
const Point_3 p3 = point(x, y - m_delta, z);
|
||||||
|
const Point_3 p4 = point(x, y, z + m_delta);
|
||||||
|
const Point_3 p5 = point(x, y, z - m_delta);
|
||||||
|
|
||||||
|
const FT gx = (m_function(p0) - m_function(p1)) * m_half_step_inv;
|
||||||
|
const FT gy = (m_function(p2) - m_function(p3)) * m_half_step_inv;
|
||||||
|
const FT gz = (m_function(p4) - m_function(p5)) * m_half_step_inv;
|
||||||
|
|
||||||
|
const FT n = CGAL::approximate_sqrt(CGAL::square(gx) + CGAL::square(gy) + CGAL::square(gz));
|
||||||
|
|
||||||
|
if(is_zero(n))
|
||||||
|
{
|
||||||
|
CGAL_warning(false && "interpolated gradient is the null vector!");
|
||||||
|
return vector(0,0,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return vector(gx / n, gy / n, gz / n);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Isosurfacing
|
||||||
|
} // namespace CGAL
|
||||||
|
|
||||||
|
#endif // CGAL_ISOSURFACING_3_FINITE_DIFFERENCE_GRADIENT_3_H
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
// Copyright (c) 2022-2024 INRIA Sophia-Antipolis (France), GeometryFactory (France).
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of CGAL (www.cgal.org).
|
||||||
|
//
|
||||||
|
// $URL$
|
||||||
|
// $Id$
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
|
||||||
|
//
|
||||||
|
// Author(s) : Julian Stahl
|
||||||
|
// Mael Rouxel-Labbé
|
||||||
|
|
||||||
|
#ifndef CGAL_ISOSURFACING_3_GRADIENT_FUNCTION_3_H
|
||||||
|
#define CGAL_ISOSURFACING_3_GRADIENT_FUNCTION_3_H
|
||||||
|
|
||||||
|
#include <CGAL/license/Isosurfacing_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/Isosurfacing_3/internal/partition_traits.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace CGAL {
|
||||||
|
namespace Isosurfacing {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup IS_Fields_grp
|
||||||
|
*
|
||||||
|
* \cgalModels{IsosurfacingGradientField_3}
|
||||||
|
*
|
||||||
|
* \brief The class `Gradient_function_3` represents a field of vectors computed
|
||||||
|
* using a user-provided unary function.
|
||||||
|
*
|
||||||
|
* \tparam Partition must be a model of `IsosurfacingPartition_3`
|
||||||
|
*
|
||||||
|
* \sa `CGAL::Isosurfacing::Dual_contouring_domain_3`
|
||||||
|
*/
|
||||||
|
template <typename Partition>
|
||||||
|
class Gradient_function_3
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Geom_traits = typename Partition::Geom_traits;
|
||||||
|
using Point_3 = typename Geom_traits::Point_3;
|
||||||
|
using Vector_3 = typename Geom_traits::Vector_3;
|
||||||
|
|
||||||
|
using PT = partition_traits<Partition>;
|
||||||
|
using vertex_descriptor = typename PT::vertex_descriptor;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::function<Vector_3(const Point_3&)> m_fn;
|
||||||
|
const Partition& m_partition;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \brief constructs a field of gradients using a gradient function and a partition.
|
||||||
|
*
|
||||||
|
* \tparam Function must provide the following function signature:
|
||||||
|
* `Vector_3 operator()(const %Point_3&) const`
|
||||||
|
*
|
||||||
|
* \param fn the function providing gradients
|
||||||
|
* \param partition the space partitioning data structure
|
||||||
|
*/
|
||||||
|
template <typename Function>
|
||||||
|
Gradient_function_3(const Function& fn,
|
||||||
|
const Partition& partition)
|
||||||
|
: m_fn{fn},
|
||||||
|
m_partition{partition}
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \brief returns the value of the function at the point `p`.
|
||||||
|
*/
|
||||||
|
Vector_3 operator()(const Point_3& p) const
|
||||||
|
{
|
||||||
|
return m_fn(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief returns the value of the function at the vertex `v`.
|
||||||
|
*/
|
||||||
|
const Vector_3& operator()(const vertex_descriptor& v) const
|
||||||
|
{
|
||||||
|
return this->operator()(PT::point(v, m_partition));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Isosurfacing
|
||||||
|
} // namespace CGAL
|
||||||
|
|
||||||
|
#endif // CGAL_ISOSURFACING_3_GRADIENT_FUNCTION_3_H
|
||||||
|
|
@ -0,0 +1,160 @@
|
||||||
|
// Copyright (c) 2022-2024 INRIA Sophia-Antipolis (France), GeometryFactory (France).
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of CGAL (www.cgal.org).
|
||||||
|
//
|
||||||
|
// $URL$
|
||||||
|
// $Id$
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
|
||||||
|
//
|
||||||
|
// Author(s) : Julian Stahl
|
||||||
|
// Mael Rouxel-Labbé
|
||||||
|
|
||||||
|
#ifndef CGAL_ISOSURFACING_3_IO_IMAGE_3_H
|
||||||
|
#define CGAL_ISOSURFACING_3_IO_IMAGE_3_H
|
||||||
|
|
||||||
|
#include <CGAL/license/Isosurfacing_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/Isosurfacing_3/Cartesian_grid_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Interpolated_discrete_values_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/Image_3.h>
|
||||||
|
|
||||||
|
namespace CGAL {
|
||||||
|
namespace Isosurfacing {
|
||||||
|
namespace IO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup IS_IO_functions_grp
|
||||||
|
*
|
||||||
|
* \brief extracts geometry and values from a `CGAL::Image_3`.
|
||||||
|
*
|
||||||
|
* The dimensions and geometric span are read from the image. The values stored
|
||||||
|
* in the image must be of type `Geom_traits::FT` or implicitly convertible to it.
|
||||||
|
*
|
||||||
|
* \tparam Grid must be `CGAL::Isosurfacing::Cartesian_grid_3<GeomTraits>` whose `GeomTraits` is a model of `IsosurfacingTraits_3`
|
||||||
|
* \tparam Values must be `CGAL::Isosurfacing::Interpolated_discrete_values_3<Grid>`
|
||||||
|
*
|
||||||
|
* \param image the image providing the data
|
||||||
|
* \param grid the output grid
|
||||||
|
* \param values the output values
|
||||||
|
*/
|
||||||
|
// We need to have the API pass us an existing grid / values pair because the values
|
||||||
|
// usually keep a reference to the grid.
|
||||||
|
template <typename Grid, typename Values>
|
||||||
|
bool convert_image_to_grid(const CGAL::Image_3& image,
|
||||||
|
Grid& grid,
|
||||||
|
Values& values)
|
||||||
|
{
|
||||||
|
using Geom_traits = typename Grid::Geom_traits;
|
||||||
|
using FT = typename Geom_traits::FT;
|
||||||
|
using Iso_cuboid_3 = typename Geom_traits::Iso_cuboid_3;
|
||||||
|
|
||||||
|
typename Geom_traits::Construct_point_3 point = grid.geom_traits().construct_point_3_object();
|
||||||
|
typename Geom_traits::Construct_iso_cuboid_3 iso_cuboid = grid.geom_traits().construct_iso_cuboid_3_object();
|
||||||
|
|
||||||
|
// compute span
|
||||||
|
const FT max_x = image.tx() + (image.xdim() - 1) * image.vx();
|
||||||
|
const FT max_y = image.ty() + (image.ydim() - 1) * image.vy();
|
||||||
|
const FT max_z = image.tz() + (image.zdim() - 1) * image.vz();
|
||||||
|
Iso_cuboid_3 span = iso_cuboid(point(image.tx(), image.ty(), image.tz()),
|
||||||
|
point(max_x, max_y, max_z));
|
||||||
|
|
||||||
|
// get spacing
|
||||||
|
// std::array<FT, 3> spacing = make_array(image.vx(), image.vy(), image.vz());
|
||||||
|
|
||||||
|
grid = Grid { span, CGAL::make_array<std::size_t>(image.xdim(), image.ydim(), image.zdim()) };
|
||||||
|
|
||||||
|
// copy values
|
||||||
|
for(std::size_t x=0; x<image.xdim(); ++x)
|
||||||
|
for(std::size_t y=0; y<image.ydim(); ++y)
|
||||||
|
for(std::size_t z=0; z<image.zdim(); ++z)
|
||||||
|
values(x, y, z) = image.value(x, y, z);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup IS_IO_functions_grp
|
||||||
|
*
|
||||||
|
* \brief create a `CGAL::Image_3` from a grid and a field of values.
|
||||||
|
*
|
||||||
|
* \tparam Grid must be `CGAL::Isosurfacing::Cartesian_grid_3<GeomTraits>` with `GeomTraits`
|
||||||
|
* a model of `IsosurfacingTraits_3`
|
||||||
|
* \tparam Values must be `CGAL::Isosurfacing::Interpolated_discrete_values_3<Grid>`
|
||||||
|
*
|
||||||
|
* \param grid the space partitioning data structure
|
||||||
|
* \param values the field of values
|
||||||
|
*/
|
||||||
|
template <typename Grid, typename Values>
|
||||||
|
CGAL::Image_3 convert_grid_to_image(const Grid& grid,
|
||||||
|
const Values& values)
|
||||||
|
{
|
||||||
|
using Geom_traits = typename Grid::Geom_traits;
|
||||||
|
|
||||||
|
using FT = typename Geom_traits::FT;
|
||||||
|
using Point_3 = typename Geom_traits::Point_3;
|
||||||
|
|
||||||
|
const Geom_traits& gt = grid.geom_traits();
|
||||||
|
typename Geom_traits::Compute_x_3 x_coord = gt.compute_x_3_object();
|
||||||
|
typename Geom_traits::Compute_y_3 y_coord = gt.compute_y_3_object();
|
||||||
|
typename Geom_traits::Compute_z_3 z_coord = gt.compute_z_3_object();
|
||||||
|
typename Geom_traits::Construct_vertex_3 vertex = gt.construct_vertex_3_object();
|
||||||
|
|
||||||
|
// select number type
|
||||||
|
WORD_KIND wordkind;
|
||||||
|
if constexpr(std::is_floating_point_v<FT>)
|
||||||
|
wordkind = WK_FLOAT;
|
||||||
|
else
|
||||||
|
wordkind = WK_FIXED;
|
||||||
|
|
||||||
|
// select signed or unsigned
|
||||||
|
SIGN sign;
|
||||||
|
if constexpr(std::is_signed_v<FT>)
|
||||||
|
sign = SGN_SIGNED;
|
||||||
|
else
|
||||||
|
sign = SGN_UNSIGNED;
|
||||||
|
|
||||||
|
// get spacing
|
||||||
|
const double vx = CGAL::to_double(grid.spacing()[0]);
|
||||||
|
const double vy = CGAL::to_double(grid.spacing()[1]);
|
||||||
|
const double vz = CGAL::to_double(grid.spacing()[2]);
|
||||||
|
|
||||||
|
// create image
|
||||||
|
_image* im = _createImage(grid.xdim(), grid.ydim(), grid.zdim(),
|
||||||
|
1, // vectorial dimension
|
||||||
|
vx, vy, vz, // voxel size
|
||||||
|
sizeof(FT), // image word size in bytes
|
||||||
|
wordkind, // image word kind WK_FIXED, WK_FLOAT, WK_UNKNOWN
|
||||||
|
sign); // image word sign
|
||||||
|
|
||||||
|
// error handling
|
||||||
|
if(im == nullptr || im->data == nullptr)
|
||||||
|
throw std::bad_alloc();
|
||||||
|
|
||||||
|
// set min coordinates
|
||||||
|
const Point_3& min_p = vertex(grid.span(), 0);
|
||||||
|
im->tx = float(CGAL::to_double(x_coord(min_p)));
|
||||||
|
im->ty = float(CGAL::to_double(y_coord(min_p)));
|
||||||
|
im->tz = float(CGAL::to_double(z_coord(min_p)));
|
||||||
|
|
||||||
|
// copy data
|
||||||
|
FT* data = static_cast<FT*>(im->data); // @fixme what compatibility with non trivial FTs?
|
||||||
|
for(std::size_t x=0; x<grid.xdim(); ++x) {
|
||||||
|
for(std::size_t y=0; y<grid.ydim(); ++y) {
|
||||||
|
for(std::size_t z=0; z<grid.zdim(); ++z)
|
||||||
|
{
|
||||||
|
const std::size_t lid = grid.linear_index(x, y, z);
|
||||||
|
data[lid] = values(grid.point(lid));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Image_3 { im, Image_3::OWN_THE_DATA };
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace IO
|
||||||
|
} // namespace Isosurfacing
|
||||||
|
} // namespace CGAL
|
||||||
|
|
||||||
|
#endif // CGAL_ISOSURFACING_3_IO_IMAGE_3_H
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
// Copyright (c) 2022-2024 INRIA Sophia-Antipolis (France), GeometryFactory (France).
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of CGAL (www.cgal.org).
|
||||||
|
//
|
||||||
|
// $URL$
|
||||||
|
// $Id$
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
|
||||||
|
//
|
||||||
|
// Author(s) : Mael Rouxel-Labbé
|
||||||
|
|
||||||
|
#ifndef CGAL_ISOSURFACING_3_IMAGE_3_H
|
||||||
|
#define CGAL_ISOSURFACING_3_IMAGE_3_H
|
||||||
|
|
||||||
|
#include <CGAL/license/Isosurfacing_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/Isosurfacing_3/Cartesian_grid_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/boost/graph/named_params_helper.h>
|
||||||
|
#include <CGAL/IO/io.h>
|
||||||
|
#include <CGAL/Named_function_parameters.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
namespace CGAL {
|
||||||
|
namespace Isosurfacing {
|
||||||
|
|
||||||
|
template <typename GeomTraits, typename MemoryPolicy>
|
||||||
|
class Cartesian_grid_3;
|
||||||
|
|
||||||
|
namespace IO {
|
||||||
|
|
||||||
|
template <typename GeomTraits, typename MemoryPolicy,
|
||||||
|
typename NamedParameters = parameters::Default_named_parameters>
|
||||||
|
bool write_OBJ(std::ostream& out,
|
||||||
|
const Cartesian_grid_3<GeomTraits, MemoryPolicy>& grid,
|
||||||
|
const NamedParameters& np = parameters::default_values())
|
||||||
|
{
|
||||||
|
using Point_3 = typename GeomTraits::Point_3;
|
||||||
|
|
||||||
|
typename GeomTraits::Compute_x_3 x_coord = grid.geom_traits().compute_x_3_object();
|
||||||
|
typename GeomTraits::Compute_y_3 y_coord = grid.geom_traits().compute_y_3_object();
|
||||||
|
typename GeomTraits::Compute_z_3 z_coord = grid.geom_traits().compute_z_3_object();
|
||||||
|
typename GeomTraits::Construct_vertex_3 vertex = grid.geom_traits().construct_vertex_3_object();
|
||||||
|
|
||||||
|
::CGAL::IO::set_ascii_mode(out); // obj is ASCII only
|
||||||
|
|
||||||
|
set_stream_precision_from_NP(out, np);
|
||||||
|
|
||||||
|
if(out.fail())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// write vertices
|
||||||
|
for(std::size_t x=0; x<grid.xdim(); ++x) {
|
||||||
|
for(std::size_t y=0; y<grid.ydim(); ++y) {
|
||||||
|
for(std::size_t z=0; z<grid.zdim(); ++z)
|
||||||
|
{
|
||||||
|
const Point_3& p = vertex(grid.span(), 0);
|
||||||
|
const double x_coord_d = CGAL::to_double(x_coord(p) + x * grid.spacing()[0]);
|
||||||
|
const double y_coord_d = CGAL::to_double(y_coord(p) + y * grid.spacing()[1]);
|
||||||
|
const double z_coord_d = CGAL::to_double(z_coord(p) + z * grid.spacing()[2]);
|
||||||
|
out << "v " << x_coord_d << " " << y_coord_d << " " << z_coord_d << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write faces
|
||||||
|
for(std::size_t x=0; x<grid.xdim()-1; ++x) {
|
||||||
|
for(std::size_t y=0; y<grid.ydim()-1; ++y) {
|
||||||
|
for(std::size_t z=0; z<grid.zdim()-1; ++z)
|
||||||
|
{
|
||||||
|
const std::size_t v0 = (z * grid.ydim() + y) * grid.xdim() + x;
|
||||||
|
const std::size_t v1 = (z * grid.ydim() + y + 1) * grid.xdim() + x;
|
||||||
|
const std::size_t v2 = (z * grid.ydim() + y + 1) * grid.xdim() + x + 1;
|
||||||
|
const std::size_t v3 = (z * grid.ydim() + y) * grid.xdim() + x + 1;
|
||||||
|
out << "f " << v0+1 << " " << v1+1 << " " << v2+1 << " " << v3+1 << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out.good();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename GeomTraits, typename MemoryPolicy,
|
||||||
|
typename NamedParameters = parameters::Default_named_parameters>
|
||||||
|
bool write_OBJ(const std::string& fname,
|
||||||
|
const Cartesian_grid_3<GeomTraits, MemoryPolicy>& grid,
|
||||||
|
const NamedParameters& np = parameters::default_values())
|
||||||
|
{
|
||||||
|
std::ofstream os(fname);
|
||||||
|
CGAL::IO::set_mode(os, CGAL::IO::ASCII);
|
||||||
|
|
||||||
|
return write_OBJ(os, grid, np);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace IO
|
||||||
|
} // namespace Isosurfacing
|
||||||
|
} // namespace CGAL
|
||||||
|
|
||||||
|
#endif // CGAL_ISOSURFACING_3_IMAGE_3_H
|
||||||
|
|
@ -0,0 +1,127 @@
|
||||||
|
// Copyright (c) 2022-2024 INRIA Sophia-Antipolis (France), GeometryFactory (France).
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of CGAL (www.cgal.org).
|
||||||
|
//
|
||||||
|
// $URL$
|
||||||
|
// $Id$
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
|
||||||
|
//
|
||||||
|
// Author(s) : Julian Stahl
|
||||||
|
// Mael Rouxel-Labbé
|
||||||
|
|
||||||
|
#ifndef CGAL_ISOSURFACING_3_INTERPOLATED_DISCRETE_GRADIENTS_3_H
|
||||||
|
#define CGAL_ISOSURFACING_3_INTERPOLATED_DISCRETE_GRADIENTS_3_H
|
||||||
|
|
||||||
|
#include <CGAL/license/Isosurfacing_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/Isosurfacing_3/internal/partition_traits.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/interpolation_schemes_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/Finite_difference_gradient_3.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace CGAL {
|
||||||
|
namespace Isosurfacing {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup IS_Fields_grp
|
||||||
|
*
|
||||||
|
* \cgalModels{IsosurfacingValueField_3}
|
||||||
|
*
|
||||||
|
* \brief Class template for a gradient field that is computed using discrete values and interpolation.
|
||||||
|
*
|
||||||
|
* \tparam Grid must be `CGAL::Isosurfacing::Cartesian_grid_3<GeomTraits>`, with `GeomTraits` a model of `IsosurfacingTraits_3`
|
||||||
|
* \tparam InterpolationScheme must be a model of `IsosurfacingInterpolationScheme_3`
|
||||||
|
*/
|
||||||
|
template <typename Grid,
|
||||||
|
typename InterpolationScheme = Trilinear_interpolation<Grid> >
|
||||||
|
class Interpolated_discrete_gradients_3
|
||||||
|
{
|
||||||
|
using Geom_traits = typename Grid::Geom_traits;
|
||||||
|
using FT = typename Geom_traits::FT;
|
||||||
|
using Point_3 = typename Geom_traits::Point_3;
|
||||||
|
using Vector_3 = typename Geom_traits::Vector_3;
|
||||||
|
|
||||||
|
using vertex_descriptor = typename partition_traits<Grid>::vertex_descriptor;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Grid& m_grid;
|
||||||
|
const InterpolationScheme m_interpolation;
|
||||||
|
|
||||||
|
std::vector<Vector_3> m_gradients;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Interpolated_discrete_gradients_3(const Grid& grid,
|
||||||
|
const InterpolationScheme& interpolation = InterpolationScheme())
|
||||||
|
: m_grid{grid},
|
||||||
|
m_interpolation{interpolation}
|
||||||
|
{
|
||||||
|
// pre-allocate memory
|
||||||
|
const std::size_t nv = grid.xdim() * grid.ydim() * grid.zdim();
|
||||||
|
m_gradients.resize(nv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// computes (using finite difference) and stores gradients at all vertices of the grid.
|
||||||
|
/// \tparam ValueField must be a model of `IsosurfacingValueField_3`
|
||||||
|
/// \param values a field of values whose gradient are being computed
|
||||||
|
template <typename ValueField>
|
||||||
|
void compute_discrete_gradients(const ValueField& values)
|
||||||
|
{
|
||||||
|
const FT step = CGAL::approximate_sqrt(m_grid.spacing().squared_length()) * 0.01; // finite difference step
|
||||||
|
Finite_difference_gradient_3<Geom_traits> g(values, step);
|
||||||
|
|
||||||
|
for(std::size_t i=0; i<m_grid.xdim(); ++i)
|
||||||
|
for(std::size_t j=0; j<m_grid.ydim(); ++j)
|
||||||
|
for(std::size_t k=0; k<m_grid.zdim(); ++k)
|
||||||
|
m_gradients[m_grid.linear_index(i, j, k)] = g(m_grid.point(i,j,k));
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \brief returns the gradient stored at the grid vertex described by its three indices.
|
||||||
|
*
|
||||||
|
* \note This function can be used to set the gradient at a grid vertex.
|
||||||
|
*
|
||||||
|
* \param i the index in the `x` direction
|
||||||
|
* \param j the index in the `y` direction
|
||||||
|
* \param k the index in the `z` direction
|
||||||
|
*
|
||||||
|
* \pre `i < xdim()` and `j < ydim()` and `k < zdim()`
|
||||||
|
*/
|
||||||
|
Vector_3& operator()(const std::size_t i,
|
||||||
|
const std::size_t j,
|
||||||
|
const std::size_t k)
|
||||||
|
{
|
||||||
|
return m_gradients[m_grid.linear_index(i, j, k)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief returns the gradient stored at the grid vertex described by a set of indices.
|
||||||
|
*
|
||||||
|
* \param i the index in the `x` direction
|
||||||
|
* \param j the index in the `y` direction
|
||||||
|
* \param k the index in the `z` direction
|
||||||
|
*
|
||||||
|
* \pre `i < xdim()` and `j < ydim()` and `k < zdim()`
|
||||||
|
*/
|
||||||
|
const Vector_3& operator()(const std::size_t i,
|
||||||
|
const std::size_t j,
|
||||||
|
const std::size_t k) const
|
||||||
|
{
|
||||||
|
return m_gradients[m_grid.linear_index(i, j, k)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* returns the gradient at a given point `p`.
|
||||||
|
*/
|
||||||
|
Vector_3 operator()(const Point_3& p) const
|
||||||
|
{
|
||||||
|
return m_interpolation.interpolated_gradient(p, m_grid, m_gradients);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Isosurfacing
|
||||||
|
} // namespace CGAL
|
||||||
|
|
||||||
|
#endif // CGAL_ISOSURFACING_3_INTERPOLATED_DISCRETE_GRADIENTS_3_H
|
||||||
|
|
@ -0,0 +1,124 @@
|
||||||
|
// Copyright (c) 2022-2024 INRIA Sophia-Antipolis (France), GeometryFactory (France).
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of CGAL (www.cgal.org).
|
||||||
|
//
|
||||||
|
// $URL$
|
||||||
|
// $Id$
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
|
||||||
|
//
|
||||||
|
// Author(s) : Julian Stahl
|
||||||
|
// Mael Rouxel-Labbé
|
||||||
|
|
||||||
|
#ifndef CGAL_ISOSURFACING_3_INTERPOLATED_DISCRETE_VALUES_3_H
|
||||||
|
#define CGAL_ISOSURFACING_3_INTERPOLATED_DISCRETE_VALUES_3_H
|
||||||
|
|
||||||
|
#include <CGAL/license/Isosurfacing_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/Isosurfacing_3/internal/partition_traits.h>
|
||||||
|
|
||||||
|
#include <CGAL/Isosurfacing_3/interpolation_schemes_3.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace CGAL {
|
||||||
|
namespace Isosurfacing {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup IS_Fields_grp
|
||||||
|
*
|
||||||
|
* \cgalModels{IsosurfacingValueField_3}
|
||||||
|
*
|
||||||
|
* \brief Class template for a field of values that are calculated using discrete values and interpolation.
|
||||||
|
*
|
||||||
|
* \tparam Grid must be `CGAL::Isosurfacing::Cartesian_grid_3<GeomTraits>`, with `GeomTraits` a model of `IsosurfacingTraits_3`
|
||||||
|
* \tparam InterpolationScheme must be a model of `IsosurfacingInterpolationScheme_3`
|
||||||
|
*/
|
||||||
|
template <typename Grid,
|
||||||
|
typename InterpolationScheme = Trilinear_interpolation<Grid> >
|
||||||
|
class Interpolated_discrete_values_3
|
||||||
|
{
|
||||||
|
using Geom_traits = typename Grid::Geom_traits;
|
||||||
|
using FT = typename Geom_traits::FT;
|
||||||
|
using Point_3 = typename Geom_traits::Point_3;
|
||||||
|
|
||||||
|
using vertex_descriptor = typename partition_traits<Grid>::vertex_descriptor;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Grid& m_grid;
|
||||||
|
const InterpolationScheme m_interpolation;
|
||||||
|
|
||||||
|
std::vector<FT> m_values;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Interpolated_discrete_values_3(const Grid& grid,
|
||||||
|
const InterpolationScheme& interpolation = InterpolationScheme())
|
||||||
|
: m_grid{grid},
|
||||||
|
m_interpolation{interpolation}
|
||||||
|
{
|
||||||
|
// pre-allocate memory
|
||||||
|
const std::size_t nv = grid.xdim() * grid.ydim() * grid.zdim();
|
||||||
|
m_values.resize(nv);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \brief returns the scalar value stored at the grid vertex described by its three indices.
|
||||||
|
*
|
||||||
|
* \note This function can be used to set the value at a grid vertex.
|
||||||
|
*
|
||||||
|
* \param i the index in the `x` direction
|
||||||
|
* \param j the index in the `y` direction
|
||||||
|
* \param k the index in the `z` direction
|
||||||
|
*
|
||||||
|
* \pre `i < xdim()` and `j < ydim()` and `k < zdim()`
|
||||||
|
*/
|
||||||
|
FT& operator()(const std::size_t i,
|
||||||
|
const std::size_t j,
|
||||||
|
const std::size_t k)
|
||||||
|
{
|
||||||
|
const std::size_t id = m_grid.linear_index(i, j, k);
|
||||||
|
if(id >= m_values.size())
|
||||||
|
m_values.resize(id + 1);
|
||||||
|
return m_values[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief returns the scalar value stored at the grid vertex described by its three indices.
|
||||||
|
*
|
||||||
|
* \param i the index in the `x` direction
|
||||||
|
* \param j the index in the `y` direction
|
||||||
|
* \param k the index in the `z` direction
|
||||||
|
*
|
||||||
|
* \pre `i < xdim()` and `j < ydim()` and `k < zdim()`
|
||||||
|
*/
|
||||||
|
FT operator()(const std::size_t i,
|
||||||
|
const std::size_t j,
|
||||||
|
const std::size_t k) const
|
||||||
|
{
|
||||||
|
CGAL_precondition(i < m_grid.xdim() && j < m_grid.ydim() && k < m_grid.zdim());
|
||||||
|
return m_values[m_grid.linear_index(i, j, k)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* returns the interpolated value at vertex `v`.
|
||||||
|
*/
|
||||||
|
FT operator()(const vertex_descriptor& v) const
|
||||||
|
{
|
||||||
|
return this->operator()(v[0], v[1], v[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* returns the interpolated value at point `p`.
|
||||||
|
*/
|
||||||
|
FT operator()(const Point_3& p) const
|
||||||
|
{
|
||||||
|
return m_interpolation.interpolated_value(p, m_grid, m_values);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Isosurfacing
|
||||||
|
} // namespace CGAL
|
||||||
|
|
||||||
|
#endif // CGAL_ISOSURFACING_3_INTERPOLATED_DISCRETE_VALUES_3_H
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
// Copyright (c) 2022-2024 INRIA Sophia-Antipolis (France), GeometryFactory (France).
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of CGAL (www.cgal.org).
|
||||||
|
//
|
||||||
|
// $URL$
|
||||||
|
// $Id$
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
|
||||||
|
//
|
||||||
|
// Author(s) : Julian Stahl
|
||||||
|
// Mael Rouxel-Labbé
|
||||||
|
|
||||||
|
#ifndef CGAL_ISOSURFACING_3_MARCHING_CUBES_DOMAIN_3_H
|
||||||
|
#define CGAL_ISOSURFACING_3_MARCHING_CUBES_DOMAIN_3_H
|
||||||
|
|
||||||
|
#include <CGAL/license/Isosurfacing_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/Isosurfacing_3/internal/Isosurfacing_domain_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/edge_intersection_oracles_3.h>
|
||||||
|
|
||||||
|
namespace CGAL {
|
||||||
|
namespace Isosurfacing {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup IS_Domains_grp
|
||||||
|
*
|
||||||
|
* \cgalModels{IsosurfacingDomain_3}
|
||||||
|
*
|
||||||
|
* \brief A domain that can be used with the Marching Cubes algorithm.
|
||||||
|
*
|
||||||
|
* \details This class is essentially wrapper around the different bricks provided by its
|
||||||
|
* template parameters: `Partition` provides the spatial partitioning, `ValueField`
|
||||||
|
* the values that define the isosurface. The optional template parameter
|
||||||
|
* `EdgeIntersectionOracle` gives control over the method used to compute edge-isosurface intersection points.
|
||||||
|
*
|
||||||
|
* \tparam Partition must be a model of `IsosurfacingPartition_3`
|
||||||
|
* \tparam ValueField must be a model of `IsosurfacingValueField_3`
|
||||||
|
* \tparam EdgeIntersectionOracle must be a model of `IsosurfacingEdgeIntersectionOracle_3`
|
||||||
|
*
|
||||||
|
* \sa `CGAL::Isosurfacing::marching_cubes()`
|
||||||
|
* \sa `CGAL::Isosurfacing::Dual_contouring_domain_3`
|
||||||
|
*/
|
||||||
|
template <typename Partition,
|
||||||
|
typename ValueField,
|
||||||
|
typename EdgeIntersectionOracle = CGAL::Isosurfacing::Linear_interpolation_edge_intersection>
|
||||||
|
class Marching_cubes_domain_3
|
||||||
|
#ifndef DOXYGEN_RUNNING
|
||||||
|
: public internal::Isosurfacing_domain_3<Partition, ValueField, EdgeIntersectionOracle>
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
using Base = internal::Isosurfacing_domain_3<Partition, ValueField, EdgeIntersectionOracle>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \brief constructs a domain that can be used with the Marching Cubes algorithm.
|
||||||
|
*
|
||||||
|
* \param partition the space partitioning data structure
|
||||||
|
* \param values a continuous field of scalar values, defined over the geometric span of `partition`
|
||||||
|
* \param intersection_oracle the oracle for edge-isosurface intersection computation
|
||||||
|
*
|
||||||
|
* \warning the domain class keeps a reference to the `partition`, `values` and `gradients` objects.
|
||||||
|
* As such, users must ensure that the lifetimes of these objects exceed that of the domain object.
|
||||||
|
*/
|
||||||
|
Marching_cubes_domain_3(const Partition& partition,
|
||||||
|
const ValueField& values,
|
||||||
|
const EdgeIntersectionOracle& intersection_oracle = EdgeIntersectionOracle())
|
||||||
|
: Base(partition, values, intersection_oracle)
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup IS_Domains_grp
|
||||||
|
*
|
||||||
|
* \brief creates a new instance of a domain that can be used with the Marching Cubes algorithm.
|
||||||
|
*
|
||||||
|
* \tparam Partition must be a model of `IsosurfacingPartition_3`
|
||||||
|
* \tparam ValueField must be a model of `IsosurfacingValueField_3`
|
||||||
|
* \tparam EdgeIntersectionOracle must be a model of `IsosurfacingEdgeIntersectionOracle_3`
|
||||||
|
*
|
||||||
|
* \param partition the space partitioning data structure
|
||||||
|
* \param values a continuous field of scalar values, defined over the geometric span of `partition`
|
||||||
|
* \param intersection_oracle the oracle for edge-isosurface intersection computation
|
||||||
|
*
|
||||||
|
* \warning the domain class keeps a reference to the `partition`, `values` and `gradients` objects.
|
||||||
|
* As such, users must ensure that the lifetimes of these objects exceed that of the domain object.
|
||||||
|
*/
|
||||||
|
template <typename Partition,
|
||||||
|
typename ValueField,
|
||||||
|
typename EdgeIntersectionOracle = Linear_interpolation_edge_intersection>
|
||||||
|
Marching_cubes_domain_3<Partition, ValueField, EdgeIntersectionOracle>
|
||||||
|
create_marching_cubes_domain_3(const Partition& partition,
|
||||||
|
const ValueField& values,
|
||||||
|
const EdgeIntersectionOracle& intersection_oracle = EdgeIntersectionOracle())
|
||||||
|
{
|
||||||
|
return { partition, values, intersection_oracle };
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Isosurfacing
|
||||||
|
} // namespace CGAL
|
||||||
|
|
||||||
|
#endif // CGAL_ISOSURFACING_3_MARCHING_CUBES_DOMAIN_3_H
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright (c) 2022-2024 INRIA Sophia-Antipolis (France), GeometryFactory (France).
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of CGAL (www.cgal.org).
|
||||||
|
//
|
||||||
|
// $URL$
|
||||||
|
// $Id$
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
|
||||||
|
//
|
||||||
|
// Author(s) : Sven Oesau
|
||||||
|
|
||||||
|
#ifndef CGAL_ISOSURFACING_3_OCTREE_PARTITION_H
|
||||||
|
#define CGAL_ISOSURFACING_3_OCTREE_PARTITION_H
|
||||||
|
|
||||||
|
#include <CGAL/license/Isosurfacing_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/Octree.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/internal/Octree_domain_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/internal/partition_traits_Octree.h>
|
||||||
|
|
||||||
|
#endif // CGAL_ISOSURFACING_3_OCTREE_PARTITION_H
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
// Copyright (c) 2022-2024 INRIA Sophia-Antipolis (France), GeometryFactory (France).
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of CGAL (www.cgal.org).
|
||||||
|
//
|
||||||
|
// $URL$
|
||||||
|
// $Id$
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
|
||||||
|
//
|
||||||
|
// Author(s) : Julian Stahl
|
||||||
|
// Mael Rouxel-Labbé
|
||||||
|
|
||||||
|
#ifndef CGAL_ISOSURFACING_3_VALUE_FUNCTION_3_H
|
||||||
|
#define CGAL_ISOSURFACING_3_VALUE_FUNCTION_3_H
|
||||||
|
|
||||||
|
#include <CGAL/license/Isosurfacing_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/Isosurfacing_3/internal/partition_traits.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace CGAL {
|
||||||
|
namespace Isosurfacing {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup IS_Fields_grp
|
||||||
|
*
|
||||||
|
* \cgalModels{IsosurfacingValueField_3}
|
||||||
|
*
|
||||||
|
* \brief The class `Value_function_3` represents a field of scalars computed
|
||||||
|
* using a user-provided unary function.
|
||||||
|
*
|
||||||
|
* \tparam Partition must be a model of `IsosurfacingPartition_3`
|
||||||
|
*
|
||||||
|
* \sa `CGAL::Isosurfacing::Marching_cubes_domain_3`
|
||||||
|
* \sa `CGAL::Isosurfacing::Dual_contouring_domain_3`
|
||||||
|
*/
|
||||||
|
template <typename Partition>
|
||||||
|
class Value_function_3
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Geom_traits = typename Partition::Geom_traits;
|
||||||
|
using FT = typename Geom_traits::FT;
|
||||||
|
using Point_3 = typename Geom_traits::Point_3;
|
||||||
|
|
||||||
|
using PT = partition_traits<Partition>;
|
||||||
|
using vertex_descriptor = typename PT::vertex_descriptor;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::function<FT(const Point_3&)> m_fn;
|
||||||
|
const Partition& m_partition;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \brief constructs a field of values using a value function and a partition.
|
||||||
|
*
|
||||||
|
* \tparam Function must provide the following function signature:
|
||||||
|
* `FT operator()(const %Point_3&) const`
|
||||||
|
*
|
||||||
|
* \param fn the function providing values
|
||||||
|
* \param partition the space partitioning data structure
|
||||||
|
*/
|
||||||
|
template <typename Function>
|
||||||
|
Value_function_3(const Function& fn,
|
||||||
|
const Partition& partition)
|
||||||
|
: m_fn{fn},
|
||||||
|
m_partition{partition}
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \brief returns the value of the function at the point `p`.
|
||||||
|
*/
|
||||||
|
FT operator()(const Point_3& p) const
|
||||||
|
{
|
||||||
|
return m_fn(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief returns the value of the function at the vertex `v`.
|
||||||
|
*/
|
||||||
|
FT operator()(const vertex_descriptor& v) const
|
||||||
|
{
|
||||||
|
return this->operator()(PT::point(v, m_partition));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Isosurfacing
|
||||||
|
} // namespace CGAL
|
||||||
|
|
||||||
|
#endif // CGAL_ISOSURFACING_3_VALUE_FUNCTION_3_H
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
// Copyright (c) 2022-2024 INRIA Sophia-Antipolis (France), GeometryFactory (France).
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of CGAL (www.cgal.org).
|
||||||
|
//
|
||||||
|
// $URL$
|
||||||
|
// $Id$
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
|
||||||
|
//
|
||||||
|
// Author(s) : Julian Stahl
|
||||||
|
// Daniel Zint
|
||||||
|
// Mael Rouxel-Labbé
|
||||||
|
|
||||||
|
#ifndef CGAL_ISOSURFACING_3_DUAL_CONTOURING_3_H
|
||||||
|
#define CGAL_ISOSURFACING_3_DUAL_CONTOURING_3_H
|
||||||
|
|
||||||
|
#include <CGAL/license/Isosurfacing_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/Isosurfacing_3/internal/dual_contouring_functors.h>
|
||||||
|
|
||||||
|
#include <CGAL/tags.h>
|
||||||
|
|
||||||
|
namespace CGAL {
|
||||||
|
namespace Isosurfacing {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup IS_Methods_grp
|
||||||
|
*
|
||||||
|
* \brief creates a polygon soup that discretizes an isosurface using the %Dual Contouring algorithm.
|
||||||
|
*
|
||||||
|
* \details The point placement strategy within each cell of the space partition is based on
|
||||||
|
* Quadric Error Metrics ("QEM", or "QEF" in %Dual Contouring-related works).
|
||||||
|
*
|
||||||
|
* \tparam ConcurrencyTag enables sequential versus parallel algorithm.
|
||||||
|
* Possible values are `Sequential_tag`, `Parallel_if_available_tag`, or `Parallel_tag`.
|
||||||
|
* \tparam Domain must be a model of `IsosurfacingDomainWithGradient_3`.
|
||||||
|
* \tparam PointRange must be a model of the concept `AssociativeContainer`
|
||||||
|
* whose value type can be constructed from the point type of the domain.
|
||||||
|
* \tparam PolygonRange must be a model of the concepts `RandomAccessContainer` and `BackInsertionSequence`
|
||||||
|
* whose value type is itself a model of the concepts `RandomAccessContainer`
|
||||||
|
* and `BackInsertionSequence` whose value type is `std::size_t`.
|
||||||
|
* \tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters"
|
||||||
|
*
|
||||||
|
* \param domain the domain providing the spatial partition and the values and gradient data
|
||||||
|
* \param isovalue the value defining the isosurface
|
||||||
|
* \param points the points of the polygons in the output polygon soup
|
||||||
|
* \param polygons the faces of the output polygon soup. Each element in the vector describes a polygon
|
||||||
|
* (either quads or triangles, see the named parameter `do_not_triangulate_faces`)
|
||||||
|
* using the indices of the points in `points`.
|
||||||
|
* \param np optional \ref bgl_namedparameters "Named Parameters" described below
|
||||||
|
*
|
||||||
|
* \cgalNamedParamsBegin
|
||||||
|
* \cgalParamNBegin{constrain_to_cell}
|
||||||
|
* \cgalParamDescription{whether to constrain the vertex location to the geometrical space of its cell}
|
||||||
|
* \cgalParamType{Boolean}
|
||||||
|
* \cgalParamDefault{`false`}
|
||||||
|
* \cgalParamExtra{Constraining the vertex to its dual cell guarantees that the resulting
|
||||||
|
* surface is without self-intersections (non-manifoldness aside). Oppositely,
|
||||||
|
* an unconstrained positioning strategy might produce better looking surfaces
|
||||||
|
* near sharp features (ridges, corners), at the cost of possible self-intersections.}
|
||||||
|
* \cgalParamNEnd
|
||||||
|
*
|
||||||
|
* \cgalParamNBegin{do_not_triangulate_faces}
|
||||||
|
* \cgalParamDescription{If `true`, the output will contain quadrilaterals.
|
||||||
|
* If `false`, the output will contain triangles.}
|
||||||
|
* \cgalParamType{Boolean}
|
||||||
|
* \cgalParamDefault{`false` (faces are triangulated)}
|
||||||
|
* \cgalParamExtra{Triangulating faces is done by inserting the intersection between an edge and
|
||||||
|
* the isosurface, and linking it to the dual points of the cells incident to the edge.
|
||||||
|
* If `constrain_to_cell` is set to `false`, triangulation faces can result in additional
|
||||||
|
* self-intersections. An alternative that has worse approximation but is less likely
|
||||||
|
* to produce self-intersections is to use the function
|
||||||
|
* `CGAL::Polygon_mesh_processing::triangulate_faces()`.}
|
||||||
|
* \cgalParamNEnd
|
||||||
|
* \cgalNamedParamsEnd
|
||||||
|
*
|
||||||
|
* \sa `CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh()`
|
||||||
|
*/
|
||||||
|
template <typename ConcurrencyTag = CGAL::Sequential_tag,
|
||||||
|
typename Domain,
|
||||||
|
typename PointRange,
|
||||||
|
typename PolygonRange,
|
||||||
|
typename NamedParameters = parameters::Default_named_parameters>
|
||||||
|
void dual_contouring(const Domain& domain,
|
||||||
|
const typename Domain::Geom_traits::FT isovalue,
|
||||||
|
PointRange& points,
|
||||||
|
PolygonRange& polygons,
|
||||||
|
const NamedParameters& np = parameters::default_values())
|
||||||
|
{
|
||||||
|
internal::Dual_contourer<ConcurrencyTag, Domain, internal::DC_Strategy::QEM> contourer;
|
||||||
|
contourer(domain, isovalue, points, polygons, np);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Isosurfacing
|
||||||
|
} // namespace CGAL
|
||||||
|
|
||||||
|
#endif // CGAL_ISOSURFACING_3_DUAL_CONTOURING_3_H
|
||||||
|
|
@ -0,0 +1,223 @@
|
||||||
|
// Copyright (c) 2022-2024 INRIA Sophia-Antipolis (France), GeometryFactory (France).
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of CGAL (www.cgal.org).
|
||||||
|
//
|
||||||
|
// $URL$
|
||||||
|
// $Id$
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
|
||||||
|
//
|
||||||
|
// Author(s) : Julian Stahl
|
||||||
|
// Mael Rouxel-Labbé
|
||||||
|
|
||||||
|
#ifndef CGAL_ISOSURFACING_3_EDGE_INTERSECTION_ORACLES_3_H
|
||||||
|
#define CGAL_ISOSURFACING_3_EDGE_INTERSECTION_ORACLES_3_H
|
||||||
|
|
||||||
|
#include <CGAL/license/Isosurfacing_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/assertions.h>
|
||||||
|
|
||||||
|
namespace CGAL {
|
||||||
|
namespace Isosurfacing {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup IS_Domain_helpers_grp
|
||||||
|
*
|
||||||
|
* \cgalModels{IsosurfacingEdgeIntersectionOracle_3}
|
||||||
|
*
|
||||||
|
* \brief The class `Dichotomy_edge_intersection` uses a dichotomy to find the intersection point
|
||||||
|
* between an edge and the isosurface.
|
||||||
|
*
|
||||||
|
* This class is for example suitable to be used as the `EdgeIntersectionOracle` template
|
||||||
|
* parameter of isosurfacing domain classes when values are computed using an implicit function.
|
||||||
|
*
|
||||||
|
* \warning It is not optimal to use this class when values are interpolated from discrete values
|
||||||
|
* since the intersection can be computed analytically in this case.
|
||||||
|
*
|
||||||
|
* \sa `CGAL::Isosurfacing::Linear_interpolation_edge_intersection`
|
||||||
|
* \sa `CGAL::Isosurfacing::Marching_cubes_domain_3`
|
||||||
|
* \sa `CGAL::Isosurfacing::Dual_contouring_domain_3`
|
||||||
|
*/
|
||||||
|
struct Dichotomy_edge_intersection
|
||||||
|
{
|
||||||
|
unsigned int m_max_iterations;
|
||||||
|
double m_relative_eps;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*!
|
||||||
|
* Constructor, enabling setting up the two criteria which can stop the dichotomy: either a
|
||||||
|
* threshold on the value (i.e., the difference between the isovalue and the value at the current
|
||||||
|
* point is smaller than `relative_eps * isovalue`), or a maximum number of iterations.
|
||||||
|
*/
|
||||||
|
Dichotomy_edge_intersection(unsigned int max_iterations = 10,
|
||||||
|
double relative_eps = 1e-7)
|
||||||
|
: m_max_iterations(max_iterations),
|
||||||
|
m_relative_eps(relative_eps)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief computes the intersection point between an edge and the isosurface.
|
||||||
|
*
|
||||||
|
* The result (if it exists) is stored in `p`.
|
||||||
|
*
|
||||||
|
* \tparam Domain must be a model of `IsosurfacingDomain_3`
|
||||||
|
*
|
||||||
|
* \param p_0 the location of the first vertex of the edge
|
||||||
|
* \param p_1 the location of the second vertex of the edge
|
||||||
|
* \param val_0 the value at the first vertex of the edge
|
||||||
|
* \param val_1 the value at the second vertex of the edge
|
||||||
|
* \param domain the isosurfacing domain
|
||||||
|
* \param isovalue the isovalue defining the isosurface with which we seek an intersection
|
||||||
|
* \param p the intersection point, if it exists
|
||||||
|
*
|
||||||
|
* \return `true` if the intersection point exists, `false` otherwise
|
||||||
|
*/
|
||||||
|
template <typename Domain> // == Isosurfacing_domain_3 or similar
|
||||||
|
bool operator()(const typename Domain::Geom_traits::Point_3& p_0,
|
||||||
|
const typename Domain::Geom_traits::Point_3& p_1,
|
||||||
|
const typename Domain::Geom_traits::FT val_0,
|
||||||
|
const typename Domain::Geom_traits::FT val_1,
|
||||||
|
const Domain& domain,
|
||||||
|
const typename Domain::Geom_traits::FT isovalue,
|
||||||
|
typename Domain::Geom_traits::Point_3& p) const
|
||||||
|
{
|
||||||
|
using Geom_traits = typename Domain::Geom_traits;
|
||||||
|
using FT = typename Geom_traits::FT;
|
||||||
|
using Point_3 = typename Geom_traits::Point_3;
|
||||||
|
|
||||||
|
typename Geom_traits::Compute_x_3 x_coord = domain.geom_traits().compute_x_3_object();
|
||||||
|
typename Geom_traits::Compute_y_3 y_coord = domain.geom_traits().compute_y_3_object();
|
||||||
|
typename Geom_traits::Compute_z_3 z_coord = domain.geom_traits().compute_z_3_object();
|
||||||
|
typename Geom_traits::Construct_point_3 point = domain.geom_traits().construct_point_3_object();
|
||||||
|
|
||||||
|
const bool sl = (val_0 <= isovalue);
|
||||||
|
const bool sr = (val_1 <= isovalue);
|
||||||
|
|
||||||
|
if(sl == sr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Point_3 pl = p_0;
|
||||||
|
Point_3 pr = p_1;
|
||||||
|
|
||||||
|
unsigned int dichotomy_iterations = m_max_iterations, iter = 0;
|
||||||
|
const FT eps = (std::max)(FT(m_relative_eps), std::abs(isovalue) * FT(m_relative_eps));
|
||||||
|
do
|
||||||
|
{
|
||||||
|
p = point((x_coord(pl) + x_coord(pr)) / FT(2),
|
||||||
|
(y_coord(pl) + y_coord(pr)) / FT(2),
|
||||||
|
(z_coord(pl) + z_coord(pr)) / FT(2));
|
||||||
|
|
||||||
|
const FT val_p = domain.value(p);
|
||||||
|
const bool sp = (val_p <= isovalue);
|
||||||
|
|
||||||
|
if(sl == sp)
|
||||||
|
pl = p;
|
||||||
|
else if(sp == sr)
|
||||||
|
pr = p;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
|
||||||
|
if(std::abs(val_p - isovalue) < eps)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
while(++iter < dichotomy_iterations);
|
||||||
|
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup IS_Domain_helpers_grp
|
||||||
|
*
|
||||||
|
* \cgalModels{IsosurfacingEdgeIntersectionOracle_3}
|
||||||
|
*
|
||||||
|
* \brief The class `Linear_interpolation_edge_intersection` uses linear interpolation
|
||||||
|
* to find the intersection point between an edge and the isosurface.
|
||||||
|
*
|
||||||
|
* This class is for example suitable when interpolated discrete values are being used.
|
||||||
|
*
|
||||||
|
* \sa `CGAL::Isosurfacing::Dichotomy_edge_intersection`
|
||||||
|
* \sa `CGAL::Isosurfacing::Marching_cubes_domain_3`
|
||||||
|
* \sa `CGAL::Isosurfacing::Dual_contouring_domain_3`
|
||||||
|
* \sa `CGAL::Isosurfacing::Interpolated_discrete_values_3`
|
||||||
|
*/
|
||||||
|
struct Linear_interpolation_edge_intersection
|
||||||
|
{
|
||||||
|
/*!
|
||||||
|
* \brief computes the intersection point between an edge and the isosurface.
|
||||||
|
*
|
||||||
|
* \tparam Domain must be a model of `IsosurfacingDomain_3`
|
||||||
|
*
|
||||||
|
* \param p_0 the location of the first vertex of the edge
|
||||||
|
* \param p_1 the location of the second vertex of the edge
|
||||||
|
* \param val_0 the value at the first vertex of the edge
|
||||||
|
* \param val_1 the value at the second vertex of the edge
|
||||||
|
* \param domain the isosurfacing domain
|
||||||
|
* \param isovalue the isovalue defining the isosurface with which we seek an intersection
|
||||||
|
* \param p the intersection point, if it exists
|
||||||
|
*
|
||||||
|
* \return `true` if the intersection point exists, `false` otherwise
|
||||||
|
*/
|
||||||
|
template <typename Domain>
|
||||||
|
bool operator()(const typename Domain::Geom_traits::Point_3& p_0,
|
||||||
|
const typename Domain::Geom_traits::Point_3& p_1,
|
||||||
|
const typename Domain::Geom_traits::FT val_0,
|
||||||
|
const typename Domain::Geom_traits::FT val_1,
|
||||||
|
const Domain& domain,
|
||||||
|
const typename Domain::Geom_traits::FT isovalue,
|
||||||
|
typename Domain::Geom_traits::Point_3& p) const
|
||||||
|
{
|
||||||
|
using Geom_traits = typename Domain::Geom_traits;
|
||||||
|
using FT = typename Geom_traits::FT;
|
||||||
|
|
||||||
|
typename Geom_traits::Compute_x_3 x_coord = domain.geom_traits().compute_x_3_object();
|
||||||
|
typename Geom_traits::Compute_y_3 y_coord = domain.geom_traits().compute_y_3_object();
|
||||||
|
typename Geom_traits::Compute_z_3 z_coord = domain.geom_traits().compute_z_3_object();
|
||||||
|
typename Geom_traits::Construct_point_3 point = domain.geom_traits().construct_point_3_object();
|
||||||
|
|
||||||
|
if((val_0 <= isovalue) == (val_1 <= isovalue))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const FT den = val_0 - val_1;
|
||||||
|
const FT u = is_zero(den) ? 0.5 : (val_0 - isovalue) / den;
|
||||||
|
p = point((FT(1) - u) * x_coord(p_0) + u * x_coord(p_1),
|
||||||
|
(FT(1) - u) * y_coord(p_0) + u * y_coord(p_1),
|
||||||
|
(FT(1) - u) * z_coord(p_0) + u * z_coord(p_1));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef DOXYGEN_RUNNING
|
||||||
|
/*
|
||||||
|
* \ingroup IS_Domain_helpers_grp
|
||||||
|
*
|
||||||
|
* \cgalModels{IsosurfacingEdgeIntersectionOracle_3}
|
||||||
|
*
|
||||||
|
* \brief The class `Ray_marching_edge_intersection` uses ray marching to find the intersection point
|
||||||
|
* between an edge and the isosurface.
|
||||||
|
*
|
||||||
|
* This class is suitable when the values stem from a signed distance function.
|
||||||
|
*/
|
||||||
|
//
|
||||||
|
// @todo this is for the case where we know domain.value is an SDF
|
||||||
|
// then we could do better than a dichotomy
|
||||||
|
// see https://github.com/MaelRL/cgal/blob/AW3-Sharp_and_sparse-GF/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/offset_intersection.h
|
||||||
|
struct Ray_marching_edge_intersection
|
||||||
|
{
|
||||||
|
template <typename Domain> // == Isosurfacing_domain_3 or similar
|
||||||
|
bool operator()(const typename Domain::Geom_traits::Point_3& p_0,
|
||||||
|
const typename Domain::Geom_traits::Point_3& p_1,
|
||||||
|
const typename Domain::Geom_traits::FT val_0,
|
||||||
|
const typename Domain::Geom_traits::FT val_1,
|
||||||
|
const Domain& domain,
|
||||||
|
const typename Domain::Geom_traits::FT isovalue,
|
||||||
|
typename Domain::Geom_traits::Point_3& p) const;
|
||||||
|
};
|
||||||
|
#endif // DOXYGEN_RUNNING
|
||||||
|
|
||||||
|
} // namespace Isosurfacing
|
||||||
|
} // namespace CGAL
|
||||||
|
|
||||||
|
#endif // CGAL_ISOSURFACING_3_EDGE_INTERSECTION_ORACLES_3_H
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
// Copyright (c) 2022-2024 INRIA Sophia-Antipolis (France), GeometryFactory (France).
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of CGAL (www.cgal.org).
|
||||||
|
//
|
||||||
|
// $URL$
|
||||||
|
// $Id$
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
|
||||||
|
//
|
||||||
|
// Author(s) : Julian Stahl
|
||||||
|
|
||||||
|
#ifndef CGAL_ISOSURFACING_3_INTERNAL_DOMAIN_CELL_TYPE_H
|
||||||
|
#define CGAL_ISOSURFACING_3_INTERNAL_DOMAIN_CELL_TYPE_H
|
||||||
|
|
||||||
|
#include <CGAL/license/Isosurfacing_3.h>
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
namespace CGAL {
|
||||||
|
namespace Isosurfacing {
|
||||||
|
|
||||||
|
// Was supposed to check if an algorithm can handle a specific domain. Not used right now.
|
||||||
|
using Cell_type = std::size_t;
|
||||||
|
|
||||||
|
static constexpr Cell_type ANY_CELL = (std::numeric_limits<std::size_t>::max)();
|
||||||
|
|
||||||
|
static constexpr Cell_type POLYHEDRAL_CELL = (std::size_t(1) << 0);
|
||||||
|
static constexpr Cell_type TETRAHEDRAL_CELL = (std::size_t(1) << 1);
|
||||||
|
static constexpr Cell_type CUBICAL_CELL = (std::size_t(1) << 2);
|
||||||
|
|
||||||
|
} // namespace Isosurfacing
|
||||||
|
} // namespace CGAL
|
||||||
|
|
||||||
|
#endif // CGAL_ISOSURFACING_3_INTERNAL_DOMAIN_CELL_TYPE_H
|
||||||
|
|
@ -0,0 +1,175 @@
|
||||||
|
// Copyright (c) 2022-2024 INRIA Sophia-Antipolis (France), GeometryFactory (France).
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of CGAL (www.cgal.org).
|
||||||
|
//
|
||||||
|
// $URL$
|
||||||
|
// $Id$
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
|
||||||
|
//
|
||||||
|
// Author(s) : Julian Stahl
|
||||||
|
// Mael Rouxel-Labbé
|
||||||
|
|
||||||
|
#ifndef CGAL_ISOSURFACING_3_INTERNAL_ISOSURFACING_DOMAIN_3_H
|
||||||
|
#define CGAL_ISOSURFACING_3_INTERNAL_ISOSURFACING_DOMAIN_3_H
|
||||||
|
|
||||||
|
#include <CGAL/license/Isosurfacing_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/Isosurfacing_3/internal/Cell_type.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/internal/partition_traits.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/edge_intersection_oracles_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/tags.h>
|
||||||
|
|
||||||
|
namespace CGAL {
|
||||||
|
namespace Isosurfacing {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// This class is pretty much just the concatenation of the following classes:
|
||||||
|
// - Partition: Space partitioning data structure, e.g. Cartesian grid, octree, ...
|
||||||
|
// - Values: values over the 3D space
|
||||||
|
// - Gradients: gradients over the 3D space
|
||||||
|
// - Oracle: edge-isosurface intersection computation
|
||||||
|
template <typename Partition,
|
||||||
|
typename ValueField,
|
||||||
|
typename GradientField,
|
||||||
|
typename IntersectionOracle = CGAL::Isosurfacing::Dichotomy_edge_intersection>
|
||||||
|
class Isosurfacing_domain_3
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Edge_intersection_oracle = IntersectionOracle;
|
||||||
|
|
||||||
|
using Geom_traits = typename Partition::Geom_traits;
|
||||||
|
using FT = typename Geom_traits::FT;
|
||||||
|
using Point_3 = typename Geom_traits::Point_3;
|
||||||
|
using Vector_3 = typename Geom_traits::Vector_3;
|
||||||
|
|
||||||
|
using PT = CGAL::Isosurfacing::partition_traits<Partition>;
|
||||||
|
|
||||||
|
using vertex_descriptor = typename PT::vertex_descriptor;
|
||||||
|
using edge_descriptor = typename PT::edge_descriptor;
|
||||||
|
using cell_descriptor = typename PT::cell_descriptor;
|
||||||
|
|
||||||
|
using Edge_vertices = typename PT::Edge_vertices;
|
||||||
|
using Cells_incident_to_edge = typename PT::Cells_incident_to_edge;
|
||||||
|
using Cell_vertices = typename PT::Cell_vertices;
|
||||||
|
using Cell_edges = typename PT::Cell_edges;
|
||||||
|
|
||||||
|
static constexpr Cell_type CELL_TYPE = PT::CELL_TYPE;
|
||||||
|
static constexpr std::size_t VERTICES_PER_CELL = PT::VERTICES_PER_CELL;
|
||||||
|
static constexpr std::size_t EDGES_PER_CELL = PT::EDGES_PER_CELL;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Partition& m_partition;
|
||||||
|
const ValueField& m_values;
|
||||||
|
const GradientField& m_gradients;
|
||||||
|
const IntersectionOracle m_intersection_oracle;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Isosurfacing_domain_3(const Partition& partition,
|
||||||
|
const ValueField& values,
|
||||||
|
const GradientField& gradients,
|
||||||
|
const IntersectionOracle& intersection_oracle = IntersectionOracle())
|
||||||
|
: m_partition{partition},
|
||||||
|
m_values{values},
|
||||||
|
m_gradients{gradients},
|
||||||
|
m_intersection_oracle{intersection_oracle}
|
||||||
|
{ }
|
||||||
|
|
||||||
|
const Geom_traits& geom_traits() const
|
||||||
|
{
|
||||||
|
return m_partition.geom_traits();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Edge_intersection_oracle& intersection_oracle() const
|
||||||
|
{
|
||||||
|
return m_intersection_oracle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// The following functions are dispatching to the partition_traits' static functions.
|
||||||
|
|
||||||
|
// returns the location of vertex `v`
|
||||||
|
decltype(auto) /*Point_3*/ point(const vertex_descriptor& v) const
|
||||||
|
{
|
||||||
|
return PT::point(v, m_partition);
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the value of the function at vertex `v`
|
||||||
|
decltype(auto) /*FT*/ value(const vertex_descriptor& v) const
|
||||||
|
{
|
||||||
|
return m_values(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the value of the function at point `p`
|
||||||
|
decltype(auto) /*FT*/ value(const Point_3& p) const
|
||||||
|
{
|
||||||
|
return m_values(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the gradient at point `p`
|
||||||
|
decltype(auto) /*Vector_3*/ gradient(const Point_3& p) const
|
||||||
|
{
|
||||||
|
return m_gradients(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a container with the two vertices incident to the edge `e`
|
||||||
|
decltype(auto) /*Edge_vertices*/ incident_vertices(const edge_descriptor& e) const
|
||||||
|
{
|
||||||
|
return PT::incident_vertices(e, m_partition);
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a container with all cells incident to the edge `e`
|
||||||
|
decltype(auto) /*Cells_incident_to_edge*/ incident_cells(const edge_descriptor& e) const
|
||||||
|
{
|
||||||
|
return PT::incident_cells(e, m_partition);
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a container with all vertices of the cell `c`
|
||||||
|
decltype(auto) /*Cell_vertices*/ cell_vertices(const cell_descriptor& c) const
|
||||||
|
{
|
||||||
|
return PT::cell_vertices(c, m_partition);
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a container with all edges of the cell `c`
|
||||||
|
decltype(auto) /*Cell_edges*/ cell_edges(const cell_descriptor& c) const
|
||||||
|
{
|
||||||
|
return PT::cell_edges(c, m_partition);
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterates over all vertices `v`, calling `f(v)` on each of them
|
||||||
|
template <typename ConcurrencyTag = CGAL::Sequential_tag, typename Functor>
|
||||||
|
void for_each_vertex(Functor& f) const
|
||||||
|
{
|
||||||
|
PT::template for_each_vertex<ConcurrencyTag>(f, m_partition);
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterates over all edges `e`, calling `f(e)` on each of them
|
||||||
|
template <typename ConcurrencyTag = CGAL::Sequential_tag, typename Functor>
|
||||||
|
void for_each_edge(Functor& f) const
|
||||||
|
{
|
||||||
|
PT::template for_each_edge<ConcurrencyTag>(f, m_partition);
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterates over all cells `c`, calling `f(c)` on each of them
|
||||||
|
template <typename ConcurrencyTag = CGAL::Sequential_tag, typename Functor>
|
||||||
|
void for_each_cell(Functor& f) const
|
||||||
|
{
|
||||||
|
PT::template for_each_cell<ConcurrencyTag>(f, m_partition);
|
||||||
|
}
|
||||||
|
|
||||||
|
// finds the intersection of the isosurface with the edge `e` (if any)
|
||||||
|
bool construct_intersection(const Point_3& p_0, const Point_3& p_1,
|
||||||
|
const FT val_0, const FT val_1,
|
||||||
|
const FT isovalue,
|
||||||
|
Point_3& p) const
|
||||||
|
{
|
||||||
|
return m_intersection_oracle(p_0, p_1, val_0, val_1, *this, isovalue, p);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace Isosurfacing
|
||||||
|
} // namespace CGAL
|
||||||
|
|
||||||
|
#endif // CGAL_ISOSURFACING_3_INTERNAL_ISOSURFACING_DOMAIN_3_H
|
||||||
|
|
@ -0,0 +1,182 @@
|
||||||
|
// Copyright (c) 2022-2024 INRIA Sophia-Antipolis (France), GeometryFactory (France).
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of CGAL (www.cgal.org).
|
||||||
|
//
|
||||||
|
// $URL$
|
||||||
|
// $Id$
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
|
||||||
|
//
|
||||||
|
// Author(s) : Julian Stahl
|
||||||
|
// Mael Rouxel-Labbé
|
||||||
|
|
||||||
|
#ifndef CGAL_ISOSURFACING_3_INTERNAL_OCTREE_DOMAIN_3_H
|
||||||
|
#define CGAL_ISOSURFACING_3_INTERNAL_OCTREE_DOMAIN_3_H
|
||||||
|
|
||||||
|
#include <CGAL/license/Isosurfacing_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/internal/Isosurfacing_domain_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/Octree.h>
|
||||||
|
|
||||||
|
#include <CGAL/tags.h>
|
||||||
|
#include <CGAL/Kernel_traits.h>
|
||||||
|
|
||||||
|
namespace CGAL {
|
||||||
|
namespace Isosurfacing {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// Specialization of the Isosurfacing_domain_3 for Orthtree
|
||||||
|
template <typename ValueField,
|
||||||
|
typename GradientField,
|
||||||
|
typename IntersectionOracle>
|
||||||
|
class Isosurfacing_domain_3<CGAL::Octree<typename Kernel_traits<typename ValueField::Point_3>::Kernel, std::vector<typename ValueField::Point_3> >, ValueField, GradientField, IntersectionOracle>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Edge_intersection_oracle = IntersectionOracle;
|
||||||
|
|
||||||
|
using Partition = CGAL::Octree<typename Kernel_traits<typename ValueField::Point_3>::Kernel, std::vector<typename ValueField::Point_3> >;
|
||||||
|
|
||||||
|
using Geom_traits = typename Partition::Geom_traits;
|
||||||
|
using FT = typename Geom_traits::FT;
|
||||||
|
using Point_3 = typename Geom_traits::Point_3;
|
||||||
|
using Vector_3 = typename Geom_traits::Vector_3;
|
||||||
|
|
||||||
|
using PT = CGAL::Isosurfacing::partition_traits<Partition>;
|
||||||
|
|
||||||
|
using vertex_descriptor = typename PT::vertex_descriptor;
|
||||||
|
using edge_descriptor = typename PT::edge_descriptor;
|
||||||
|
using cell_descriptor = typename PT::cell_descriptor;
|
||||||
|
|
||||||
|
using Edge_vertices = typename PT::Edge_vertices;
|
||||||
|
using Cells_incident_to_edge = typename PT::Cells_incident_to_edge;
|
||||||
|
using Cell_vertices = typename PT::Cell_vertices;
|
||||||
|
using Cell_edges = typename PT::Cell_edges;
|
||||||
|
|
||||||
|
static constexpr Cell_type CELL_TYPE = PT::CELL_TYPE;
|
||||||
|
static constexpr std::size_t VERTICES_PER_CELL = PT::VERTICES_PER_CELL;
|
||||||
|
static constexpr std::size_t EDGES_PER_CELL = PT::EDGES_PER_CELL;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Partition& m_partition;
|
||||||
|
const ValueField& m_values;
|
||||||
|
const GradientField& m_gradients;
|
||||||
|
const IntersectionOracle m_intersection_oracle;
|
||||||
|
mutable std::vector<vertex_descriptor> m_leaf_vertices; // cache variable
|
||||||
|
mutable std::vector<edge_descriptor> m_leaf_edges; // cache variable
|
||||||
|
mutable std::vector<cell_descriptor> m_leaf_cells; // cache variable
|
||||||
|
|
||||||
|
public:
|
||||||
|
Isosurfacing_domain_3(const Partition& partition,
|
||||||
|
const ValueField& values,
|
||||||
|
const GradientField& gradients,
|
||||||
|
const IntersectionOracle& intersection_oracle = IntersectionOracle())
|
||||||
|
: m_partition{partition},
|
||||||
|
m_values{values},
|
||||||
|
m_gradients{gradients},
|
||||||
|
m_intersection_oracle{intersection_oracle}
|
||||||
|
{ }
|
||||||
|
|
||||||
|
const Geom_traits& geom_traits() const
|
||||||
|
{
|
||||||
|
return m_partition.geom_traits();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Edge_intersection_oracle& intersection_oracle() const
|
||||||
|
{
|
||||||
|
return m_intersection_oracle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// The following functions are dispatching to the partition_traits' static functions.
|
||||||
|
|
||||||
|
// returns the location of vertex `v`
|
||||||
|
decltype(auto) /*Point_3*/ point(const vertex_descriptor& v) const
|
||||||
|
{
|
||||||
|
return PT::point(v, m_partition);
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the value of the function at vertex `v`
|
||||||
|
decltype(auto) /*FT*/ value(const vertex_descriptor& v) const
|
||||||
|
{
|
||||||
|
return m_values(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the value of the function at point `p`
|
||||||
|
decltype(auto) /*FT*/ value(const Point_3& p) const
|
||||||
|
{
|
||||||
|
return m_values(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the gradient at point `p`
|
||||||
|
decltype(auto) /*Vector_3*/ gradient(const Point_3& p) const
|
||||||
|
{
|
||||||
|
return m_gradients(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a container with the two vertices incident to the edge `e`
|
||||||
|
decltype(auto) /*Edge_vertices*/ incident_vertices(const edge_descriptor& e) const
|
||||||
|
{
|
||||||
|
return PT::incident_vertices(e, m_partition);
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a container with all cells incident to the edge `e`
|
||||||
|
decltype(auto) /*Cells_incident_to_edge*/ incident_cells(const edge_descriptor& e) const
|
||||||
|
{
|
||||||
|
return PT::incident_cells(e, m_partition);
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a container with all vertices of the cell `c`
|
||||||
|
decltype(auto) /*Cell_vertices*/ cell_vertices(const cell_descriptor& c) const
|
||||||
|
{
|
||||||
|
return PT::cell_vertices(c, m_partition);
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a container with all edges of the cell `c`
|
||||||
|
decltype(auto) /*Cell_edges*/ cell_edges(const cell_descriptor& c) const
|
||||||
|
{
|
||||||
|
return PT::cell_edges(c, m_partition);
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterates over all vertices `v`, calling `f(v)` on each of them
|
||||||
|
template <typename ConcurrencyTag = CGAL::Sequential_tag, typename Functor>
|
||||||
|
void for_each_vertex(Functor& f) const
|
||||||
|
{
|
||||||
|
if (m_leaf_vertices.empty())
|
||||||
|
PT::get_leaves(m_partition, m_leaf_cells, m_leaf_edges, m_leaf_vertices);
|
||||||
|
|
||||||
|
PT::template for_each_vertex<ConcurrencyTag>(f, m_leaf_vertices, m_partition);
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterates over all edges `e`, calling `f(e)` on each of them
|
||||||
|
template <typename ConcurrencyTag = CGAL::Sequential_tag, typename Functor>
|
||||||
|
void for_each_edge(Functor& f) const
|
||||||
|
{
|
||||||
|
if (m_leaf_edges.empty())
|
||||||
|
PT::get_leaves(m_partition, m_leaf_cells, m_leaf_edges, m_leaf_vertices);
|
||||||
|
PT::template for_each_edge<ConcurrencyTag>(f, m_leaf_edges, m_partition);
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterates over all cells `c`, calling `f(c)` on each of them
|
||||||
|
template <typename ConcurrencyTag = CGAL::Sequential_tag, typename Functor>
|
||||||
|
void for_each_cell(Functor& f) const
|
||||||
|
{
|
||||||
|
if (m_leaf_cells.empty())
|
||||||
|
PT::get_leaves(m_partition, m_leaf_cells, m_leaf_edges, m_leaf_vertices);
|
||||||
|
PT::template for_each_cell<ConcurrencyTag>(f, m_leaf_cells, m_partition);
|
||||||
|
}
|
||||||
|
|
||||||
|
// finds the intersection of the isosurface with the edge `e` (if any)
|
||||||
|
bool construct_intersection(const Point_3& p_0, const Point_3& p_1,
|
||||||
|
const FT val_0, const FT val_1,
|
||||||
|
const FT isovalue,
|
||||||
|
Point_3& p) const
|
||||||
|
{
|
||||||
|
return m_intersection_oracle(p_0, p_1, val_0, val_1, *this, isovalue, p);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace Isosurfacing
|
||||||
|
} // namespace CGAL
|
||||||
|
|
||||||
|
#endif // CGAL_ISOSURFACING_3_INTERNAL_OCTREE_DOMAIN_3_H
|
||||||
|
|
@ -0,0 +1,671 @@
|
||||||
|
// Copyright (c) 2022-2024 INRIA Sophia-Antipolis (France), GeometryFactory (France).
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of CGAL (www.cgal.org).
|
||||||
|
//
|
||||||
|
// $URL$
|
||||||
|
// $Id$
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
|
||||||
|
//
|
||||||
|
// Author(s) : Daniel Zint
|
||||||
|
// Julian Stahl
|
||||||
|
// Mael Rouxel-Labbé
|
||||||
|
|
||||||
|
#ifndef CGAL_ISOSURFACING_3_INTERNAL_DUAL_CONTOURING_FUNCTORS_H
|
||||||
|
#define CGAL_ISOSURFACING_3_INTERNAL_DUAL_CONTOURING_FUNCTORS_H
|
||||||
|
|
||||||
|
#include <CGAL/license/Isosurfacing_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/boost/graph/named_params_helper.h>
|
||||||
|
#include <CGAL/Named_function_parameters.h>
|
||||||
|
#include <CGAL/Container_helper.h>
|
||||||
|
#include <CGAL/Origin.h>
|
||||||
|
|
||||||
|
#ifdef CGAL_EIGEN3_ENABLED
|
||||||
|
#include <CGAL/Eigen_matrix.h>
|
||||||
|
#include <CGAL/Eigen_vector.h>
|
||||||
|
|
||||||
|
#include <Eigen/SVD>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CGAL_LINKED_WITH_TBB
|
||||||
|
#if TBB_INTERFACE_VERSION < 12010 && !defined(TBB_PREVIEW_CONCURRENT_ORDERED_CONTAINERS)
|
||||||
|
#define CGAL_HAS_DEFINED_TBB_PREVIEW_CONCURRENT_ORDERED_CONTAINERS
|
||||||
|
#define TBB_PREVIEW_CONCURRENT_ORDERED_CONTAINERS 1
|
||||||
|
#endif
|
||||||
|
#include <tbb/concurrent_map.h>
|
||||||
|
#include <tbb/parallel_for_each.h>
|
||||||
|
#endif
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <mutex>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace CGAL {
|
||||||
|
namespace Isosurfacing {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template <typename Domain,
|
||||||
|
typename EdgeToPointIDMap,
|
||||||
|
typename PointRange,
|
||||||
|
typename GradientRange>
|
||||||
|
bool cell_position_QEM(const typename Domain::cell_descriptor& c,
|
||||||
|
const Domain& domain,
|
||||||
|
const bool constrain_to_cell,
|
||||||
|
const EdgeToPointIDMap& edge_to_point_id,
|
||||||
|
const PointRange& edge_points,
|
||||||
|
const GradientRange& edge_gradients,
|
||||||
|
typename Domain::Geom_traits::Point_3& p)
|
||||||
|
{
|
||||||
|
using Geom_traits = typename Domain::Geom_traits;
|
||||||
|
using FT = typename Geom_traits::FT;
|
||||||
|
using Point_3 = typename Geom_traits::Point_3;
|
||||||
|
using Vector_3 = typename Geom_traits::Vector_3;
|
||||||
|
|
||||||
|
using Eigen_vector_3 = Eigen_vector<FT, 3>;
|
||||||
|
using Eigen_matrix_3 = Eigen_matrix<FT, 3, 3>;
|
||||||
|
using Eigen_vector_x = Eigen_vector<FT>;
|
||||||
|
using Eigen_matrix_x = Eigen_matrix<FT>;
|
||||||
|
|
||||||
|
typename Geom_traits::Compute_x_3 x_coord = domain.geom_traits().compute_x_3_object();
|
||||||
|
typename Geom_traits::Compute_y_3 y_coord = domain.geom_traits().compute_y_3_object();
|
||||||
|
typename Geom_traits::Compute_z_3 z_coord = domain.geom_traits().compute_z_3_object();
|
||||||
|
typename Geom_traits::Construct_point_3 point = domain.geom_traits().construct_point_3_object();
|
||||||
|
|
||||||
|
// compute edge intersections
|
||||||
|
std::vector<Point_3> cell_edge_intersections;
|
||||||
|
std::vector<Vector_3> cell_edge_intersection_normals;
|
||||||
|
|
||||||
|
for(const auto& edge : domain.cell_edges(c))
|
||||||
|
{
|
||||||
|
const auto it = edge_to_point_id.find(edge);
|
||||||
|
if(it == edge_to_point_id.end())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
cell_edge_intersections.push_back(edge_points[it->second]);
|
||||||
|
cell_edge_intersection_normals.push_back(edge_gradients[it->second]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::size_t en = cell_edge_intersections.size();
|
||||||
|
if(en == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
#ifdef CGAL_ISOSURFACING_3_DC_FUNCTORS_DEBUG
|
||||||
|
std::cout << "Points and normals: " << std::endl;
|
||||||
|
for(std::size_t i=0; i<cell_edge_intersections.size(); ++i)
|
||||||
|
{
|
||||||
|
std::cout << x_coord(cell_edge_intersections[i]) << " "
|
||||||
|
<< y_coord(cell_edge_intersections[i]) << " "
|
||||||
|
<< z_coord(cell_edge_intersections[i]) << " "
|
||||||
|
<< x_coord(cell_edge_intersections[i]) + x_coord(cell_edge_intersection_normals[i]) << " "
|
||||||
|
<< y_coord(cell_edge_intersections[i]) + y_coord(cell_edge_intersection_normals[i]) << " "
|
||||||
|
<< z_coord(cell_edge_intersections[i]) + z_coord(cell_edge_intersection_normals[i]) << std::endl;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FT x_min, y_min, z_min, x_max, y_max, z_max;
|
||||||
|
x_min = y_min = z_min = (std::numeric_limits<double>::max)();
|
||||||
|
x_max = y_max = z_max = - (std::numeric_limits<double>::max)();
|
||||||
|
FT x(0), y(0), z(0);
|
||||||
|
|
||||||
|
if(constrain_to_cell)
|
||||||
|
{
|
||||||
|
typename Domain::Cell_vertices vertices = domain.cell_vertices(c);
|
||||||
|
for(const auto& v : vertices)
|
||||||
|
{
|
||||||
|
const Point_3& cp = domain.point(v);
|
||||||
|
x_min = (std::min<FT>)(x_min, x_coord(cp));
|
||||||
|
y_min = (std::min<FT>)(y_min, y_coord(cp));
|
||||||
|
z_min = (std::min<FT>)(z_min, z_coord(cp));
|
||||||
|
|
||||||
|
x_max = (std::max<FT>)(x_max, x_coord(cp));
|
||||||
|
y_max = (std::max<FT>)(y_max, y_coord(cp));
|
||||||
|
z_max = (std::max<FT>)(z_max, z_coord(cp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const auto& ep : cell_edge_intersections)
|
||||||
|
{
|
||||||
|
x += x_coord(ep);
|
||||||
|
y += y_coord(ep);
|
||||||
|
z += z_coord(ep);
|
||||||
|
}
|
||||||
|
|
||||||
|
Point_3 com = point(x / FT(en), y / FT(en), z / FT(en));
|
||||||
|
|
||||||
|
#ifdef CGAL_ISOSURFACING_3_DC_FUNCTORS_DEBUG
|
||||||
|
std::cout << "cell: " << x_min << " " << y_min << " " << z_min << " " << x_max << " " << y_max << " " << z_max << std::endl;
|
||||||
|
std::cout << "COM: " << com << std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// SVD QEM
|
||||||
|
Eigen_matrix_3 A;
|
||||||
|
A.setZero();
|
||||||
|
Eigen_vector_3 rhs;
|
||||||
|
rhs.setZero();
|
||||||
|
for(std::size_t i=0; i<cell_edge_intersections.size(); ++i)
|
||||||
|
{
|
||||||
|
Eigen_vector_3 n_k;
|
||||||
|
n_k.set(0, x_coord(cell_edge_intersection_normals[i]));
|
||||||
|
n_k.set(1, y_coord(cell_edge_intersection_normals[i]));
|
||||||
|
n_k.set(2, z_coord(cell_edge_intersection_normals[i]));
|
||||||
|
|
||||||
|
Eigen_vector_3 p_k;
|
||||||
|
p_k.set(0, x_coord(cell_edge_intersections[i]));
|
||||||
|
p_k.set(1, y_coord(cell_edge_intersections[i]));
|
||||||
|
p_k.set(2, z_coord(cell_edge_intersections[i]));
|
||||||
|
|
||||||
|
const FT d_k = n_k.transpose() * p_k;
|
||||||
|
|
||||||
|
// have to cast to cast to the underlying Eigen type because the type
|
||||||
|
// of 'n_k * n_k.transpose()' is Eigen::Product< ... > etc.,
|
||||||
|
// so the double conversion is not implicit
|
||||||
|
Eigen_matrix_3 A_k = typename Eigen_matrix_3::EigenType(n_k * n_k.transpose());
|
||||||
|
Eigen_vector_3 b_k;
|
||||||
|
b_k = d_k * n_k;
|
||||||
|
A += A_k;
|
||||||
|
rhs += b_k;
|
||||||
|
}
|
||||||
|
|
||||||
|
Eigen::JacobiSVD<typename Eigen_matrix_x::EigenType> svd(A, Eigen::ComputeThinU | Eigen::ComputeThinV);
|
||||||
|
|
||||||
|
// Ju's paper, "Dual Contouring of Hermite Data": 1e-1
|
||||||
|
// Lindstrom's paper, "Out-of-Core Simplification of Large Polygonal Models": 1e-3
|
||||||
|
svd.setThreshold(1e-3);
|
||||||
|
|
||||||
|
Eigen_vector_3 x_hat;
|
||||||
|
x_hat << x_coord(com), y_coord(com), z_coord(com);
|
||||||
|
|
||||||
|
// Lindstrom formula for QEM new position for singular matrices
|
||||||
|
Eigen_vector_x v_svd;
|
||||||
|
v_svd = x_hat + svd.solve(rhs - A * x_hat);
|
||||||
|
|
||||||
|
if(constrain_to_cell)
|
||||||
|
{
|
||||||
|
// @todo clamping back doesn't necessarily yield the optimal position within the cell
|
||||||
|
v_svd[0] = std::clamp<FT>(v_svd[0], x_min, x_max);
|
||||||
|
v_svd[1] = std::clamp<FT>(v_svd[1], y_min, y_max);
|
||||||
|
v_svd[2] = std::clamp<FT>(v_svd[2], z_min, z_max);
|
||||||
|
}
|
||||||
|
|
||||||
|
p = point(v_svd[0], v_svd[1], v_svd[2]);
|
||||||
|
|
||||||
|
#ifdef CGAL_ISOSURFACING_3_DC_FUNCTORS_DEBUG
|
||||||
|
std::cout << "CGAL QEM POINT: " << v_svd[0] << " " << v_svd[1] << " " << v_svd[2] << std::endl;
|
||||||
|
std::cout << "CGAL clamped QEM POINT: " << p[0] << " " << p[1] << " " << p[2] << std::endl;
|
||||||
|
std::cout << "--" << std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Domain,
|
||||||
|
typename EdgeToPointIDMap,
|
||||||
|
typename CellToPointIDMap,
|
||||||
|
typename PolygonRange>
|
||||||
|
void generate_face(const typename Domain::edge_descriptor& e,
|
||||||
|
const Domain& domain,
|
||||||
|
const typename Domain::Geom_traits::FT isovalue,
|
||||||
|
const bool do_not_triangulate_faces,
|
||||||
|
const EdgeToPointIDMap& edge_to_point_id,
|
||||||
|
const CellToPointIDMap& cell_to_point_id,
|
||||||
|
std::mutex& mutex,
|
||||||
|
PolygonRange& polygons)
|
||||||
|
{
|
||||||
|
using FT = typename Domain::Geom_traits::FT;
|
||||||
|
|
||||||
|
using cell_descriptor = typename Domain::cell_descriptor;
|
||||||
|
|
||||||
|
const auto& vertices = domain.incident_vertices(e);
|
||||||
|
|
||||||
|
// @todo this check could be avoided for QEM: active edges are in `edge_to_point_id`
|
||||||
|
const FT val_0 = domain.value(vertices[0]);
|
||||||
|
const FT val_1 = domain.value(vertices[1]);
|
||||||
|
if((val_0 <= isovalue) == (val_1 <= isovalue))
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::vector<std::size_t> vertex_ids;
|
||||||
|
|
||||||
|
const auto& icells = domain.incident_cells(e);
|
||||||
|
for(const cell_descriptor& c : icells)
|
||||||
|
{
|
||||||
|
auto it = cell_to_point_id.find(c);
|
||||||
|
if(it == cell_to_point_id.end())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
vertex_ids.push_back(it->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(vertex_ids.size() < 3)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(val_0 > val_1)
|
||||||
|
std::reverse(vertex_ids.begin(), vertex_ids.end());
|
||||||
|
|
||||||
|
// @todo? filter degenerate faces?
|
||||||
|
|
||||||
|
if(do_not_triangulate_faces)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
polygons.emplace_back();
|
||||||
|
CGAL::internal::resize(polygons.back(), vertex_ids.size());
|
||||||
|
std::copy(vertex_ids.begin(), vertex_ids.end(), std::begin(polygons.back()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto it = edge_to_point_id.find(e);
|
||||||
|
if(it == edge_to_point_id.end())
|
||||||
|
{
|
||||||
|
CGAL_assertion(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const std::size_t ei = it->second;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
for(std::size_t i=0; i<vertex_ids.size(); ++i)
|
||||||
|
polygons.push_back({ei, vertex_ids[i], vertex_ids[(i + 1) % vertex_ids.size()]});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
namespace DC_Strategy {
|
||||||
|
|
||||||
|
struct QEM {};
|
||||||
|
struct Centroid_of_edge_intersections {};
|
||||||
|
struct Cell_center {};
|
||||||
|
|
||||||
|
} // namespace DC_Strategy
|
||||||
|
|
||||||
|
template <typename ConcurrencyTag,
|
||||||
|
typename Domain,
|
||||||
|
typename Dual_contouring_strategy>
|
||||||
|
class Dual_contourer;
|
||||||
|
|
||||||
|
template <typename ConcurrencyTag,
|
||||||
|
typename Domain>
|
||||||
|
class Dual_contourer<ConcurrencyTag, Domain, DC_Strategy::QEM>
|
||||||
|
{
|
||||||
|
using Geom_traits = typename Domain::Geom_traits;
|
||||||
|
using FT = typename Geom_traits::FT;
|
||||||
|
using Point_3 = typename Geom_traits::Point_3;
|
||||||
|
using Vector_3 = typename Geom_traits::Vector_3;
|
||||||
|
|
||||||
|
using vertex_descriptor = typename Domain::vertex_descriptor;
|
||||||
|
using edge_descriptor = typename Domain::edge_descriptor;
|
||||||
|
using cell_descriptor = typename Domain::cell_descriptor;
|
||||||
|
|
||||||
|
std::mutex m_mutex;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template<typename PointRange, typename PolygonRange, typename NamedParameters>
|
||||||
|
void operator()(const Domain& domain,
|
||||||
|
const FT isovalue,
|
||||||
|
PointRange& points,
|
||||||
|
PolygonRange& polygons,
|
||||||
|
const NamedParameters& np = parameters::default_values())
|
||||||
|
{
|
||||||
|
using parameters::choose_parameter;
|
||||||
|
using parameters::get_parameter;
|
||||||
|
|
||||||
|
// Otherwise the `edge_to_point_id` map might be messed up
|
||||||
|
CGAL_precondition(points.empty());
|
||||||
|
CGAL_precondition(polygons.empty());
|
||||||
|
|
||||||
|
const bool constrain_to_cell = choose_parameter(get_parameter(np, internal_np::constrain_to_cell), false);
|
||||||
|
|
||||||
|
const bool do_not_triangulate_faces =
|
||||||
|
choose_parameter(get_parameter(np, internal_np::do_not_triangulate_faces), false);
|
||||||
|
|
||||||
|
using Edge_to_point_ID_map = std::unordered_map<edge_descriptor, std::size_t>;
|
||||||
|
using Cell_to_point_ID_map = std::unordered_map<cell_descriptor, std::size_t>;
|
||||||
|
|
||||||
|
Edge_to_point_ID_map edge_to_point_id;
|
||||||
|
Cell_to_point_ID_map cell_to_point_id;
|
||||||
|
|
||||||
|
std::vector<Point_3> edge_points;
|
||||||
|
std::vector<Vector_3> edge_gradients;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------------------
|
||||||
|
// construct the intersection of the surface at active edges
|
||||||
|
auto edge_positioner = [&](const edge_descriptor& e)
|
||||||
|
{
|
||||||
|
const auto& evs = domain.incident_vertices(e);
|
||||||
|
const vertex_descriptor& v0 = evs[0];
|
||||||
|
const vertex_descriptor& v1 = evs[1];
|
||||||
|
const Point_3& p0 = domain.point(v0);
|
||||||
|
const Point_3& p1 = domain.point(v1);
|
||||||
|
const FT val0 = domain.value(v0);
|
||||||
|
const FT val1 = domain.value(v1);
|
||||||
|
|
||||||
|
Point_3 p;
|
||||||
|
bool res = domain.construct_intersection(p0, p1, val0, val1, isovalue, p);
|
||||||
|
if(!res)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const Vector_3 g = domain.gradient(p);
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
edge_to_point_id[e] = edge_points.size();
|
||||||
|
edge_points.push_back(p);
|
||||||
|
edge_gradients.push_back(g);
|
||||||
|
};
|
||||||
|
domain.template for_each_edge<ConcurrencyTag>(edge_positioner);
|
||||||
|
|
||||||
|
#ifdef CGAL_ISOSURFACING_3_DC_FUNCTORS_DEBUG
|
||||||
|
typename Geom_traits::Compute_x_3 x_coord = domain.geom_traits().compute_x_3_object();
|
||||||
|
typename Geom_traits::Compute_y_3 y_coord = domain.geom_traits().compute_y_3_object();
|
||||||
|
typename Geom_traits::Compute_z_3 z_coord = domain.geom_traits().compute_z_3_object();
|
||||||
|
|
||||||
|
std::ofstream out_active_edges("active_edges.polylines.cgal");
|
||||||
|
for(const auto& ei : edge_to_point_id)
|
||||||
|
{
|
||||||
|
const edge_descriptor& e = ei.first;
|
||||||
|
const auto& evs = domain.incident_vertices(e);
|
||||||
|
const vertex_descriptor& v0 = evs[0];
|
||||||
|
const vertex_descriptor& v1 = evs[1];
|
||||||
|
const Point_3& p0 = domain.point(v0);
|
||||||
|
const Point_3& p1 = domain.point(v1);
|
||||||
|
|
||||||
|
out_active_edges << "2 " << x_coord(p0) << " " << y_coord(p0) << " " << z_coord(p0) << " " << x_coord(p1) << " " << y_coord(p1) << " " << z_coord(p1) << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ofstream out_edge_intersections("edge_intersections.polylines.cgal");
|
||||||
|
for(const auto& ei : edge_to_point_id)
|
||||||
|
{
|
||||||
|
const Point_3& p = edge_points.at(ei.second);
|
||||||
|
const Vector_3& g = edge_gradients.at(ei.second);
|
||||||
|
|
||||||
|
out_edge_intersections << "2 " << x_coord(p) << " " << y_coord(p) << " " << z_coord(p) << " " << x_coord(p) + x_coord(g) << " " << y_coord(p) + y_coord(g) << " " << z_coord(p) + z_coord(g) << std::endl;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(!do_not_triangulate_faces)
|
||||||
|
points.insert(points.end(), edge_points.begin(), edge_points.end());
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------------------
|
||||||
|
// create a vertex for each cell that has at least one active edge
|
||||||
|
auto cell_positioner = [&](const cell_descriptor& c)
|
||||||
|
{
|
||||||
|
Point_3 p;
|
||||||
|
if(cell_position_QEM(c, domain, constrain_to_cell, edge_to_point_id,
|
||||||
|
edge_points, edge_gradients, p))
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex); // @todo useless if sequential
|
||||||
|
cell_to_point_id[c] = points.size();
|
||||||
|
points.push_back(p);
|
||||||
|
|
||||||
|
#ifdef CGAL_ISOSURFACING_3_DC_FUNCTORS_DEBUG
|
||||||
|
static std::ofstream os("dual_to_sources.polylines.cgal");
|
||||||
|
for(const auto& edge : domain.cell_edges(c))
|
||||||
|
{
|
||||||
|
const auto it = edge_to_point_id.find(edge);
|
||||||
|
if(it == edge_to_point_id.end())
|
||||||
|
continue;
|
||||||
|
os << "2 " << edge_points.at(it->second) << " " << p << "\n";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
domain.template for_each_cell<ConcurrencyTag>(cell_positioner);
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------------------
|
||||||
|
// connect vertices around edges to form faces
|
||||||
|
auto face_generator = [&](const edge_descriptor& e)
|
||||||
|
{
|
||||||
|
generate_face(e, domain, isovalue, do_not_triangulate_faces,
|
||||||
|
edge_to_point_id, cell_to_point_id, m_mutex, polygons);
|
||||||
|
};
|
||||||
|
domain.template for_each_edge<ConcurrencyTag>(face_generator);
|
||||||
|
|
||||||
|
#ifdef CGAL_ISOSURFACING_3_DC_FUNCTORS_DEBUG
|
||||||
|
std::cout << points.size() << " points" << std::endl;
|
||||||
|
std::cout << polygons.size() << " polygons" << std::endl;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename ConcurrencyTag,
|
||||||
|
typename Domain>
|
||||||
|
class Dual_contourer<ConcurrencyTag, Domain, DC_Strategy::Centroid_of_edge_intersections>
|
||||||
|
{
|
||||||
|
using Geom_traits = typename Domain::Geom_traits;
|
||||||
|
using FT = typename Geom_traits::FT;
|
||||||
|
using Point_3 = typename Geom_traits::Point_3;
|
||||||
|
using Vector_3 = typename Geom_traits::Vector_3;
|
||||||
|
|
||||||
|
using vertex_descriptor = typename Domain::vertex_descriptor;
|
||||||
|
using edge_descriptor = typename Domain::edge_descriptor;
|
||||||
|
using cell_descriptor = typename Domain::cell_descriptor;
|
||||||
|
|
||||||
|
std::mutex m_mutex;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template<typename PointRange, typename PolygonRange, typename NamedParameters>
|
||||||
|
void operator()(const Domain& domain,
|
||||||
|
const typename Geom_traits::FT isovalue,
|
||||||
|
PointRange& points,
|
||||||
|
PolygonRange& polygons,
|
||||||
|
const NamedParameters& np = parameters::default_values())
|
||||||
|
{
|
||||||
|
using parameters::choose_parameter;
|
||||||
|
using parameters::get_parameter;
|
||||||
|
|
||||||
|
// Otherwise the `edge_to_point_id` map might be messed up
|
||||||
|
CGAL_precondition(points.empty());
|
||||||
|
CGAL_precondition(polygons.empty());
|
||||||
|
|
||||||
|
bool do_not_triangulate_faces =
|
||||||
|
choose_parameter(get_parameter(np, internal_np::do_not_triangulate_faces), false);
|
||||||
|
|
||||||
|
using Edge_to_point_ID_map = std::unordered_map<edge_descriptor, std::size_t>;
|
||||||
|
using Cell_to_point_ID_map = std::unordered_map<cell_descriptor, std::size_t>;
|
||||||
|
|
||||||
|
Edge_to_point_ID_map edge_to_point_id;
|
||||||
|
Cell_to_point_ID_map cell_to_point_id;
|
||||||
|
|
||||||
|
std::vector<Point_3> edge_points;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------------------
|
||||||
|
auto edge_positioner = [&](const edge_descriptor& e)
|
||||||
|
{
|
||||||
|
const auto& evs = domain.incident_vertices(e);
|
||||||
|
const vertex_descriptor& v0 = evs[0];
|
||||||
|
const vertex_descriptor& v1 = evs[1];
|
||||||
|
const Point_3& p0 = domain.point(v0);
|
||||||
|
const Point_3& p1 = domain.point(v1);
|
||||||
|
const FT val0 = domain.value(v0);
|
||||||
|
const FT val1 = domain.value(v1);
|
||||||
|
|
||||||
|
Point_3 p;
|
||||||
|
bool res = domain.construct_intersection(p0, p1, val0, val1, isovalue, p);
|
||||||
|
if(!res)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
edge_to_point_id[e] = edge_points.size();
|
||||||
|
edge_points.push_back(p);
|
||||||
|
};
|
||||||
|
domain.template for_each_edge<ConcurrencyTag>(edge_positioner);
|
||||||
|
|
||||||
|
if(!do_not_triangulate_faces)
|
||||||
|
points.insert(points.end(), edge_points.begin(), edge_points.end());
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------------------
|
||||||
|
auto cell_positioner = [&](const cell_descriptor& c)
|
||||||
|
{
|
||||||
|
typename Geom_traits::Compute_x_3 x_coord = domain.geom_traits().compute_x_3_object();
|
||||||
|
typename Geom_traits::Compute_y_3 y_coord = domain.geom_traits().compute_y_3_object();
|
||||||
|
typename Geom_traits::Compute_z_3 z_coord = domain.geom_traits().compute_z_3_object();
|
||||||
|
typename Geom_traits::Construct_point_3 point = domain.geom_traits().construct_point_3_object();
|
||||||
|
|
||||||
|
// compute edge intersections
|
||||||
|
std::vector<Point_3> edge_intersections;
|
||||||
|
for(const edge_descriptor& e : domain.cell_edges(c))
|
||||||
|
{
|
||||||
|
const auto it = edge_to_point_id.find(e);
|
||||||
|
if(it == edge_to_point_id.end())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
edge_intersections.push_back(edge_points[it->second]); // @todo could avoid copying
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::size_t en = edge_intersections.size();
|
||||||
|
if(en == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
FT x = 0, y = 0, z = 0;
|
||||||
|
for(const Point_3& p : edge_intersections)
|
||||||
|
{
|
||||||
|
x += x_coord(p);
|
||||||
|
y += y_coord(p);
|
||||||
|
z += z_coord(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Point_3 p = point(x / FT(en), y / FT(en), z / FT(en));
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
cell_to_point_id[c] = points.size();
|
||||||
|
points.push_back(p);
|
||||||
|
};
|
||||||
|
domain.template for_each_cell<ConcurrencyTag>(cell_positioner);
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------------------
|
||||||
|
auto face_generator = [&](const edge_descriptor& e)
|
||||||
|
{
|
||||||
|
generate_face(e, domain, isovalue, do_not_triangulate_faces,
|
||||||
|
edge_to_point_id, cell_to_point_id, m_mutex, polygons);
|
||||||
|
};
|
||||||
|
domain.template for_each_edge<ConcurrencyTag>(face_generator);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename ConcurrencyTag,
|
||||||
|
typename Domain>
|
||||||
|
class Dual_contourer<ConcurrencyTag, Domain, DC_Strategy::Cell_center>
|
||||||
|
{
|
||||||
|
using Geom_traits = typename Domain::Geom_traits;
|
||||||
|
using FT = typename Geom_traits::FT;
|
||||||
|
using Point_3 = typename Geom_traits::Point_3;
|
||||||
|
using Vector_3 = typename Geom_traits::Vector_3;
|
||||||
|
|
||||||
|
using vertex_descriptor = typename Domain::vertex_descriptor;
|
||||||
|
using edge_descriptor = typename Domain::edge_descriptor;
|
||||||
|
using cell_descriptor = typename Domain::cell_descriptor;
|
||||||
|
|
||||||
|
std::mutex m_mutex;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template<typename PointRange, typename PolygonRange, typename NamedParameters>
|
||||||
|
void operator()(const Domain& domain,
|
||||||
|
const FT isovalue,
|
||||||
|
PointRange& points,
|
||||||
|
PolygonRange& polygons,
|
||||||
|
const NamedParameters& np = parameters::default_values())
|
||||||
|
{
|
||||||
|
using parameters::choose_parameter;
|
||||||
|
using parameters::get_parameter;
|
||||||
|
|
||||||
|
// Otherwise the `edge_to_point_id` map might be messed up
|
||||||
|
CGAL_precondition(points.empty());
|
||||||
|
CGAL_precondition(polygons.empty());
|
||||||
|
|
||||||
|
bool do_not_triangulate_faces =
|
||||||
|
choose_parameter(get_parameter(np, internal_np::do_not_triangulate_faces), false);
|
||||||
|
|
||||||
|
using Edge_to_point_ID_map = std::unordered_map<edge_descriptor, std::size_t>;
|
||||||
|
using Cell_to_point_ID_map = std::unordered_map<cell_descriptor, std::size_t>;
|
||||||
|
|
||||||
|
Edge_to_point_ID_map edge_to_point_id;
|
||||||
|
Cell_to_point_ID_map cell_to_point_id;
|
||||||
|
|
||||||
|
std::vector<Point_3> edge_points;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------------------
|
||||||
|
auto edge_positioner = [&](const edge_descriptor& e)
|
||||||
|
{
|
||||||
|
typename Geom_traits::Compute_x_3 x_coord = domain.geom_traits().compute_x_3_object();
|
||||||
|
typename Geom_traits::Compute_y_3 y_coord = domain.geom_traits().compute_y_3_object();
|
||||||
|
typename Geom_traits::Compute_z_3 z_coord = domain.geom_traits().compute_z_3_object();
|
||||||
|
typename Geom_traits::Construct_point_3 point = domain.geom_traits().construct_point_3_object();
|
||||||
|
|
||||||
|
const auto& evs = domain.incident_vertices(e);
|
||||||
|
const vertex_descriptor& v0 = evs[0];
|
||||||
|
const vertex_descriptor& v1 = evs[1];
|
||||||
|
|
||||||
|
const FT val_0 = domain.value(v0);
|
||||||
|
const FT val_1 = domain.value(v1);
|
||||||
|
if((val_0 <= isovalue) == (val_1 <= isovalue))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Point_3 p = point((x_coord(domain.point(v0)) + x_coord(domain.point(v1))) / FT(2),
|
||||||
|
(y_coord(domain.point(v0)) + y_coord(domain.point(v1))) / FT(2),
|
||||||
|
(z_coord(domain.point(v0)) + z_coord(domain.point(v1))) / FT(2));
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
edge_to_point_id[e] = edge_points.size();
|
||||||
|
edge_points.push_back(p);
|
||||||
|
};
|
||||||
|
domain.template for_each_edge<ConcurrencyTag>(edge_positioner);
|
||||||
|
|
||||||
|
if(!do_not_triangulate_faces)
|
||||||
|
points.insert(points.end(), edge_points.begin(), edge_points.end());
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------------------
|
||||||
|
auto cell_positioner = [&](const cell_descriptor& c)
|
||||||
|
{
|
||||||
|
typename Geom_traits::Compute_x_3 x_coord = domain.geom_traits().compute_x_3_object();
|
||||||
|
typename Geom_traits::Compute_y_3 y_coord = domain.geom_traits().compute_y_3_object();
|
||||||
|
typename Geom_traits::Compute_z_3 z_coord = domain.geom_traits().compute_z_3_object();
|
||||||
|
typename Geom_traits::Construct_point_3 point = domain.geom_traits().construct_point_3_object();
|
||||||
|
|
||||||
|
typename Domain::Cell_vertices vertices = domain.cell_vertices(c);
|
||||||
|
const std::size_t cn = vertices.size();
|
||||||
|
|
||||||
|
bool all_smaller = true;
|
||||||
|
bool all_greater = true;
|
||||||
|
for(const auto& v : vertices)
|
||||||
|
{
|
||||||
|
const bool b = (domain.value(v) <= isovalue);
|
||||||
|
all_smaller = all_smaller && b;
|
||||||
|
all_greater = all_greater && !b;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(all_smaller || all_greater)
|
||||||
|
return;
|
||||||
|
|
||||||
|
FT x(0), y(0), z(0);
|
||||||
|
for(const auto& v : vertices)
|
||||||
|
{
|
||||||
|
const Point_3& cp = domain.point(v);
|
||||||
|
x += x_coord(cp);
|
||||||
|
y += y_coord(cp);
|
||||||
|
z += z_coord(cp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set point to cell center
|
||||||
|
Point_3 p = point(x / cn, y / cn, z / cn);
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
cell_to_point_id[c] = points.size();
|
||||||
|
points.push_back(p);
|
||||||
|
};
|
||||||
|
domain.template for_each_cell<ConcurrencyTag>(cell_positioner);
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------------------
|
||||||
|
auto face_generator = [&](const edge_descriptor& e)
|
||||||
|
{
|
||||||
|
generate_face(e, domain, isovalue, do_not_triangulate_faces,
|
||||||
|
edge_to_point_id, cell_to_point_id, m_mutex, polygons);
|
||||||
|
};
|
||||||
|
domain.template for_each_edge<ConcurrencyTag>(face_generator);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace Isosurfacing
|
||||||
|
} // namespace CGAL
|
||||||
|
|
||||||
|
#endif // CGAL_ISOSURFACING_3_INTERNAL_DUAL_CONTOURING_FUNCTORS_H
|
||||||
|
|
@ -0,0 +1,164 @@
|
||||||
|
// Copyright (c) 2024 INRIA Sophia-Antipolis (France).
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of CGAL (www.cgal.org).
|
||||||
|
//
|
||||||
|
// $URL$
|
||||||
|
// $Id$
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
|
||||||
|
//
|
||||||
|
// Author(s) : Mael Rouxel-Labbé
|
||||||
|
|
||||||
|
// This file is only used by the testsuite and examples
|
||||||
|
|
||||||
|
#ifndef CGAL_ISOSURFACING_3_INTERNAL_IMPLICIT_SHAPES_HELPER_H
|
||||||
|
#define CGAL_ISOSURFACING_3_INTERNAL_IMPLICIT_SHAPES_HELPER_H
|
||||||
|
|
||||||
|
#include <CGAL/license/Isosurfacing_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/Simple_cartesian.h> // quiet the CI in case this file gets compiled alone
|
||||||
|
|
||||||
|
namespace CGAL {
|
||||||
|
namespace Isosurfacing {
|
||||||
|
namespace Shapes {
|
||||||
|
|
||||||
|
// Shapes are defined at isovalue 0
|
||||||
|
|
||||||
|
// c is the center
|
||||||
|
// r the radius
|
||||||
|
template<typename K>
|
||||||
|
typename K::FT
|
||||||
|
sphere(const typename K::Point_3& c,
|
||||||
|
const typename K::FT r,
|
||||||
|
const typename K::Point_3& q)
|
||||||
|
{
|
||||||
|
return CGAL::approximate_sqrt(CGAL::squared_distance(c, q)) - r;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename K>
|
||||||
|
typename K::FT
|
||||||
|
box(const typename K::Point_3& b,
|
||||||
|
const typename K::Point_3& t,
|
||||||
|
const typename K::Point_3& q)
|
||||||
|
{
|
||||||
|
typename K::Point_3 c = CGAL::midpoint(b, t);
|
||||||
|
typename K::Iso_cuboid_3 ic(b, t);
|
||||||
|
bool inside = ic.has_on_bounded_side(q);
|
||||||
|
typename K::FT d = 0;
|
||||||
|
if(inside)
|
||||||
|
{
|
||||||
|
d = (std::min)({CGAL::abs(q.x() - b.x()), CGAL::abs(q.x() - t.x()),
|
||||||
|
CGAL::abs(q.y() - b.y()), CGAL::abs(q.y() - t.y()),
|
||||||
|
CGAL::abs(q.z() - b.z()), CGAL::abs(q.z() - t.z())});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for(int i=0; i<3; ++i)
|
||||||
|
d += (CGAL::abs(q[i] - c[i]) > (c[i] - b[i]) ? CGAL::square(q[i] - c[i]) : 0);
|
||||||
|
d = CGAL::approximate_sqrt(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
return inside ? - d : d;
|
||||||
|
}
|
||||||
|
|
||||||
|
// p is the center of the base disk
|
||||||
|
// q is the center of the top disk
|
||||||
|
template<typename K>
|
||||||
|
typename K::FT
|
||||||
|
infinite_cylinder(const typename K::Point_3& b,
|
||||||
|
const typename K::Vector_3& n,
|
||||||
|
const typename K::FT r,
|
||||||
|
const typename K::Point_3& q)
|
||||||
|
{
|
||||||
|
typename K::Plane_3 pl(b, n);
|
||||||
|
typename K::Point_3 pq = pl.projection(q);
|
||||||
|
return CGAL::approximate_sqrt(CGAL::squared_distance(pq, b)) - r;
|
||||||
|
}
|
||||||
|
|
||||||
|
// c is the center of the torus
|
||||||
|
// n is the normal of the plane containing all centers of the tube
|
||||||
|
// r is the small radius
|
||||||
|
// R is the large radius
|
||||||
|
template<typename K>
|
||||||
|
typename K::FT
|
||||||
|
torus(const typename K::Point_3& c,
|
||||||
|
const typename K::Vector_3& n,
|
||||||
|
const typename K::FT r,
|
||||||
|
const typename K::FT R,
|
||||||
|
const typename K::Point_3& q)
|
||||||
|
{
|
||||||
|
typename K::Vector_3 w (c, q);
|
||||||
|
typename K::Plane_3 pl(c, n);
|
||||||
|
typename K::Point_3 pq = pl.projection(q);
|
||||||
|
typename K::FT d = CGAL::approximate_sqrt(CGAL::squared_distance(pq, c)) - R;
|
||||||
|
typename K::FT h = CGAL::abs(CGAL::scalar_product(w, n));
|
||||||
|
|
||||||
|
return CGAL::approximate_sqrt(CGAL::square(d) + CGAL::square(h)) - r;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename K>
|
||||||
|
typename K::FT
|
||||||
|
torus_ridge(const typename K::Point_3& c,
|
||||||
|
const typename K::Vector_3& n,
|
||||||
|
const typename K::FT r,
|
||||||
|
const typename K::FT R,
|
||||||
|
const typename K::Point_3& q)
|
||||||
|
{
|
||||||
|
typename K::Vector_3 w (c, q);
|
||||||
|
typename K::Plane_3 pl(c, n);
|
||||||
|
typename K::Point_3 pq = pl.projection(q);
|
||||||
|
typename K::FT d = CGAL::abs(CGAL::approximate_sqrt(CGAL::squared_distance(pq, c)) - R) - r;
|
||||||
|
return d + CGAL::squared_distance(q, pl);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename K>
|
||||||
|
typename K::FT
|
||||||
|
inverted_torus(const typename K::Point_3& c,
|
||||||
|
const typename K::Vector_3& n,
|
||||||
|
const typename K::FT r,
|
||||||
|
const typename K::FT R,
|
||||||
|
const typename K::Point_3& q)
|
||||||
|
{
|
||||||
|
typename K::Vector_3 w (c, q);
|
||||||
|
typename K::Plane_3 pl(c, n);
|
||||||
|
typename K::Point_3 pq = pl.projection(q);
|
||||||
|
typename K::FT d = CGAL::abs(CGAL::approximate_sqrt(CGAL::squared_distance(pq, c)) - R) - r;
|
||||||
|
return d - CGAL::squared_distance(q, pl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
template <typename K, typename S1, typename S2>
|
||||||
|
typename K::FT
|
||||||
|
shape_union(const S1& s1, const S2& s2, const typename K::Point_3& q)
|
||||||
|
{
|
||||||
|
return (std::min)(s1(q), s2(q));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename K, typename S1, typename S2>
|
||||||
|
typename K::FT
|
||||||
|
shape_difference(const S1& s1, const S2& s2, const typename K::Point_3& q)
|
||||||
|
{
|
||||||
|
return (std::max)(s1(q), -s2(q));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename K, typename S1, typename S2>
|
||||||
|
typename K::FT
|
||||||
|
shape_intersection(const S1& s1, const S2& s2, const typename K::Point_3& q)
|
||||||
|
{
|
||||||
|
return (std::max)(s1(q), s2(q));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename K, typename S1, typename S2>
|
||||||
|
typename K::FT
|
||||||
|
shape_symmetric_difference(const S1& s1, const S2& s2, const typename K::Point_3& q)
|
||||||
|
{
|
||||||
|
return (std::max)(-(std::min)(s1(q), s2(q)), (std::max)(s1(q), s2(q)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace Shapes
|
||||||
|
} // namespace Isosurfacing
|
||||||
|
} // namespace CGAL
|
||||||
|
|
||||||
|
#endif // CGAL_ISOSURFACING_3_INTERNAL_IMPLICIT_SHAPES_HELPER_H
|
||||||
|
|
@ -0,0 +1,334 @@
|
||||||
|
// Copyright (c) 2020 INRIA Sophia-Antipolis (France).
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of CGAL (www.cgal.org).
|
||||||
|
//
|
||||||
|
// $URL$
|
||||||
|
// $Id$
|
||||||
|
// SPDX-License-Identifier: ( GPL-3.0-or-later OR LicenseRef-Commercial ) AND MIT
|
||||||
|
//
|
||||||
|
// Author(s) : Julian Stahl
|
||||||
|
//
|
||||||
|
// This file incorporates work covered by the following copyright and permission notice:
|
||||||
|
//
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2020 Roberto Grosso
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// The code below uses the version of
|
||||||
|
// https://github.com/rogrosso/tmc available on 15th of September 2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef CGAL_ISOSURFACING_3_INTERNAL_MARCHING_CUBES_FUNCTORS_H
|
||||||
|
#define CGAL_ISOSURFACING_3_INTERNAL_MARCHING_CUBES_FUNCTORS_H
|
||||||
|
|
||||||
|
#include <CGAL/license/Isosurfacing_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/Isosurfacing_3/internal/tables.h>
|
||||||
|
|
||||||
|
#include <CGAL/assertions.h>
|
||||||
|
#include <CGAL/Container_helper.h>
|
||||||
|
|
||||||
|
#ifdef CGAL_LINKED_WITH_TBB
|
||||||
|
#include <tbb/enumerable_thread_specific.h>
|
||||||
|
#else
|
||||||
|
#include <vector>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <bitset>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace CGAL {
|
||||||
|
namespace Isosurfacing {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// Interpolate linearly between two vertex locations v0, v1 with values d0 and d1 according to the isovalue
|
||||||
|
template <typename GeomTraits>
|
||||||
|
std::pair<typename GeomTraits::Point_3, typename GeomTraits::FT>
|
||||||
|
vertex_interpolation(const typename GeomTraits::Point_3& p0,
|
||||||
|
const typename GeomTraits::Point_3& p1,
|
||||||
|
const typename GeomTraits::FT d0,
|
||||||
|
const typename GeomTraits::FT d1,
|
||||||
|
const typename GeomTraits::FT isovalue,
|
||||||
|
const GeomTraits& gt)
|
||||||
|
{
|
||||||
|
using FT = typename GeomTraits::FT;
|
||||||
|
|
||||||
|
typename GeomTraits::Compute_x_3 x_coord = gt.compute_x_3_object();
|
||||||
|
typename GeomTraits::Compute_y_3 y_coord = gt.compute_y_3_object();
|
||||||
|
typename GeomTraits::Compute_z_3 z_coord = gt.compute_z_3_object();
|
||||||
|
typename GeomTraits::Construct_point_3 point = gt.construct_point_3_object();
|
||||||
|
|
||||||
|
// @todo, technically we should be using the edge intersection oracle here, but there is a nuance
|
||||||
|
// between MC and DC on the handling of edges that have val0 = val1 = isovalue: in MC we assume
|
||||||
|
// the isosurface is in the middle, in DC we assume the isosurface is not intersecting the edge.
|
||||||
|
// In the oracle, we follow DC right now. Could put a Boolean parameter, but it's ugly.
|
||||||
|
|
||||||
|
const FT den = d1 - d0;
|
||||||
|
FT mu = is_zero(den) ? FT(1) / FT(2) : (isovalue - d0) / den;
|
||||||
|
mu = std::clamp<FT>(mu, FT(0), FT(1));
|
||||||
|
|
||||||
|
// linear interpolation
|
||||||
|
return { point((FT(1) - mu) * x_coord(p0) + mu * x_coord(p1),
|
||||||
|
(FT(1) - mu) * y_coord(p0) + mu * y_coord(p1),
|
||||||
|
(FT(1) - mu) * z_coord(p0) + mu * z_coord(p1)), mu };
|
||||||
|
}
|
||||||
|
|
||||||
|
// retrieves the values of a cell and return the lookup index
|
||||||
|
// if the cell is completely above or below the isovalue, corner points are not computed
|
||||||
|
template <typename Domain,
|
||||||
|
typename Corners,
|
||||||
|
typename Values>
|
||||||
|
std::size_t get_cell_corners(const Domain& domain,
|
||||||
|
const typename Domain::cell_descriptor& cell,
|
||||||
|
const typename Domain::Geom_traits::FT isovalue,
|
||||||
|
Corners& corners,
|
||||||
|
Values& values,
|
||||||
|
bool isovalue_nudging)
|
||||||
|
{
|
||||||
|
using vertex_descriptor = typename Domain::vertex_descriptor;
|
||||||
|
|
||||||
|
const auto& vertices = domain.cell_vertices(cell);
|
||||||
|
|
||||||
|
// collect function values and build index
|
||||||
|
std::size_t v_id = 0;
|
||||||
|
std::bitset<Domain::VERTICES_PER_CELL> index = 0;
|
||||||
|
static_assert(Domain::VERTICES_PER_CELL == 8);
|
||||||
|
|
||||||
|
for(const vertex_descriptor& v : vertices)
|
||||||
|
{
|
||||||
|
auto val = domain.value(v);
|
||||||
|
|
||||||
|
// Avoiding singular cases.
|
||||||
|
if (isovalue_nudging && abs(val - isovalue) < 0.00000001)
|
||||||
|
val = isovalue - 0.00000001;
|
||||||
|
|
||||||
|
values[v_id] = val;
|
||||||
|
if(values[v_id] >= isovalue)
|
||||||
|
index.set(v_id);
|
||||||
|
|
||||||
|
++v_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(index.all() || index.none()) // nothing is happening in this cell
|
||||||
|
return static_cast<std::size_t>(index.to_ullong());
|
||||||
|
|
||||||
|
v_id = 0;
|
||||||
|
for(const vertex_descriptor& v : vertices)
|
||||||
|
corners[v_id++] = domain.point(v);
|
||||||
|
|
||||||
|
return static_cast<std::size_t>(index.to_ullong());
|
||||||
|
}
|
||||||
|
|
||||||
|
// creates the vertices on the edges of one cell
|
||||||
|
template <typename Corners,
|
||||||
|
typename Values,
|
||||||
|
typename Domain,
|
||||||
|
typename Vertices>
|
||||||
|
void MC_construct_vertices(const typename Domain::cell_descriptor& cell,
|
||||||
|
const std::size_t i_case,
|
||||||
|
const Corners& corners,
|
||||||
|
const Values& values,
|
||||||
|
const typename Domain::Geom_traits::FT isovalue,
|
||||||
|
const Domain& domain,
|
||||||
|
Vertices& vertices)
|
||||||
|
{
|
||||||
|
using Cell_edges = typename Domain::Cell_edges;
|
||||||
|
using edge_descriptor = typename Domain::edge_descriptor;
|
||||||
|
|
||||||
|
const Cell_edges& cell_edges = domain.cell_edges(cell);
|
||||||
|
|
||||||
|
// compute for this case the vertices
|
||||||
|
std::size_t flag = 1;
|
||||||
|
std::size_t e_id = 0;
|
||||||
|
|
||||||
|
for(const edge_descriptor& e : cell_edges)
|
||||||
|
{
|
||||||
|
CGAL_USE(e);
|
||||||
|
|
||||||
|
if(flag & Cube_table::intersected_edges[i_case])
|
||||||
|
{
|
||||||
|
// generate vertex here, do not care at this point if vertex already exists
|
||||||
|
const int v0 = Cube_table::edge_to_vertex[e_id][0];
|
||||||
|
const int v1 = Cube_table::edge_to_vertex[e_id][1];
|
||||||
|
|
||||||
|
vertices[e_id] = vertex_interpolation(corners[v0], corners[v1],
|
||||||
|
values[v0], values[v1],
|
||||||
|
isovalue, domain.geom_traits()).first;
|
||||||
|
}
|
||||||
|
|
||||||
|
flag <<= 1;
|
||||||
|
++e_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// connects the vertices of one cell to form triangles
|
||||||
|
template <typename Vertices,
|
||||||
|
typename TriangleList>
|
||||||
|
void MC_construct_triangles(const std::size_t i_case,
|
||||||
|
const Vertices& vertices,
|
||||||
|
TriangleList& triangles)
|
||||||
|
{
|
||||||
|
// construct triangles
|
||||||
|
for(int t=0; t<16; t+=3)
|
||||||
|
{
|
||||||
|
const std::size_t t_index = i_case * 16 + t;
|
||||||
|
|
||||||
|
// if(e_tris_list[t_index] == 0x7f)
|
||||||
|
if(Cube_table::triangle_cases[t_index] == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
const int eg0 = Cube_table::triangle_cases[t_index + 0];
|
||||||
|
const int eg1 = Cube_table::triangle_cases[t_index + 1];
|
||||||
|
const int eg2 = Cube_table::triangle_cases[t_index + 2];
|
||||||
|
|
||||||
|
// insert new triangle in list
|
||||||
|
#ifdef CGAL_LINKED_WITH_TBB
|
||||||
|
auto& tris = triangles.local();
|
||||||
|
#else
|
||||||
|
auto& tris = triangles;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
tris.push_back({vertices[eg0], vertices[eg1], vertices[eg2]});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TriangleRange,
|
||||||
|
typename PointRange,
|
||||||
|
typename PolygonRange>
|
||||||
|
void triangles_to_polygon_soup(const TriangleRange& triangles,
|
||||||
|
PointRange& points,
|
||||||
|
PolygonRange& polygons)
|
||||||
|
{
|
||||||
|
using Point = typename PointRange::value_type;
|
||||||
|
using PointIndexMap = std::unordered_map<Point, std::size_t>;
|
||||||
|
|
||||||
|
PointIndexMap point_index_map;
|
||||||
|
std::size_t current_index = 0;
|
||||||
|
|
||||||
|
#ifdef CGAL_LINKED_WITH_TBB
|
||||||
|
for(const auto& triangle_list : triangles)
|
||||||
|
{
|
||||||
|
#else
|
||||||
|
const auto& triangle_list = triangles;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for(const auto& triangle : triangle_list)
|
||||||
|
{
|
||||||
|
auto& polygon = polygons.emplace_back();
|
||||||
|
CGAL::internal::resize(polygon, 3);
|
||||||
|
|
||||||
|
for (int i=2; i>=0; --i)
|
||||||
|
{
|
||||||
|
const Point& p = triangle[i];
|
||||||
|
|
||||||
|
auto [it, inserted] = point_index_map.emplace(p, current_index);
|
||||||
|
if(inserted)
|
||||||
|
{
|
||||||
|
points.push_back(p);
|
||||||
|
++current_index;
|
||||||
|
}
|
||||||
|
polygon[i] = it->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CGAL_LINKED_WITH_TBB
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marching Cubes implemented as a functor that runs on every cell of the grid
|
||||||
|
template <typename Domain_>
|
||||||
|
class Marching_cubes_3
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Domain = Domain_;
|
||||||
|
|
||||||
|
using Geom_traits = typename Domain::Geom_traits;
|
||||||
|
using FT = typename Geom_traits::FT;
|
||||||
|
using Point_3 = typename Geom_traits::Point_3;
|
||||||
|
|
||||||
|
using cell_descriptor = typename Domain::cell_descriptor;
|
||||||
|
|
||||||
|
#ifdef CGAL_LINKED_WITH_TBB
|
||||||
|
using Triangles = tbb::enumerable_thread_specific<std::vector<std::array<Point_3, 3>>>;
|
||||||
|
#else
|
||||||
|
using Triangles = std::vector<std::array<Point_3, 3> >;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Domain& m_domain;
|
||||||
|
const FT m_isovalue;
|
||||||
|
const bool m_isovalue_nudging;
|
||||||
|
|
||||||
|
Triangles m_triangles;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// creates a Marching Cubes functor for a domain and isovalue
|
||||||
|
Marching_cubes_3(const Domain& domain,
|
||||||
|
const FT isovalue,
|
||||||
|
const bool isovalue_nudging = true)
|
||||||
|
: m_domain(domain),
|
||||||
|
m_isovalue(isovalue),
|
||||||
|
m_isovalue_nudging(isovalue_nudging)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
// returns the created triangle list
|
||||||
|
Triangles& triangles()
|
||||||
|
{
|
||||||
|
return m_triangles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// computes one cell
|
||||||
|
void operator()(const cell_descriptor& cell)
|
||||||
|
{
|
||||||
|
CGAL_precondition(m_domain.cell_vertices(cell).size() == 8);
|
||||||
|
CGAL_precondition(m_domain.cell_edges(cell).size() == 12);
|
||||||
|
|
||||||
|
// @speed for SDFs, we could query at the center of the voxel an early exit
|
||||||
|
|
||||||
|
constexpr std::size_t vpc = Domain::VERTICES_PER_CELL;
|
||||||
|
|
||||||
|
std::array<FT, vpc> values;
|
||||||
|
std::array<Point_3, vpc> corners;
|
||||||
|
const std::size_t i_case = get_cell_corners(m_domain, cell, m_isovalue, corners, values, m_isovalue_nudging);
|
||||||
|
|
||||||
|
// skip empty / full cells
|
||||||
|
constexpr std::size_t ones = (1 << vpc) - 1;
|
||||||
|
if((i_case & ones) == ones || // all bits set
|
||||||
|
(i_case & ones) == 0) // no bits set
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::array<Point_3, 12> vertices;
|
||||||
|
MC_construct_vertices(cell, i_case, corners, values, m_isovalue, m_domain, vertices);
|
||||||
|
|
||||||
|
MC_construct_triangles(i_case, vertices, m_triangles);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace Isosurfacing
|
||||||
|
} // namespace CGAL
|
||||||
|
|
||||||
|
#endif // CGAL_ISOSURFACING_3_INTERNAL_MARCHING_CUBES_FUNCTORS_H
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
// Copyright (c) 2022-2024 INRIA Sophia-Antipolis (France), GeometryFactory (France).
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of CGAL (www.cgal.org).
|
||||||
|
//
|
||||||
|
// $URL$
|
||||||
|
// $Id$
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
|
||||||
|
//
|
||||||
|
// Author(s) : Mael Rouxel-Labbé
|
||||||
|
|
||||||
|
#ifndef CGAL_ISOSURFACING_3_INTERNAL_PARTITION_TRAITS_H
|
||||||
|
#define CGAL_ISOSURFACING_3_INTERNAL_PARTITION_TRAITS_H
|
||||||
|
|
||||||
|
#include <CGAL/license/Isosurfacing_3.h>
|
||||||
|
|
||||||
|
namespace CGAL {
|
||||||
|
namespace Isosurfacing {
|
||||||
|
|
||||||
|
template <typename Partition>
|
||||||
|
struct partition_traits;
|
||||||
|
|
||||||
|
} // namespace Isosurfacing
|
||||||
|
} // namespace CGAL
|
||||||
|
|
||||||
|
#endif // CGAL_ISOSURFACING_3_INTERNAL_PARTITION_TRAITS_H
|
||||||
|
|
@ -0,0 +1,302 @@
|
||||||
|
// Copyright (c) 2022-2024 INRIA Sophia-Antipolis (France), GeometryFactory (France).
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of CGAL (www.cgal.org).
|
||||||
|
//
|
||||||
|
// $URL$
|
||||||
|
// $Id$
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
|
||||||
|
//
|
||||||
|
// Author(s) : Julian Stahl
|
||||||
|
// Mael Rouxel-Labbé
|
||||||
|
|
||||||
|
#ifndef CGAL_ISOSURFACING_3_INTERNAL_PARTITION_TRAITS_CARTESIAN_GRID_3_H
|
||||||
|
#define CGAL_ISOSURFACING_3_INTERNAL_PARTITION_TRAITS_CARTESIAN_GRID_3_H
|
||||||
|
|
||||||
|
#include <CGAL/license/Isosurfacing_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/Isosurfacing_3/Cartesian_grid_3.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/internal/Cell_type.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/internal/tables.h>
|
||||||
|
|
||||||
|
#include <CGAL/tags.h>
|
||||||
|
|
||||||
|
#ifdef CGAL_LINKED_WITH_TBB
|
||||||
|
#include <tbb/parallel_for.h>
|
||||||
|
#include <tbb/blocked_range3d.h>
|
||||||
|
#endif // CGAL_LINKED_WITH_TBB
|
||||||
|
|
||||||
|
#include <boost/functional/hash.hpp>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
namespace CGAL {
|
||||||
|
namespace Isosurfacing {
|
||||||
|
|
||||||
|
template <typename GeomTraits, typename MemoryPolicy>
|
||||||
|
class Cartesian_grid_3;
|
||||||
|
|
||||||
|
template <typename Partition>
|
||||||
|
struct partition_traits;
|
||||||
|
|
||||||
|
struct CG_Edge_descriptor : public std::array<std::size_t, 4> { };
|
||||||
|
struct CG_Cell_descriptor : public std::array<std::size_t, 3> { };
|
||||||
|
|
||||||
|
template <typename GeomTraits, typename MemoryPolicy>
|
||||||
|
struct partition_traits<Cartesian_grid_3<GeomTraits, MemoryPolicy> >
|
||||||
|
{
|
||||||
|
using Grid = Cartesian_grid_3<GeomTraits, MemoryPolicy>;
|
||||||
|
|
||||||
|
// identifies a vertex by its (i, j, k) indices
|
||||||
|
using vertex_descriptor = std::array<std::size_t, 3>;
|
||||||
|
|
||||||
|
// identifies an edge by its starting vertex (i, j, k) and the direction x -> 0, y -> 1, z -> 2
|
||||||
|
using edge_descriptor = CG_Edge_descriptor;
|
||||||
|
|
||||||
|
// identifies a cell by its corner vertex with the smallest (i, j, k) index
|
||||||
|
using cell_descriptor = CG_Cell_descriptor;
|
||||||
|
|
||||||
|
static constexpr Cell_type CELL_TYPE = CUBICAL_CELL;
|
||||||
|
static constexpr std::size_t VERTICES_PER_CELL = 8;
|
||||||
|
static constexpr std::size_t EDGES_PER_CELL = 12;
|
||||||
|
|
||||||
|
using Edge_vertices = std::array<vertex_descriptor, 2>;
|
||||||
|
using Cells_incident_to_edge = std::array<cell_descriptor, 4>;
|
||||||
|
using Cell_vertices = std::array<vertex_descriptor, VERTICES_PER_CELL>;
|
||||||
|
using Cell_edges = std::array<edge_descriptor, EDGES_PER_CELL>;
|
||||||
|
|
||||||
|
static decltype(auto) /*Point_3*/ point(const vertex_descriptor& v,
|
||||||
|
const Grid& g)
|
||||||
|
{
|
||||||
|
return g.point(v[0], v[1], v[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a container with the two vertices incident to edge e
|
||||||
|
static Edge_vertices incident_vertices(const edge_descriptor& e,
|
||||||
|
const Grid&)
|
||||||
|
{
|
||||||
|
Edge_vertices ev;
|
||||||
|
ev[0] = { e[0], e[1], e[2] }; // start vertex
|
||||||
|
ev[1] = { e[0], e[1], e[2] }; // end vertex
|
||||||
|
ev[1][e[3]] += 1; // one position further in the direction of the edge
|
||||||
|
return ev;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a container with all cells incident to edge e
|
||||||
|
static Cells_incident_to_edge incident_cells(const edge_descriptor& e,
|
||||||
|
const Grid&)
|
||||||
|
{
|
||||||
|
// lookup the neighbor cells relative to the edge
|
||||||
|
const int local = internal::Cube_table::edge_store_index[e[3]];
|
||||||
|
auto neighbors = internal::Cube_table::edge_to_voxel_neighbor[local];
|
||||||
|
|
||||||
|
Cells_incident_to_edge cite;
|
||||||
|
for(std::size_t i=0; i<cite.size(); ++i) {
|
||||||
|
for(std::size_t j=0; j<cite[i].size(); ++j)
|
||||||
|
{
|
||||||
|
// offset the relative indices by the edge position
|
||||||
|
cite[i][j] = e[j] + neighbors[i][j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cite;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a container with all vertices of cell c
|
||||||
|
static Cell_vertices cell_vertices(const cell_descriptor& c,
|
||||||
|
const Grid&)
|
||||||
|
{
|
||||||
|
Cell_vertices cv;
|
||||||
|
for(std::size_t i=0; i<cv.size(); ++i) {
|
||||||
|
for(std::size_t j=0; j<c.size(); ++j)
|
||||||
|
{
|
||||||
|
// lookup the relative vertex indices and offset them by the cell position
|
||||||
|
cv[i][j] = c[j] + internal::Cube_table::local_vertex_location[i][j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a container with all edges of cell c
|
||||||
|
static Cell_edges cell_edges(const cell_descriptor& c,
|
||||||
|
const Grid&)
|
||||||
|
{
|
||||||
|
Cell_edges ce;
|
||||||
|
for(std::size_t i=0; i<ce.size(); ++i) {
|
||||||
|
for(std::size_t j=0; j<c.size(); ++j)
|
||||||
|
{
|
||||||
|
// lookup the relative edge indices and offset them by the cell position
|
||||||
|
ce[i][j] = c[j] + internal::Cube_table::global_edge_id[i][j];
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the direction of the edge
|
||||||
|
ce[i][3] = internal::Cube_table::global_edge_id[i][3];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ce;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// iterates sequentially over all vertices v calling f(v) on every one
|
||||||
|
template <typename Functor>
|
||||||
|
static void for_each_vertex(Functor& f,
|
||||||
|
const Grid& g,
|
||||||
|
const CGAL::Sequential_tag)
|
||||||
|
{
|
||||||
|
for(std::size_t i=0; i<g.xdim(); ++i)
|
||||||
|
for(std::size_t j=0; j<g.ydim(); ++j)
|
||||||
|
for(std::size_t k=0; k<g.zdim(); ++k)
|
||||||
|
f({i, j, k});
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterates sequentially over all edges e calling f(e) on every one
|
||||||
|
template <typename Functor>
|
||||||
|
static void for_each_edge(Functor& f,
|
||||||
|
const Grid& g,
|
||||||
|
const CGAL::Sequential_tag)
|
||||||
|
{
|
||||||
|
for(std::size_t i=0; i<g.xdim()-1; ++i) {
|
||||||
|
for(std::size_t j=0; j<g.ydim()-1; ++j) {
|
||||||
|
for(std::size_t k=0; k<g.zdim()-1; ++k)
|
||||||
|
{
|
||||||
|
// all three edges starting at vertex (i, j, k)
|
||||||
|
f({{i, j, k, 0}});
|
||||||
|
f({{i, j, k, 1}});
|
||||||
|
f({{i, j, k, 2}});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterates sequentially over all cells c calling f(c) on every one
|
||||||
|
template <typename Functor>
|
||||||
|
static void for_each_cell(Functor& f,
|
||||||
|
const Grid& g,
|
||||||
|
const CGAL::Sequential_tag)
|
||||||
|
{
|
||||||
|
for(std::size_t i=0; i<g.xdim()-1; ++i)
|
||||||
|
for(std::size_t j=0; j<g.ydim()-1; ++j)
|
||||||
|
for(std::size_t k=0; k<g.zdim()-1; ++k)
|
||||||
|
f({{i, j, k}});
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CGAL_LINKED_WITH_TBB
|
||||||
|
// iterates in parallel over all vertices v calling f(v) on every one
|
||||||
|
template <typename Functor>
|
||||||
|
static void for_each_vertex(Functor& f,
|
||||||
|
const Grid& g,
|
||||||
|
const CGAL::Parallel_tag)
|
||||||
|
{
|
||||||
|
const std::size_t sj = g.ydim();
|
||||||
|
const std::size_t sk = g.zdim();
|
||||||
|
|
||||||
|
// for now only parallelize outer loop
|
||||||
|
auto iterator = [&f, sj, sk](const tbb::blocked_range<std::size_t>& r)
|
||||||
|
{
|
||||||
|
for(std::size_t i=r.begin(); i!=r.end(); ++i)
|
||||||
|
for(std::size_t j=0; j<sj; ++j)
|
||||||
|
for(std::size_t k=0; k<sk; ++k)
|
||||||
|
f({i, j, k});
|
||||||
|
};
|
||||||
|
|
||||||
|
tbb::parallel_for(tbb::blocked_range<std::size_t>(0, g.xdim()), iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterates in parallel over all edges e calling f(e) on every one
|
||||||
|
template <typename Functor>
|
||||||
|
static void for_each_edge(Functor& f,
|
||||||
|
const Grid& g,
|
||||||
|
const CGAL::Parallel_tag)
|
||||||
|
{
|
||||||
|
const std::size_t sj = g.ydim();
|
||||||
|
const std::size_t sk = g.zdim();
|
||||||
|
|
||||||
|
// for now only parallelize outer loop
|
||||||
|
auto iterator = [&f, sj, sk](const tbb::blocked_range<std::size_t>& r)
|
||||||
|
{
|
||||||
|
for(std::size_t i=r.begin(); i != r.end(); ++i) {
|
||||||
|
for(std::size_t j=0; j<sj-1; ++j) {
|
||||||
|
for(std::size_t k=0; k<sk-1; ++k)
|
||||||
|
{
|
||||||
|
f({i, j, k, 0});
|
||||||
|
f({i, j, k, 1});
|
||||||
|
f({i, j, k, 2});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
tbb::parallel_for(tbb::blocked_range<std::size_t>(0, g.xdim() - 1), iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterates in parallel over all cells c calling f(c) on every one
|
||||||
|
template <typename Functor>
|
||||||
|
static void for_each_cell(Functor& f,
|
||||||
|
const Grid& g,
|
||||||
|
const CGAL::Parallel_tag)
|
||||||
|
{
|
||||||
|
// for now only parallelize outer loop
|
||||||
|
auto iterator = [&f](const tbb::blocked_range3d<std::size_t>& r)
|
||||||
|
{
|
||||||
|
const std::size_t i_begin = r.pages().begin();
|
||||||
|
const std::size_t i_end = r.pages().end();
|
||||||
|
const std::size_t j_begin = r.rows().begin();
|
||||||
|
const std::size_t j_end = r.rows().end();
|
||||||
|
const std::size_t k_begin = r.cols().begin();
|
||||||
|
const std::size_t k_end = r.cols().end();
|
||||||
|
|
||||||
|
for(std::size_t i = i_begin; i != i_end; ++i)
|
||||||
|
for(std::size_t j = j_begin; j != j_end; ++j)
|
||||||
|
for(std::size_t k = k_begin; k != k_end; ++k)
|
||||||
|
f({i, j, k});
|
||||||
|
};
|
||||||
|
|
||||||
|
tbb::blocked_range3d<std::size_t> range(0, g.xdim() - 1, 0, g.ydim() - 1, 0, g.zdim() - 1);
|
||||||
|
tbb::parallel_for(range, iterator);
|
||||||
|
}
|
||||||
|
#endif // CGAL_LINKED_WITH_TBB
|
||||||
|
|
||||||
|
template <typename ConcurrencyTag, typename Functor>
|
||||||
|
static void for_each_vertex(Functor& f, const Grid& g) { return for_each_vertex(f, g, ConcurrencyTag{}); }
|
||||||
|
template <typename ConcurrencyTag, typename Functor>
|
||||||
|
static void for_each_edge(Functor& f, const Grid& g) { return for_each_edge(f, g, ConcurrencyTag{}); }
|
||||||
|
template <typename ConcurrencyTag, typename Functor>
|
||||||
|
static void for_each_cell(Functor& f, const Grid& g) { return for_each_cell(f, g, ConcurrencyTag{}); }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Isosurfacing
|
||||||
|
} // namespace CGAL
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct hash<CGAL::Isosurfacing::CG_Edge_descriptor>
|
||||||
|
{
|
||||||
|
std::size_t operator()(const CGAL::Isosurfacing::CG_Edge_descriptor& e) const
|
||||||
|
{
|
||||||
|
std::size_t seed = 0;
|
||||||
|
boost::hash_combine(seed, e[0]);
|
||||||
|
boost::hash_combine(seed, e[1]);
|
||||||
|
boost::hash_combine(seed, e[2]);
|
||||||
|
boost::hash_combine(seed, e[3]);
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct hash<CGAL::Isosurfacing::CG_Cell_descriptor>
|
||||||
|
{
|
||||||
|
std::size_t operator()(const CGAL::Isosurfacing::CG_Cell_descriptor& e) const
|
||||||
|
{
|
||||||
|
std::size_t seed = 0;
|
||||||
|
boost::hash_combine(seed, e[0]);
|
||||||
|
boost::hash_combine(seed, e[1]);
|
||||||
|
boost::hash_combine(seed, e[2]);
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace std
|
||||||
|
|
||||||
|
#endif // CGAL_ISOSURFACING_3_INTERNAL_PARTITION_TRAITS_CARTESIAN_GRID_3_H
|
||||||
|
|
@ -0,0 +1,672 @@
|
||||||
|
// Copyright (c) 2022-2024 INRIA Sophia-Antipolis (France), GeometryFactory (France).
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of CGAL (www.cgal.org).
|
||||||
|
//
|
||||||
|
// $URL$
|
||||||
|
// $Id$
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
|
||||||
|
//
|
||||||
|
// Author(s) : Julian Stahl
|
||||||
|
// Mael Rouxel-Labbé
|
||||||
|
// Sven Oesau
|
||||||
|
|
||||||
|
#ifndef CGAL_ISOSURFACING_3_INTERNAL_PARTITION_TRAITS_OCTREE_H
|
||||||
|
#define CGAL_ISOSURFACING_3_INTERNAL_PARTITION_TRAITS_OCTREE_H
|
||||||
|
|
||||||
|
#include <CGAL/license/Isosurfacing_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/Octree.h>
|
||||||
|
#include <CGAL/Orthtree/Traversals.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/internal/Cell_type.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/internal/tables.h>
|
||||||
|
|
||||||
|
#include <CGAL/tags.h>
|
||||||
|
#include <boost/functional/hash.hpp>
|
||||||
|
#include <CGAL/Simple_cartesian.h>
|
||||||
|
|
||||||
|
#ifdef CGAL_LINKED_WITH_TBB
|
||||||
|
#include <tbb/parallel_for.h>
|
||||||
|
#endif // CGAL_LINKED_WITH_TBB
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
namespace CGAL {
|
||||||
|
namespace Isosurfacing {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// this is to be able to specialize std::hash
|
||||||
|
struct OW_Edge_handle : public std::tuple<std::size_t, std::size_t>
|
||||||
|
{
|
||||||
|
using std::tuple<std::size_t, std::size_t>::tuple; // inherit constructors
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace Isosurfacing
|
||||||
|
} // namespace CGAL
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct hash<CGAL::Isosurfacing::internal::OW_Edge_handle>
|
||||||
|
{
|
||||||
|
std::size_t operator()(const CGAL::Isosurfacing::internal::OW_Edge_handle& e) const
|
||||||
|
{
|
||||||
|
std::size_t seed = 0;
|
||||||
|
boost::hash_combine(seed, std::get<0>(e));
|
||||||
|
boost::hash_combine(seed, std::get<1>(e));
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace std
|
||||||
|
|
||||||
|
namespace CGAL {
|
||||||
|
namespace Isosurfacing {
|
||||||
|
|
||||||
|
template <typename Partition>
|
||||||
|
struct partition_traits;
|
||||||
|
|
||||||
|
template<typename GeomTraits>
|
||||||
|
struct partition_traits<CGAL::Octree<GeomTraits, std::vector<typename GeomTraits::Point_3> > >
|
||||||
|
{
|
||||||
|
using Orthtree = CGAL::Octree<GeomTraits, std::vector<typename GeomTraits::Point_3> >;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using Geom_traits = GeomTraits;
|
||||||
|
using FT = typename Geom_traits::FT;
|
||||||
|
using Point_3 = typename Geom_traits::Point_3;
|
||||||
|
using Vector_3 = typename Geom_traits::Vector_3;
|
||||||
|
|
||||||
|
using Node_index = typename Orthtree::Node_index;
|
||||||
|
using Uniform_coords = typename Orthtree::Global_coordinates; // coordinates on max depth level
|
||||||
|
|
||||||
|
using vertex_descriptor = std::size_t;
|
||||||
|
using edge_descriptor = internal::OW_Edge_handle;
|
||||||
|
using cell_descriptor = std::size_t;
|
||||||
|
|
||||||
|
static constexpr Cell_type CELL_TYPE = CUBICAL_CELL;
|
||||||
|
static constexpr std::size_t VERTICES_PER_CELL = 8;
|
||||||
|
static constexpr std::size_t EDGES_PER_CELL = 12;
|
||||||
|
|
||||||
|
using Edge_vertices = std::array<vertex_descriptor, 2>;
|
||||||
|
using Cells_incident_to_edge = std::vector<cell_descriptor>;
|
||||||
|
using Cell_vertices = std::array<vertex_descriptor, 8>;
|
||||||
|
using Cell_edges = std::array<edge_descriptor, 12>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static std::set<edge_descriptor> get_leaf_edges(const Orthtree& o)
|
||||||
|
{
|
||||||
|
std::set<edge_descriptor> leaf_edge_set;
|
||||||
|
std::size_t dim = std::size_t(1) << o.depth();
|
||||||
|
for (Node_index node_index : o.traverse(CGAL::Orthtrees::Leaves_traversal<Orthtree>(o)))
|
||||||
|
{
|
||||||
|
const Uniform_coords& coords_uniform = uniform_coordinates(node_index, o);
|
||||||
|
|
||||||
|
// write all leaf edges in a set
|
||||||
|
const Uniform_coords& coords_global = o.global_coordinates(node_index);
|
||||||
|
const std::size_t depth = o.depth(node_index);
|
||||||
|
const std::size_t df = std::size_t(1) << (o.depth() - depth);
|
||||||
|
for (const auto& edge_voxels : internal::Cube_table::edge_to_voxel_neighbor)
|
||||||
|
{
|
||||||
|
bool are_all_voxels_leafs = true;
|
||||||
|
for (const auto& node_ijk : edge_voxels)
|
||||||
|
{
|
||||||
|
const std::size_t x = coords_uniform[0] + df * node_ijk[0];
|
||||||
|
const std::size_t y = coords_uniform[1] + df * node_ijk[1];
|
||||||
|
const std::size_t z = coords_uniform[2] + df * node_ijk[2];
|
||||||
|
// check for overflow / ignore edges on boundary
|
||||||
|
if (x >= dim || y >= dim || z >= dim)
|
||||||
|
{
|
||||||
|
are_all_voxels_leafs = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Node_index n = get_node(x, y, z, o);
|
||||||
|
if (o.depth(n) > depth)
|
||||||
|
{
|
||||||
|
are_all_voxels_leafs = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (are_all_voxels_leafs)
|
||||||
|
{
|
||||||
|
// add to leaf edge set
|
||||||
|
std::size_t e_gl = e_glIndex(edge_voxels[0][3],
|
||||||
|
coords_global[0], coords_global[1], coords_global[2],
|
||||||
|
depth);
|
||||||
|
leaf_edge_set.insert({ e_gl, depth });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return leaf_edge_set;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::set<vertex_descriptor> get_leaf_vertices(const Orthtree& o)
|
||||||
|
{
|
||||||
|
std::set<vertex_descriptor> leaf_vertex_set;
|
||||||
|
for (Node_index node_index : o.traverse(CGAL::Orthtrees::Leaves_traversal<Orthtree>(o)))
|
||||||
|
{
|
||||||
|
const Uniform_coords& coords_uniform = uniform_coordinates(node_index, o);
|
||||||
|
|
||||||
|
// write all vertices edges in a set
|
||||||
|
const Uniform_coords& coords_global = o.global_coordinates(node_index);
|
||||||
|
const std::size_t depth = o.depth(node_index);
|
||||||
|
const std::size_t df = std::size_t(1) << (o.depth() - depth);
|
||||||
|
|
||||||
|
for (int i = 0; i < internal::Cube_table::N_VERTICES; ++i)
|
||||||
|
{
|
||||||
|
Uniform_coords v_coords = coords_global;
|
||||||
|
typename Orthtree::Local_coordinates local_coords(i);
|
||||||
|
|
||||||
|
for (int j = 0; j < 3; ++j)
|
||||||
|
v_coords[j] += std::size_t(local_coords[j]);
|
||||||
|
|
||||||
|
for (int j = 0; j < 3; ++j)
|
||||||
|
v_coords[j] *= static_cast<uint32_t>(df);
|
||||||
|
|
||||||
|
const std::size_t lex = lex_index(v_coords[0], v_coords[1], v_coords[2], o.depth());
|
||||||
|
leaf_vertex_set.insert(lex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return leaf_vertex_set;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void get_leaves(const Orthtree& o,
|
||||||
|
std::vector<cell_descriptor>& cells,
|
||||||
|
std::vector<edge_descriptor>& edges,
|
||||||
|
std::vector<vertex_descriptor>& vertices)
|
||||||
|
{
|
||||||
|
std::set<edge_descriptor> leaf_edge_set;
|
||||||
|
std::set<vertex_descriptor> leaf_vertex_set;
|
||||||
|
std::size_t dim = std::size_t(1) << o.depth();
|
||||||
|
cells.clear();
|
||||||
|
for (Node_index node_index : o.traverse(CGAL::Orthtrees::Leaves_traversal<Orthtree>(o)))
|
||||||
|
{
|
||||||
|
const Uniform_coords& coords_uniform = uniform_coordinates(node_index, o);
|
||||||
|
cells.push_back(node_index);
|
||||||
|
|
||||||
|
// write all leaf edges in a set
|
||||||
|
const Uniform_coords& coords_global = o.global_coordinates(node_index);
|
||||||
|
const std::size_t depth = o.depth(node_index);
|
||||||
|
const std::size_t df = std::size_t(1) << (o.depth() - depth);
|
||||||
|
|
||||||
|
for (int i = 0; i < internal::Cube_table::N_VERTICES; ++i)
|
||||||
|
{
|
||||||
|
Uniform_coords v_coords = coords_global;
|
||||||
|
typename Orthtree::Local_coordinates local_coords(i);
|
||||||
|
|
||||||
|
for (int j = 0; j < 3; ++j)
|
||||||
|
v_coords[j] += std::size_t(local_coords[j]);
|
||||||
|
|
||||||
|
for (int j = 0; j < 3; ++j)
|
||||||
|
v_coords[j] *= static_cast<uint32_t>(df);
|
||||||
|
|
||||||
|
const std::size_t lex = lex_index(v_coords[0], v_coords[1], v_coords[2], o.depth());
|
||||||
|
leaf_vertex_set.insert(lex);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& edge_voxels : internal::Cube_table::edge_to_voxel_neighbor)
|
||||||
|
{
|
||||||
|
bool are_all_voxels_leafs = true;
|
||||||
|
for (const auto& node_ijk : edge_voxels)
|
||||||
|
{
|
||||||
|
const std::size_t x = coords_uniform[0] + df * node_ijk[0];
|
||||||
|
const std::size_t y = coords_uniform[1] + df * node_ijk[1];
|
||||||
|
const std::size_t z = coords_uniform[2] + df * node_ijk[2];
|
||||||
|
// check for overflow / ignore edges on boundary
|
||||||
|
if (x >= dim || y >= dim || z >= dim)
|
||||||
|
{
|
||||||
|
are_all_voxels_leafs = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Node_index n = get_node(x, y, z, o);
|
||||||
|
if (o.depth(n) > depth)
|
||||||
|
{
|
||||||
|
are_all_voxels_leafs = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (are_all_voxels_leafs)
|
||||||
|
{
|
||||||
|
// add to leaf edge set
|
||||||
|
std::size_t e_gl = e_glIndex(edge_voxels[0][3],
|
||||||
|
coords_global[0], coords_global[1], coords_global[2],
|
||||||
|
depth);
|
||||||
|
leaf_edge_set.insert({ e_gl, depth });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
edges.clear();
|
||||||
|
vertices.clear();
|
||||||
|
|
||||||
|
std::copy(leaf_edge_set.begin(), leaf_edge_set.end(), std::back_inserter(edges));
|
||||||
|
std::copy(leaf_vertex_set.begin(), leaf_vertex_set.end(), std::back_inserter(vertices));
|
||||||
|
}
|
||||||
|
|
||||||
|
static Point_3 point(const vertex_descriptor v,
|
||||||
|
const Orthtree& o)
|
||||||
|
{
|
||||||
|
std::size_t dim_ = std::size_t(1) << o.depth();
|
||||||
|
const auto bbox = o.bbox(0);
|
||||||
|
FT hx_ = (bbox.xmax() - bbox.xmin()) / dim_;
|
||||||
|
std::size_t i, j, k;
|
||||||
|
std::tie(i, j, k) = ijk_index(v, o.depth());
|
||||||
|
|
||||||
|
const FT x0 = bbox.xmin() + i * hx_;
|
||||||
|
const FT y0 = bbox.ymin() + j * hx_;
|
||||||
|
const FT z0 = bbox.zmin() + k * hx_;
|
||||||
|
|
||||||
|
return { x0, y0, z0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Edge_vertices incident_vertices(const edge_descriptor& e,
|
||||||
|
const Orthtree& o)
|
||||||
|
{
|
||||||
|
return edge_vertices(e, o);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Cells_incident_to_edge incident_cells(const edge_descriptor& e_id,
|
||||||
|
const Orthtree& o)
|
||||||
|
{
|
||||||
|
namespace Tables = internal::Cube_table;
|
||||||
|
|
||||||
|
std::size_t e_global_id, depth;
|
||||||
|
std::tie(e_global_id, depth) = static_cast<const std::tuple<std::size_t, std::size_t>&>(e_id);
|
||||||
|
const std::size_t e_local_index = Tables::edge_store_index[e_global_id % 3];
|
||||||
|
|
||||||
|
const std::size_t max_depth = o.depth();
|
||||||
|
const std::size_t df = std::size_t(1) << (max_depth - depth);
|
||||||
|
|
||||||
|
const size_t v0_lex_index = e_global_id / 3;
|
||||||
|
std::size_t i, j, k;
|
||||||
|
std::tie(i, j, k) = ijk_index(v0_lex_index, depth);
|
||||||
|
i *= df;
|
||||||
|
j *= df;
|
||||||
|
k *= df;
|
||||||
|
|
||||||
|
const auto& voxel_neighbors = Tables::edge_to_voxel_neighbor[e_local_index];
|
||||||
|
Node_index n0 = get_node(i + voxel_neighbors[0][0], j + voxel_neighbors[0][1], k + voxel_neighbors[0][2], o);
|
||||||
|
Node_index n1 = get_node(i + voxel_neighbors[1][0], j + voxel_neighbors[1][1], k + voxel_neighbors[1][2], o);
|
||||||
|
Node_index n2 = get_node(i + voxel_neighbors[2][0], j + voxel_neighbors[2][1], k + voxel_neighbors[2][2], o);
|
||||||
|
Node_index n3 = get_node(i + voxel_neighbors[3][0], j + voxel_neighbors[3][1], k + voxel_neighbors[3][2], o);
|
||||||
|
|
||||||
|
const Uniform_coords n0_uniform_coords = uniform_coordinates(n0, o);
|
||||||
|
const Uniform_coords n1_uniform_coords = uniform_coordinates(n1, o);
|
||||||
|
const Uniform_coords n2_uniform_coords = uniform_coordinates(n2, o);
|
||||||
|
const Uniform_coords n3_uniform_coords = uniform_coordinates(n3, o);
|
||||||
|
|
||||||
|
std::size_t n0_lex = lex_index(n0_uniform_coords[0], n0_uniform_coords[1], n0_uniform_coords[2], max_depth);
|
||||||
|
std::size_t n1_lex = lex_index(n1_uniform_coords[0], n1_uniform_coords[1], n1_uniform_coords[2], max_depth);
|
||||||
|
std::size_t n2_lex = lex_index(n2_uniform_coords[0], n2_uniform_coords[1], n2_uniform_coords[2], max_depth);
|
||||||
|
std::size_t n3_lex = lex_index(n3_uniform_coords[0], n3_uniform_coords[1], n3_uniform_coords[2], max_depth);
|
||||||
|
|
||||||
|
Cells_incident_to_edge ics = { n0_lex, n1_lex, n2_lex, n3_lex };
|
||||||
|
|
||||||
|
// With an adaptative octree, there is no reason that all cells around an interesting edge
|
||||||
|
// have the same level. If there is such a jump, we have a duplicate to filter.
|
||||||
|
if (ics.front() == ics.back()) {
|
||||||
|
ics.pop_back(); // Remove the duplicate if the first and last are the same.
|
||||||
|
} else {
|
||||||
|
ics.erase(std::unique(ics.begin(), ics.end()), ics.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
CGAL_postcondition(ics.size() >= 3);
|
||||||
|
CGAL_postcondition(std::set<std::size_t>(ics.begin(), ics.end()).size() == ics.size());
|
||||||
|
|
||||||
|
return ics;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::size_t depth(const cell_descriptor c,
|
||||||
|
const Orthtree& o)
|
||||||
|
{
|
||||||
|
return o.depth(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Cell_vertices cell_vertices(const cell_descriptor c,
|
||||||
|
const Orthtree& o)
|
||||||
|
{
|
||||||
|
std::size_t i, j, k;
|
||||||
|
std::tie(i, j, k) = ijk_index(c, o.depth());
|
||||||
|
Node_index node_index = get_node(i, j, k, o);
|
||||||
|
const std::size_t df = std::size_t(1) << (o.depth() - o.depth(node_index));
|
||||||
|
|
||||||
|
std::array<vertex_descriptor, 8> v;
|
||||||
|
for (int v_id = 0; v_id < internal::Cube_table::N_VERTICES; ++v_id)
|
||||||
|
{
|
||||||
|
const int* l = internal::Cube_table::local_vertex_location[v_id];
|
||||||
|
const std::size_t lex = lex_index(i + df * l[0], j + df * l[1], k + df * l[2], o.depth());
|
||||||
|
v[v_id] = lex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Cell_edges cell_edges(const cell_descriptor c,
|
||||||
|
const Orthtree& o)
|
||||||
|
{
|
||||||
|
std::size_t i, j, k;
|
||||||
|
std::tie(i, j, k) = ijk_index(c, o.depth());
|
||||||
|
Node_index node_index = get_node(i, j, k, o);
|
||||||
|
|
||||||
|
const Uniform_coords& coords_global = o.global_coordinates(node_index);
|
||||||
|
const std::size_t depth = o.depth(node_index);
|
||||||
|
|
||||||
|
std::array<edge_descriptor, internal::Cube_table::N_EDGES> edges;
|
||||||
|
for (std::size_t e_id = 0; e_id < edges.size(); ++e_id)
|
||||||
|
{
|
||||||
|
const std::size_t e_gl = e_glIndex(e_id, coords_global[0], coords_global[1], coords_global[2], depth);
|
||||||
|
edges[e_id] = { e_gl, depth };
|
||||||
|
}
|
||||||
|
|
||||||
|
return edges;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Functor>
|
||||||
|
static void for_each_vertex(Functor& f,
|
||||||
|
const Orthtree& o,
|
||||||
|
const CGAL::Sequential_tag)
|
||||||
|
{
|
||||||
|
for(const vertex_descriptor v : get_leaf_vertices(o))
|
||||||
|
f(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Functor>
|
||||||
|
static void for_each_vertex(Functor& f,
|
||||||
|
std::vector<vertex_descriptor>& vertices,
|
||||||
|
const Orthtree&,
|
||||||
|
const CGAL::Sequential_tag)
|
||||||
|
{
|
||||||
|
for (const vertex_descriptor v : vertices)
|
||||||
|
f(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Functor>
|
||||||
|
static void for_each_edge(Functor& f,
|
||||||
|
const Orthtree& o,
|
||||||
|
Sequential_tag)
|
||||||
|
{
|
||||||
|
for(const edge_descriptor& e : get_leaf_edges(o))
|
||||||
|
f(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Functor>
|
||||||
|
static void for_each_edge(Functor& f,
|
||||||
|
std::vector<edge_descriptor>& edges,
|
||||||
|
const Orthtree& o,
|
||||||
|
Sequential_tag)
|
||||||
|
{
|
||||||
|
if (edges.empty()) {
|
||||||
|
std::set<edge_descriptor> edge_set = get_leaf_edges(o);
|
||||||
|
edges.assign(edge_set.begin(), edge_set.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const edge_descriptor& e : edges)
|
||||||
|
f(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Functor>
|
||||||
|
static void for_each_cell(Functor& f,
|
||||||
|
const Orthtree& o,
|
||||||
|
CGAL::Sequential_tag)
|
||||||
|
{
|
||||||
|
for (const cell_descriptor c : o.traverse(CGAL::Orthtrees::Leaves_traversal<Orthtree>(o))) {
|
||||||
|
const Uniform_coords uc = uniform_coordinates(c, o);
|
||||||
|
f(lex_index(uc[0], uc[1], uc[2], o.depth()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Functor>
|
||||||
|
static void for_each_cell(Functor& f,
|
||||||
|
std::vector<cell_descriptor>& cells,
|
||||||
|
const Orthtree& o,
|
||||||
|
CGAL::Sequential_tag)
|
||||||
|
{
|
||||||
|
if (cells.empty()) {
|
||||||
|
auto cell_range = o.traverse(CGAL::Orthtrees::Leaves_traversal<Orthtree>(o));
|
||||||
|
cells.assign(cell_range.begin(), cell_range.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const cell_descriptor c : cells) {
|
||||||
|
const Uniform_coords uc = uniform_coordinates(c, o);
|
||||||
|
f(lex_index(uc[0], uc[1], uc[2], o.depth()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CGAL_LINKED_WITH_TBB
|
||||||
|
template <typename Functor>
|
||||||
|
static void for_each_vertex(Functor& f,
|
||||||
|
const Orthtree& o,
|
||||||
|
Parallel_tag)
|
||||||
|
{
|
||||||
|
auto edges = get_leaf_edges(o);
|
||||||
|
|
||||||
|
tbb::parallel_for_each(edges.begin(), edges.end(), f);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Functor>
|
||||||
|
static void for_each_vertex(Functor& f,
|
||||||
|
std::vector<vertex_descriptor>& vertices,
|
||||||
|
const Orthtree&,
|
||||||
|
Parallel_tag)
|
||||||
|
{
|
||||||
|
auto iterator = [&](const tbb::blocked_range<std::size_t>& r)
|
||||||
|
{
|
||||||
|
for(std::size_t i = r.begin(); i != r.end(); ++i)
|
||||||
|
f(vertices[i]);
|
||||||
|
};
|
||||||
|
|
||||||
|
tbb::parallel_for(tbb::blocked_range<std::size_t>(0, vertices.size()), iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Functor>
|
||||||
|
static void for_each_edge(Functor& f,
|
||||||
|
const Orthtree& o,
|
||||||
|
Parallel_tag)
|
||||||
|
{
|
||||||
|
std::set<edge_descriptor> edges = get_leaf_edges(o);
|
||||||
|
|
||||||
|
tbb::parallel_for_each(edges.begin(), edges.end(), f);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Functor>
|
||||||
|
static void for_each_edge(Functor& f,
|
||||||
|
std::vector<edge_descriptor>& edges,
|
||||||
|
const Orthtree& o,
|
||||||
|
Parallel_tag)
|
||||||
|
{
|
||||||
|
if (edges.empty()) {
|
||||||
|
std::set<edge_descriptor> edge_set = get_leaf_edges(o);
|
||||||
|
edges.assign(edge_set.begin(), edge_set.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto iterator = [&](const tbb::blocked_range<std::size_t>& r)
|
||||||
|
{
|
||||||
|
for (std::size_t i = r.begin(); i != r.end(); ++i)
|
||||||
|
f(edges[i]);
|
||||||
|
};
|
||||||
|
|
||||||
|
tbb::parallel_for(tbb::blocked_range<std::size_t>(0, edges.size()), iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Functor>
|
||||||
|
static void for_each_cell(Functor& f,
|
||||||
|
const Orthtree& o,
|
||||||
|
Parallel_tag)
|
||||||
|
{
|
||||||
|
auto func = [&](const typename Orthtree::Node_index& n)
|
||||||
|
{
|
||||||
|
const Uniform_coords& uc = uniform_coordinates(n, o);
|
||||||
|
f(lex_index(uc[0], uc[1], uc[2], o.depth()));
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto& cells = o.traverse(CGAL::Orthtrees::Leaves_traversal<Orthtree>(o));
|
||||||
|
tbb::parallel_for_each(cells.begin(), cells.end(), func);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Functor>
|
||||||
|
static void for_each_cell(Functor& f,
|
||||||
|
std::vector<cell_descriptor>& cells,
|
||||||
|
const Orthtree& o,
|
||||||
|
Parallel_tag)
|
||||||
|
{
|
||||||
|
if (cells.empty()) {
|
||||||
|
auto cell_range = o.traverse(CGAL::Orthtrees::Leaves_traversal<Orthtree>(o));
|
||||||
|
cells.assign(cell_range.begin(), cell_range.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto iterator = [&](const tbb::blocked_range<std::size_t>& r)
|
||||||
|
{
|
||||||
|
for (std::size_t i = r.begin(); i != r.end(); ++i) {
|
||||||
|
const Uniform_coords& uc = uniform_coordinates(cells[i], o);
|
||||||
|
f(lex_index(uc[0], uc[1], uc[2], o.depth()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
tbb::parallel_for(tbb::blocked_range<std::size_t>(0, cells.size()), iterator);
|
||||||
|
}
|
||||||
|
#endif // CGAL_LINKED_WITH_TBB
|
||||||
|
|
||||||
|
template <typename ConcurrencyTag, typename Functor>
|
||||||
|
static void for_each_vertex(Functor& f, const Orthtree& o)
|
||||||
|
{ return for_each_vertex(f, o, ConcurrencyTag{}); }
|
||||||
|
|
||||||
|
template <typename ConcurrencyTag, typename Functor>
|
||||||
|
static void for_each_edge(Functor& f, std::vector<edge_descriptor>& edges, const Orthtree& o)
|
||||||
|
{ return for_each_edge(f, edges, o, ConcurrencyTag{}); }
|
||||||
|
template <typename ConcurrencyTag, typename Functor>
|
||||||
|
static void for_each_edge(Functor& f, const Orthtree& o)
|
||||||
|
{ return for_each_edge(f, o, ConcurrencyTag{}); }
|
||||||
|
|
||||||
|
template <typename ConcurrencyTag, typename Functor>
|
||||||
|
static void for_each_cell(Functor& f, std::vector<cell_descriptor>& cells, const Orthtree& o)
|
||||||
|
{ return for_each_cell(f, cells, o, ConcurrencyTag{}); }
|
||||||
|
template <typename ConcurrencyTag, typename Functor>
|
||||||
|
static void for_each_cell(Functor& f, const Orthtree& o)
|
||||||
|
{ return for_each_cell(f, o, ConcurrencyTag{}); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
static Uniform_coords uniform_coordinates(Node_index node_index,
|
||||||
|
const Orthtree& o)
|
||||||
|
{
|
||||||
|
Uniform_coords coords = o.global_coordinates(node_index);
|
||||||
|
const std::size_t df = std::size_t(1) << (o.depth() - o.depth(node_index));
|
||||||
|
for (int i = 0; i < 3; ++i)
|
||||||
|
coords[i] *= static_cast<uint32_t>(df);
|
||||||
|
|
||||||
|
return coords;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::tuple<std::size_t, std::size_t, std::size_t> ijk_index(const std::size_t lex_index,
|
||||||
|
const std::size_t depth)
|
||||||
|
{
|
||||||
|
const std::size_t dim = (std::size_t(1) << depth) + 1;
|
||||||
|
return std::make_tuple(lex_index % dim, (lex_index / dim) % dim, lex_index / (dim * dim));
|
||||||
|
}
|
||||||
|
|
||||||
|
// computes unique edge global index.
|
||||||
|
// \param e local edge index
|
||||||
|
// \param i_idx i-index of cell
|
||||||
|
// \param j_idx j-index of cell
|
||||||
|
// \param k_idx k-index of cell
|
||||||
|
// \param depth of cell
|
||||||
|
static std::size_t e_glIndex(const std::size_t e,
|
||||||
|
const std::size_t i_idx,
|
||||||
|
const std::size_t j_idx,
|
||||||
|
const std::size_t k_idx,
|
||||||
|
const std::size_t depth)
|
||||||
|
{
|
||||||
|
const unsigned long long gei_pattern_ = 670526590282893600ull;
|
||||||
|
const size_t i = i_idx + (size_t)((gei_pattern_ >> 5 * e) & 1); // global_edge_id[eg][0];
|
||||||
|
const size_t j = j_idx + (size_t)((gei_pattern_ >> (5 * e + 1)) & 1); // global_edge_id[eg][1];
|
||||||
|
const size_t k = k_idx + (size_t)((gei_pattern_ >> (5 * e + 2)) & 1); // global_edge_id[eg][2];
|
||||||
|
const size_t offs = (size_t)((gei_pattern_ >> (5 * e + 3)) & 3);
|
||||||
|
|
||||||
|
return (3 * lex_index(i, j, k, depth) + offs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::size_t lex_index(const std::size_t i,
|
||||||
|
const std::size_t j,
|
||||||
|
const std::size_t k,
|
||||||
|
const std::size_t depth)
|
||||||
|
{
|
||||||
|
std::size_t dim = (std::size_t(1) << depth) + 1;
|
||||||
|
return k * dim * dim + j * dim + i;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::array<vertex_descriptor, 2> edge_vertices(const edge_descriptor& e_id,
|
||||||
|
const Orthtree& o)
|
||||||
|
{
|
||||||
|
namespace Tables = internal::Cube_table;
|
||||||
|
|
||||||
|
std::size_t e_global_id, depth, max_depth = o.depth();
|
||||||
|
std::tie(e_global_id, depth) = static_cast<const std::tuple<std::size_t, std::size_t>&>(e_id);
|
||||||
|
const std::size_t df = std::size_t(1) << (max_depth - depth);
|
||||||
|
|
||||||
|
const size_t v0_lex_index = e_global_id / 3;
|
||||||
|
std::size_t i0, j0, k0;
|
||||||
|
std::tie(i0, j0, k0) = ijk_index(v0_lex_index, depth);
|
||||||
|
|
||||||
|
// v1
|
||||||
|
const std::size_t e_local_index = Tables::edge_store_index[e_global_id % 3];
|
||||||
|
const int* v1_local = Tables::local_vertex_location[Tables::edge_to_vertex[e_local_index][1]];
|
||||||
|
|
||||||
|
const std::size_t i1 = i0 + v1_local[0];
|
||||||
|
const std::size_t j1 = j0 + v1_local[1];
|
||||||
|
const std::size_t k1 = k0 + v1_local[2];
|
||||||
|
|
||||||
|
const std::size_t v0 = lex_index(df * i0, df * j0, df * k0, max_depth);
|
||||||
|
const std::size_t v1 = lex_index(df * i1, df * j1, df * k1, max_depth);
|
||||||
|
|
||||||
|
return { v0, v1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
static Node_index get_node(const std::size_t i,
|
||||||
|
const std::size_t j,
|
||||||
|
const std::size_t k,
|
||||||
|
const Orthtree& o)
|
||||||
|
{
|
||||||
|
Node_index node_index = o.root();
|
||||||
|
const std::size_t x = i;
|
||||||
|
const std::size_t y = j;
|
||||||
|
const std::size_t z = k;
|
||||||
|
|
||||||
|
while (!o.is_leaf(node_index))
|
||||||
|
{
|
||||||
|
std::size_t dist_to_max = o.depth() - o.depth(node_index) - 1;
|
||||||
|
typename Orthtree::Local_coordinates loc;
|
||||||
|
if (x & (std::size_t(1) << dist_to_max))
|
||||||
|
loc[0] = true;
|
||||||
|
|
||||||
|
if (y & (std::size_t(1) << dist_to_max))
|
||||||
|
loc[1] = true;
|
||||||
|
|
||||||
|
if (z & (std::size_t(1) << dist_to_max))
|
||||||
|
loc[2] = true;
|
||||||
|
|
||||||
|
node_index = o.child(node_index, loc.to_ulong());
|
||||||
|
}
|
||||||
|
|
||||||
|
return node_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Node_index get_node(const std::size_t lex_index,
|
||||||
|
const Orthtree& o)
|
||||||
|
{
|
||||||
|
std::size_t i, j, k;
|
||||||
|
std::tie(i, j, k) = ijk_index(lex_index, o.depth());
|
||||||
|
return get_node(i, j, k, o);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Isosurfacing
|
||||||
|
} // namespace CGAL
|
||||||
|
|
||||||
|
#endif // CGAL_ISOSURFACING_3_INTERNAL_PARTITION_TRAITS_OCTREE_H
|
||||||
|
|
@ -0,0 +1,703 @@
|
||||||
|
// Copyright (c) 2020 INRIA Sophia-Antipolis (France).
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of CGAL (www.cgal.org).
|
||||||
|
//
|
||||||
|
// $URL$
|
||||||
|
// $Id$
|
||||||
|
// SPDX-License-Identifier: ( GPL-3.0-or-later OR LicenseRef-Commercial ) AND MIT
|
||||||
|
//
|
||||||
|
// Author(s) : Julian Stahl
|
||||||
|
//
|
||||||
|
// This file incorporates work covered by the following copyright and permission notice:
|
||||||
|
//
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2020 Roberto Grosso
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// The code below uses the version of
|
||||||
|
// https://github.com/rogrosso/tmc available on 15th of September 2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef CGAL_ISOSURFACING_3_INTERNAL_TABLES_H
|
||||||
|
#define CGAL_ISOSURFACING_3_INTERNAL_TABLES_H
|
||||||
|
|
||||||
|
#include <CGAL/license/Isosurfacing_3.h>
|
||||||
|
|
||||||
|
namespace CGAL {
|
||||||
|
namespace Isosurfacing {
|
||||||
|
namespace internal {
|
||||||
|
namespace Cube_table {
|
||||||
|
/*
|
||||||
|
* Naming convention from "A parallel dual marching cubes approach
|
||||||
|
* to quad only surface reconstruction - Grosso & Zint"
|
||||||
|
*
|
||||||
|
* ^ y
|
||||||
|
* |
|
||||||
|
* v2------e2------v3
|
||||||
|
* /| /|
|
||||||
|
* e11| e10|
|
||||||
|
* / e3 / e1
|
||||||
|
* v6------e6------v7 |
|
||||||
|
* | | | |
|
||||||
|
* | v0------e0--|---v1 --> x
|
||||||
|
* e7 / e5 /
|
||||||
|
* | e8 | e9
|
||||||
|
* |/ |/
|
||||||
|
* v4------e4------v5
|
||||||
|
* /
|
||||||
|
* < z
|
||||||
|
*/
|
||||||
|
|
||||||
|
constexpr int N_VERTICES = 8;
|
||||||
|
constexpr int N_EDGES = 12;
|
||||||
|
|
||||||
|
// This table iterates around an edge of a voxel in positive direction, starting from the given voxel (0,0,0). The
|
||||||
|
// iteration is described in coordinates relative to the given voxel. The last number is the local edge index.
|
||||||
|
constexpr int edge_to_voxel_neighbor[N_EDGES][4][4] =
|
||||||
|
{
|
||||||
|
{{0, 0, 0, 0}, {0, -1, 0, 2}, {0, -1, -1, 6}, {0, 0, -1, 4}}, // e0
|
||||||
|
{{0, 0, 0, 1}, {1, 0, 0, 3}, {1, 0, -1, 7}, {0, 0, -1, 5}}, // e1
|
||||||
|
{{0, 0, 0, 2}, {0, 0, -1, 6}, {0, 1, -1, 4}, {0, 1, 0, 0}}, // e2
|
||||||
|
{{0, 0, 0, 3}, {0, 0, -1, 7}, {-1, 0, -1, 5}, {-1, 0, 0, 1}}, // e3
|
||||||
|
{{0, 0, 0, 4}, {0, 0, 1, 0}, {0, -1, 1, 2}, {0, -1, 0, 6}}, // e4
|
||||||
|
{{0, 0, 0, 5}, {0, 0, 1, 1}, {1, 0, 1, 3}, {1, 0, 0, 7}}, // e5
|
||||||
|
{{0, 0, 0, 6}, {0, 1, 0, 4}, {0, 1, 1, 0}, {0, 0, 1, 2}}, // e6
|
||||||
|
{{0, 0, 0, 7}, {-1, 0, 0, 5}, {-1, 0, 1, 1}, {0, 0, 1, 3}}, // e7
|
||||||
|
{{0, 0, 0, 8}, {-1, 0, 0, 9}, {-1, -1, 0, 10}, {0, -1, 0, 11}}, // e8
|
||||||
|
{{0, 0, 0, 9}, {0, -1, 0, 10}, {1, -1, 0, 11}, {1, 0, 0, 8}}, // e9
|
||||||
|
{{0, 0, 0, 10}, {1, 0, 0, 11}, {1, 1, 0, 8}, {0, 1, 0, 9}}, // e10
|
||||||
|
{{0, 0, 0, 11}, {0, 1, 0, 8}, {-1, 1, 0, 9}, {-1, 0, 0, 10}} // e11
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The global edge index consists of the lexicographical index of the v0 vertex of a voxel, and an index that
|
||||||
|
* represents the axis. This table maps from the axis index to the local edge index: 0 = x-axis --> 0 1 = y-axis -->
|
||||||
|
* 3 2 = z-axis --> 8
|
||||||
|
*/
|
||||||
|
constexpr int edge_store_index[3] = {0, 3, 8};
|
||||||
|
|
||||||
|
// The local vertex indices of an edge. The indices are sorted by axis direction.
|
||||||
|
constexpr int edge_to_vertex[N_EDGES][2] =
|
||||||
|
{
|
||||||
|
{0, 1}, // e0
|
||||||
|
{1, 3}, // e1
|
||||||
|
{2, 3}, // e2
|
||||||
|
{0, 2}, // e3
|
||||||
|
{4, 5}, // e4
|
||||||
|
{5, 7}, // e5
|
||||||
|
{6, 7}, // e6
|
||||||
|
{4, 6}, // e7
|
||||||
|
{0, 4}, // e8
|
||||||
|
{1, 5}, // e9
|
||||||
|
{3, 7}, // e10
|
||||||
|
{2, 6} // e11
|
||||||
|
};
|
||||||
|
|
||||||
|
// The local vertex coordinates within a voxel.
|
||||||
|
constexpr int local_vertex_location[N_VERTICES][3] =
|
||||||
|
{
|
||||||
|
{0, 0, 0}, // v0
|
||||||
|
{1, 0, 0}, // v1
|
||||||
|
{0, 1, 0}, // v2
|
||||||
|
{1, 1, 0}, // v3
|
||||||
|
{0, 0, 1}, // v4
|
||||||
|
{1, 0, 1}, // v5
|
||||||
|
{0, 1, 1}, // v6
|
||||||
|
{1, 1, 1} // v7
|
||||||
|
};
|
||||||
|
|
||||||
|
// Edges are uniquely characterized by the two end vertices, which have a unique vertex id
|
||||||
|
// the end vertices of the edge are computed in the cell by giving the indices (i,j,k).
|
||||||
|
// These indices are obtained from the cell index by adding 0 or 1 to i, j or k respectively
|
||||||
|
// Example: edge 0: (i,j,k) - (i+1,j,k)
|
||||||
|
// edge 1: (i+1,j,k) - (i+1,j+1,k)
|
||||||
|
// The first 3 indices are for the first vertex and the second 3 for the second vertex.
|
||||||
|
// there are 12 edges, assign to each vertex three edges, the global edge numbering
|
||||||
|
// consist of 3*global_vertex_id + edge_offset.
|
||||||
|
constexpr int global_edge_id[][4] = {{0, 0, 0, 0}, {1, 0, 0, 1}, {0, 1, 0, 0}, {0, 0, 0, 1},
|
||||||
|
{0, 0, 1, 0}, {1, 0, 1, 1}, {0, 1, 1, 0}, {0, 0, 1, 1},
|
||||||
|
{0, 0, 0, 2}, {1, 0, 0, 2}, {1, 1, 0, 2}, {0, 1, 0, 2}};
|
||||||
|
|
||||||
|
// probably a list without errors
|
||||||
|
// indicates which edges has to be intersected
|
||||||
|
constexpr int intersected_edges[256] =
|
||||||
|
{
|
||||||
|
0, 265, 515, 778, 2060, 2309, 2575, 2822, 1030, 1295, 1541, 1804, 3082, 3331, 3593, 3840, 400, 153, 915,
|
||||||
|
666, 2460, 2197, 2975, 2710, 1430, 1183, 1941, 1692, 3482, 3219, 3993, 3728, 560, 825, 51, 314, 2620, 2869,
|
||||||
|
2111, 2358, 1590, 1855, 1077, 1340, 3642, 3891, 3129, 3376, 928, 681, 419, 170, 2988, 2725, 2479, 2214, 1958,
|
||||||
|
1711, 1445, 1196, 4010, 3747, 3497, 3232, 2240, 2505, 2755, 3018, 204, 453, 719, 966, 3270, 3535, 3781, 4044,
|
||||||
|
1226, 1475, 1737, 1984, 2384, 2137, 2899, 2650, 348, 85, 863, 598, 3414, 3167, 3925, 3676, 1370, 1107, 1881,
|
||||||
|
1616, 2800, 3065, 2291, 2554, 764, 1013, 255, 502, 3830, 4095, 3317, 3580, 1786, 2035, 1273, 1520, 2912, 2665,
|
||||||
|
2403, 2154, 876, 613, 367, 102, 3942, 3695, 3429, 3180, 1898, 1635, 1385, 1120, 1120, 1385, 1635, 1898, 3180,
|
||||||
|
3429, 3695, 3942, 102, 367, 613, 876, 2154, 2403, 2665, 2912, 1520, 1273, 2035, 1786, 3580, 3317, 4095, 3830,
|
||||||
|
502, 255, 1013, 764, 2554, 2291, 3065, 2800, 1616, 1881, 1107, 1370, 3676, 3925, 3167, 3414, 598, 863, 85,
|
||||||
|
348, 2650, 2899, 2137, 2384, 1984, 1737, 1475, 1226, 4044, 3781, 3535, 3270, 966, 719, 453, 204, 3018, 2755,
|
||||||
|
2505, 2240, 3232, 3497, 3747, 4010, 1196, 1445, 1711, 1958, 2214, 2479, 2725, 2988, 170, 419, 681, 928, 3376,
|
||||||
|
3129, 3891, 3642, 1340, 1077, 1855, 1590, 2358, 2111, 2869, 2620, 314, 51, 825, 560, 3728, 3993, 3219, 3482,
|
||||||
|
1692, 1941, 1183, 1430, 2710, 2975, 2197, 2460, 666, 915, 153, 400, 3840, 3593, 3331, 3082, 1804, 1541, 1295,
|
||||||
|
1030, 2822, 2575, 2309, 2060, 778, 515, 265, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// list of triangles for Marching Cubes case t, position at t*16 + tri
|
||||||
|
constexpr int triangle_cases[4096] =
|
||||||
|
{
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 0 <-> mc: 0, class rep: 0
|
||||||
|
0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 1 <-> mc: 1, class rep: 1
|
||||||
|
0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 2 <-> mc: 2, class rep: 1
|
||||||
|
1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 3 <-> mc: 3, class rep: 3
|
||||||
|
3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 4 <-> mc: 8, class rep: 1
|
||||||
|
0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 5 <-> mc: 9, class rep: 3
|
||||||
|
1, 0, 9, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 6 <-> mc: 10, class rep: 6
|
||||||
|
1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1, // quitte: 7 <-> mc: 11, class rep: 7
|
||||||
|
1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 8 <-> mc: 4, class rep: 1
|
||||||
|
0, 3, 8, 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 9 <-> mc: 5, class rep: 6
|
||||||
|
9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 10 <-> mc: 6, class rep: 3
|
||||||
|
2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1, // quitte: 11 <-> mc: 7, class rep: 7
|
||||||
|
3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 12 <-> mc: 12, class rep: 3
|
||||||
|
0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1, // quitte: 13 <-> mc: 13, class rep: 7
|
||||||
|
3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1, // quitte: 14 <-> mc: 14, class rep: 7
|
||||||
|
9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 15 <-> mc: 15, class rep: 15
|
||||||
|
4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 16 <-> mc: 16, class rep: 1
|
||||||
|
4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 17 <-> mc: 17, class rep: 3
|
||||||
|
0, 9, 1, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 18 <-> mc: 18, class rep: 6
|
||||||
|
4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1, // quitte: 19 <-> mc: 19, class rep: 7
|
||||||
|
8, 7, 4, 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 20 <-> mc: 24, class rep: 6
|
||||||
|
11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1, // quitte: 21 <-> mc: 25, class rep: 7
|
||||||
|
9, 1, 0, 8, 7, 4, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, // quitte: 22 <-> mc: 26, class rep: 22
|
||||||
|
4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1, // quitte: 23 <-> mc: 27, class rep: 23
|
||||||
|
1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 24 <-> mc: 20, class rep: 24
|
||||||
|
3, 7, 4, 3, 4, 0, 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, // quitte: 25 <-> mc: 21, class rep: 25
|
||||||
|
9, 10, 2, 9, 2, 0, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, // quitte: 26 <-> mc: 22, class rep: 25
|
||||||
|
2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1, // quitte: 27 <-> mc: 23, class rep: 27
|
||||||
|
3, 1, 10, 3, 10, 11, 7, 4, 8, -1, -1, -1, -1, -1, -1, -1, // quitte: 28 <-> mc: 28, class rep: 25
|
||||||
|
1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1, // quitte: 29 <-> mc: 29, class rep: 29
|
||||||
|
4, 8, 7, 9, 11, 0, 9, 10, 11, 11, 3, 0, -1, -1, -1, -1, // quitte: 30 <-> mc: 30, class rep: 30
|
||||||
|
4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1, // quitte: 31 <-> mc: 31, class rep: 7
|
||||||
|
9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 32 <-> mc: 32, class rep: 1
|
||||||
|
9, 4, 5, 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 33 <-> mc: 33, class rep: 6
|
||||||
|
0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 34 <-> mc: 34, class rep: 3
|
||||||
|
8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1, // quitte: 35 <-> mc: 35, class rep: 7
|
||||||
|
9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 36 <-> mc: 40, class rep: 24
|
||||||
|
0, 2, 11, 0, 11, 8, 4, 5, 9, -1, -1, -1, -1, -1, -1, -1, // quitte: 37 <-> mc: 41, class rep: 25
|
||||||
|
0, 4, 5, 0, 5, 1, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, // quitte: 38 <-> mc: 42, class rep: 25
|
||||||
|
2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1, // quitte: 39 <-> mc: 43, class rep: 29
|
||||||
|
1, 10, 2, 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 40 <-> mc: 36, class rep: 6
|
||||||
|
3, 8, 0, 1, 10, 2, 4, 5, 9, -1, -1, -1, -1, -1, -1, -1, // quitte: 41 <-> mc: 37, class rep: 22
|
||||||
|
5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1, // quitte: 42 <-> mc: 38, class rep: 7
|
||||||
|
2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1, // quitte: 43 <-> mc: 39, class rep: 23
|
||||||
|
10, 11, 3, 10, 3, 1, 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, // quitte: 44 <-> mc: 44, class rep: 25
|
||||||
|
4, 5, 9, 0, 1, 8, 8, 1, 10, 8, 10, 11, -1, -1, -1, -1, // quitte: 45 <-> mc: 45, class rep: 30
|
||||||
|
5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1, // quitte: 46 <-> mc: 46, class rep: 27
|
||||||
|
5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, // quitte: 47 <-> mc: 47, class rep: 7
|
||||||
|
9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 48 <-> mc: 48, class rep: 3
|
||||||
|
9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1, // quitte: 49 <-> mc: 49, class rep: 7
|
||||||
|
0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1, // quitte: 50 <-> mc: 50, class rep: 7
|
||||||
|
1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 51 <-> mc: 51, class rep: 15
|
||||||
|
7, 5, 9, 7, 9, 8, 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, // quitte: 52 <-> mc: 56, class rep: 25
|
||||||
|
9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1, // quitte: 53 <-> mc: 57, class rep: 27
|
||||||
|
2, 11, 3, 0, 8, 1, 1, 8, 7, 1, 7, 5, -1, -1, -1, -1, // quitte: 54 <-> mc: 58, class rep: 30
|
||||||
|
11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1, // quitte: 55 <-> mc: 59, class rep: 7
|
||||||
|
9, 8, 7, 9, 7, 5, 10, 2, 1, -1, -1, -1, -1, -1, -1, -1, // quitte: 56 <-> mc: 52, class rep: 25
|
||||||
|
10, 2, 1, 9, 0, 5, 5, 0, 3, 5, 3, 7, -1, -1, -1, -1, // quitte: 57 <-> mc: 53, class rep: 30
|
||||||
|
8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1, // quitte: 58 <-> mc: 54, class rep: 29
|
||||||
|
2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, // quitte: 59 <-> mc: 55, class rep: 7
|
||||||
|
9, 8, 5, 8, 7, 5, 10, 3, 1, 10, 11, 3, -1, -1, -1, -1, // quitte: 60 <-> mc: 60, class rep: 60
|
||||||
|
5, 0, 7, 5, 9, 0, 7, 0, 11, 1, 10, 0, 11, 0, 10, -1, // quitte: 61 <-> mc: 61, class rep: 25
|
||||||
|
11, 0, 10, 11, 3, 0, 10, 0, 5, 8, 7, 0, 5, 0, 7, -1, // quitte: 62 <-> mc: 62, class rep: 25
|
||||||
|
11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 63 <-> mc: 63, class rep: 3
|
||||||
|
7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 64 <-> mc: 128, class rep: 1
|
||||||
|
3, 8, 0, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 65 <-> mc: 129, class rep: 6
|
||||||
|
0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 66 <-> mc: 130, class rep: 24
|
||||||
|
8, 9, 1, 8, 1, 3, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, // quitte: 67 <-> mc: 131, class rep: 25
|
||||||
|
7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 68 <-> mc: 136, class rep: 3
|
||||||
|
7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1, // quitte: 69 <-> mc: 137, class rep: 7
|
||||||
|
2, 6, 7, 2, 7, 3, 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, // quitte: 70 <-> mc: 138, class rep: 25
|
||||||
|
1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1, // quitte: 71 <-> mc: 139, class rep: 27
|
||||||
|
10, 2, 1, 6, 7, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 72 <-> mc: 132, class rep: 6
|
||||||
|
1, 10, 2, 3, 8, 0, 6, 7, 11, -1, -1, -1, -1, -1, -1, -1, // quitte: 73 <-> mc: 133, class rep: 22
|
||||||
|
2, 0, 9, 2, 9, 10, 6, 7, 11, -1, -1, -1, -1, -1, -1, -1, // quitte: 74 <-> mc: 134, class rep: 25
|
||||||
|
6, 7, 11, 2, 3, 10, 10, 3, 8, 10, 8, 9, -1, -1, -1, -1, // quitte: 75 <-> mc: 135, class rep: 30
|
||||||
|
10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1, // quitte: 76 <-> mc: 140, class rep: 7
|
||||||
|
10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1, // quitte: 77 <-> mc: 141, class rep: 23
|
||||||
|
0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1, // quitte: 78 <-> mc: 142, class rep: 29
|
||||||
|
7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1, // quitte: 79 <-> mc: 143, class rep: 7
|
||||||
|
6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 80 <-> mc: 144, class rep: 3
|
||||||
|
3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1, // quitte: 81 <-> mc: 145, class rep: 7
|
||||||
|
8, 11, 6, 8, 6, 4, 9, 1, 0, -1, -1, -1, -1, -1, -1, -1, // quitte: 82 <-> mc: 146, class rep: 25
|
||||||
|
9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1, // quitte: 83 <-> mc: 147, class rep: 29
|
||||||
|
8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, // quitte: 84 <-> mc: 152, class rep: 7
|
||||||
|
0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 85 <-> mc: 153, class rep: 15
|
||||||
|
1, 0, 9, 2, 4, 3, 2, 6, 4, 4, 8, 3, -1, -1, -1, -1, // quitte: 86 <-> mc: 154, class rep: 30
|
||||||
|
1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1, // quitte: 87 <-> mc: 155, class rep: 7
|
||||||
|
6, 4, 8, 6, 8, 11, 2, 1, 10, -1, -1, -1, -1, -1, -1, -1, // quitte: 88 <-> mc: 148, class rep: 25
|
||||||
|
1, 10, 2, 3, 11, 0, 0, 11, 6, 0, 6, 4, -1, -1, -1, -1, // quitte: 89 <-> mc: 149, class rep: 30
|
||||||
|
4, 8, 11, 4, 11, 6, 0, 9, 2, 2, 9, 10, -1, -1, -1, -1, // quitte: 90 <-> mc: 150, class rep: 60
|
||||||
|
10, 3, 9, 10, 2, 3, 9, 3, 4, 11, 6, 3, 4, 3, 6, -1, // quitte: 91 <-> mc: 151, class rep: 25
|
||||||
|
8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1, // quitte: 92 <-> mc: 156, class rep: 27
|
||||||
|
10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1, // quitte: 93 <-> mc: 157, class rep: 7
|
||||||
|
4, 3, 6, 4, 8, 3, 6, 3, 10, 0, 9, 3, 10, 3, 9, -1, // quitte: 94 <-> mc: 158, class rep: 25
|
||||||
|
10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 95 <-> mc: 159, class rep: 3
|
||||||
|
4, 5, 9, 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 96 <-> mc: 160, class rep: 6
|
||||||
|
0, 3, 8, 4, 5, 9, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, // quitte: 97 <-> mc: 161, class rep: 22
|
||||||
|
5, 1, 0, 5, 0, 4, 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, // quitte: 98 <-> mc: 162, class rep: 25
|
||||||
|
11, 6, 7, 8, 4, 3, 3, 4, 5, 3, 5, 1, -1, -1, -1, -1, // quitte: 99 <-> mc: 163, class rep: 30
|
||||||
|
7, 3, 2, 7, 2, 6, 5, 9, 4, -1, -1, -1, -1, -1, -1, -1, // quitte: 100 <-> mc: 168, class rep: 25
|
||||||
|
9, 4, 5, 0, 6, 8, 0, 2, 6, 6, 7, 8, -1, -1, -1, -1, // quitte: 101 <-> mc: 169, class rep: 30
|
||||||
|
3, 2, 6, 3, 6, 7, 1, 0, 5, 5, 0, 4, -1, -1, -1, -1, // quitte: 102 <-> mc: 170, class rep: 60
|
||||||
|
6, 8, 2, 6, 7, 8, 2, 8, 1, 4, 5, 8, 1, 8, 5, -1, // quitte: 103 <-> mc: 171, class rep: 25
|
||||||
|
9, 4, 5, 10, 2, 1, 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, // quitte: 104 <-> mc: 164, class rep: 22
|
||||||
|
6, 7, 11, 1, 10, 2, 0, 3, 8, 4, 5, 9, -1, -1, -1, -1, // quitte: 105 <-> mc: 165, class rep: 105
|
||||||
|
7, 11, 6, 5, 10, 4, 4, 10, 2, 4, 2, 0, -1, -1, -1, -1, // quitte: 106 <-> mc: 166, class rep: 30
|
||||||
|
3, 8, 4, 3, 4, 5, 3, 5, 2, 10, 2, 5, 11, 6, 7, -1, // quitte: 107 <-> mc: 167, class rep: 22
|
||||||
|
9, 4, 5, 10, 6, 1, 1, 6, 7, 1, 7, 3, -1, -1, -1, -1, // quitte: 108 <-> mc: 172, class rep: 30
|
||||||
|
1, 10, 6, 1, 6, 7, 1, 7, 0, 8, 0, 7, 9, 4, 5, -1, // quitte: 109 <-> mc: 173, class rep: 22
|
||||||
|
4, 10, 0, 4, 5, 10, 0, 10, 3, 6, 7, 10, 3, 10, 7, -1, // quitte: 110 <-> mc: 174, class rep: 25
|
||||||
|
7, 10, 6, 7, 8, 10, 5, 10, 4, 4, 10, 8, -1, -1, -1, -1, // quitte: 111 <-> mc: 175, class rep: 6
|
||||||
|
6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1, // quitte: 112 <-> mc: 176, class rep: 7
|
||||||
|
3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1, // quitte: 113 <-> mc: 177, class rep: 23
|
||||||
|
0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1, // quitte: 114 <-> mc: 178, class rep: 27
|
||||||
|
6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1, // quitte: 115 <-> mc: 179, class rep: 7
|
||||||
|
5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1, // quitte: 116 <-> mc: 184, class rep: 29
|
||||||
|
9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1, // quitte: 117 <-> mc: 185, class rep: 7
|
||||||
|
1, 8, 5, 1, 0, 8, 5, 8, 6, 3, 2, 8, 6, 8, 2, -1, // quitte: 118 <-> mc: 186, class rep: 25
|
||||||
|
1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 119 <-> mc: 187, class rep: 3
|
||||||
|
1, 10, 2, 9, 11, 5, 9, 8, 11, 11, 6, 5, -1, -1, -1, -1, // quitte: 120 <-> mc: 180, class rep: 30
|
||||||
|
0, 3, 11, 0, 11, 6, 0, 6, 9, 5, 9, 6, 1, 10, 2, -1, // quitte: 121 <-> mc: 181, class rep: 22
|
||||||
|
11, 5, 8, 11, 6, 5, 8, 5, 0, 10, 2, 5, 0, 5, 2, -1, // quitte: 122 <-> mc: 182, class rep: 25
|
||||||
|
6, 3, 11, 6, 5, 3, 2, 3, 10, 10, 3, 5, -1, -1, -1, -1, // quitte: 123 <-> mc: 183, class rep: 6
|
||||||
|
1, 6, 3, 1, 10, 6, 3, 6, 8, 5, 9, 6, 8, 6, 9, -1, // quitte: 124 <-> mc: 188, class rep: 25
|
||||||
|
10, 0, 1, 10, 6, 0, 9, 0, 5, 5, 0, 6, -1, -1, -1, -1, // quitte: 125 <-> mc: 189, class rep: 6
|
||||||
|
0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 126 <-> mc: 190, class rep: 24
|
||||||
|
10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 127 <-> mc: 191, class rep: 1
|
||||||
|
10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 128 <-> mc: 64, class rep: 1
|
||||||
|
0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 129 <-> mc: 65, class rep: 24
|
||||||
|
9, 1, 0, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 130 <-> mc: 66, class rep: 6
|
||||||
|
1, 3, 8, 1, 8, 9, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, // quitte: 131 <-> mc: 67, class rep: 25
|
||||||
|
2, 11, 3, 10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 132 <-> mc: 72, class rep: 6
|
||||||
|
11, 8, 0, 11, 0, 2, 10, 5, 6, -1, -1, -1, -1, -1, -1, -1, // quitte: 133 <-> mc: 73, class rep: 25
|
||||||
|
0, 9, 1, 2, 11, 3, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, // quitte: 134 <-> mc: 74, class rep: 22
|
||||||
|
5, 6, 10, 1, 2, 9, 9, 2, 11, 9, 11, 8, -1, -1, -1, -1, // quitte: 135 <-> mc: 75, class rep: 30
|
||||||
|
1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 136 <-> mc: 68, class rep: 3
|
||||||
|
1, 5, 6, 1, 6, 2, 3, 8, 0, -1, -1, -1, -1, -1, -1, -1, // quitte: 137 <-> mc: 69, class rep: 25
|
||||||
|
9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1, // quitte: 138 <-> mc: 70, class rep: 7
|
||||||
|
5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1, // quitte: 139 <-> mc: 71, class rep: 29
|
||||||
|
6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1, // quitte: 140 <-> mc: 76, class rep: 7
|
||||||
|
0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1, // quitte: 141 <-> mc: 77, class rep: 27
|
||||||
|
3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1, // quitte: 142 <-> mc: 78, class rep: 23
|
||||||
|
6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1, // quitte: 143 <-> mc: 79, class rep: 7
|
||||||
|
5, 6, 10, 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 144 <-> mc: 80, class rep: 6
|
||||||
|
4, 0, 3, 4, 3, 7, 6, 10, 5, -1, -1, -1, -1, -1, -1, -1, // quitte: 145 <-> mc: 81, class rep: 25
|
||||||
|
1, 0, 9, 5, 6, 10, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, // quitte: 146 <-> mc: 82, class rep: 22
|
||||||
|
10, 5, 6, 1, 7, 9, 1, 3, 7, 7, 4, 9, -1, -1, -1, -1, // quitte: 147 <-> mc: 83, class rep: 30
|
||||||
|
3, 2, 11, 7, 4, 8, 10, 5, 6, -1, -1, -1, -1, -1, -1, -1, // quitte: 148 <-> mc: 88, class rep: 22
|
||||||
|
5, 6, 10, 4, 2, 7, 4, 0, 2, 2, 11, 7, -1, -1, -1, -1, // quitte: 149 <-> mc: 89, class rep: 30
|
||||||
|
0, 9, 1, 4, 8, 7, 2, 11, 3, 5, 6, 10, -1, -1, -1, -1, // quitte: 150 <-> mc: 90, class rep: 105
|
||||||
|
9, 1, 2, 9, 2, 11, 9, 11, 4, 7, 4, 11, 5, 6, 10, -1, // quitte: 151 <-> mc: 91, class rep: 22
|
||||||
|
6, 2, 1, 6, 1, 5, 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, // quitte: 152 <-> mc: 84, class rep: 25
|
||||||
|
1, 5, 2, 5, 6, 2, 3, 4, 0, 3, 7, 4, -1, -1, -1, -1, // quitte: 153 <-> mc: 85, class rep: 60
|
||||||
|
8, 7, 4, 9, 5, 0, 0, 5, 6, 0, 6, 2, -1, -1, -1, -1, // quitte: 154 <-> mc: 86, class rep: 30
|
||||||
|
7, 9, 3, 7, 4, 9, 3, 9, 2, 5, 6, 9, 2, 9, 6, -1, // quitte: 155 <-> mc: 87, class rep: 25
|
||||||
|
8, 7, 4, 3, 5, 11, 3, 1, 5, 5, 6, 11, -1, -1, -1, -1, // quitte: 156 <-> mc: 92, class rep: 30
|
||||||
|
5, 11, 1, 5, 6, 11, 1, 11, 0, 7, 4, 11, 0, 11, 4, -1, // quitte: 157 <-> mc: 93, class rep: 25
|
||||||
|
0, 9, 5, 0, 5, 6, 0, 6, 3, 11, 3, 6, 8, 7, 4, -1, // quitte: 158 <-> mc: 94, class rep: 22
|
||||||
|
6, 9, 5, 6, 11, 9, 4, 9, 7, 7, 9, 11, -1, -1, -1, -1, // quitte: 159 <-> mc: 95, class rep: 6
|
||||||
|
10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 160 <-> mc: 96, class rep: 3
|
||||||
|
4, 6, 10, 4, 10, 9, 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, // quitte: 161 <-> mc: 97, class rep: 25
|
||||||
|
10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1, // quitte: 162 <-> mc: 98, class rep: 7
|
||||||
|
8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1, // quitte: 163 <-> mc: 99, class rep: 27
|
||||||
|
10, 9, 4, 10, 4, 6, 11, 3, 2, -1, -1, -1, -1, -1, -1, -1, // quitte: 164 <-> mc: 104, class rep: 25
|
||||||
|
0, 2, 8, 2, 11, 8, 4, 10, 9, 4, 6, 10, -1, -1, -1, -1, // quitte: 165 <-> mc: 105, class rep: 60
|
||||||
|
3, 2, 11, 0, 6, 1, 0, 4, 6, 6, 10, 1, -1, -1, -1, -1, // quitte: 166 <-> mc: 106, class rep: 30
|
||||||
|
6, 1, 4, 6, 10, 1, 4, 1, 8, 2, 11, 1, 8, 1, 11, -1, // quitte: 167 <-> mc: 107, class rep: 25
|
||||||
|
1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1, // quitte: 168 <-> mc: 100, class rep: 7
|
||||||
|
3, 8, 0, 1, 9, 2, 2, 9, 4, 2, 4, 6, -1, -1, -1, -1, // quitte: 169 <-> mc: 101, class rep: 30
|
||||||
|
0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 170 <-> mc: 102, class rep: 15
|
||||||
|
8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, // quitte: 171 <-> mc: 103, class rep: 7
|
||||||
|
9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1, // quitte: 172 <-> mc: 108, class rep: 29
|
||||||
|
8, 1, 11, 8, 0, 1, 11, 1, 6, 9, 4, 1, 6, 1, 4, -1, // quitte: 173 <-> mc: 109, class rep: 25
|
||||||
|
3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1, // quitte: 174 <-> mc: 110, class rep: 7
|
||||||
|
6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 175 <-> mc: 111, class rep: 3
|
||||||
|
7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1, // quitte: 176 <-> mc: 112, class rep: 7
|
||||||
|
0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1, // quitte: 177 <-> mc: 113, class rep: 29
|
||||||
|
10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1, // quitte: 178 <-> mc: 114, class rep: 23
|
||||||
|
10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1, // quitte: 179 <-> mc: 115, class rep: 7
|
||||||
|
2, 11, 3, 10, 8, 6, 10, 9, 8, 8, 7, 6, -1, -1, -1, -1, // quitte: 180 <-> mc: 120, class rep: 30
|
||||||
|
2, 7, 0, 2, 11, 7, 0, 7, 9, 6, 10, 7, 9, 7, 10, -1, // quitte: 181 <-> mc: 121, class rep: 25
|
||||||
|
1, 0, 8, 1, 8, 7, 1, 7, 10, 6, 10, 7, 2, 11, 3, -1, // quitte: 182 <-> mc: 122, class rep: 22
|
||||||
|
11, 1, 2, 11, 7, 1, 10, 1, 6, 6, 1, 7, -1, -1, -1, -1, // quitte: 183 <-> mc: 123, class rep: 6
|
||||||
|
1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1, // quitte: 184 <-> mc: 116, class rep: 27
|
||||||
|
2, 9, 6, 2, 1, 9, 6, 9, 7, 0, 3, 9, 7, 9, 3, -1, // quitte: 185 <-> mc: 117, class rep: 25
|
||||||
|
7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1, // quitte: 186 <-> mc: 118, class rep: 7
|
||||||
|
7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 187 <-> mc: 119, class rep: 3
|
||||||
|
8, 6, 9, 8, 7, 6, 9, 6, 1, 11, 3, 6, 1, 6, 3, -1, // quitte: 188 <-> mc: 124, class rep: 25
|
||||||
|
0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 189 <-> mc: 125, class rep: 24
|
||||||
|
7, 0, 8, 7, 6, 0, 3, 0, 11, 11, 0, 6, -1, -1, -1, -1, // quitte: 190 <-> mc: 126, class rep: 6
|
||||||
|
7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 191 <-> mc: 127, class rep: 1
|
||||||
|
11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 192 <-> mc: 192, class rep: 3
|
||||||
|
11, 10, 5, 11, 5, 7, 8, 0, 3, -1, -1, -1, -1, -1, -1, -1, // quitte: 193 <-> mc: 193, class rep: 25
|
||||||
|
5, 7, 11, 5, 11, 10, 1, 0, 9, -1, -1, -1, -1, -1, -1, -1, // quitte: 194 <-> mc: 194, class rep: 25
|
||||||
|
10, 5, 7, 10, 7, 11, 9, 1, 8, 8, 1, 3, -1, -1, -1, -1, // quitte: 195 <-> mc: 195, class rep: 60
|
||||||
|
2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, // quitte: 196 <-> mc: 200, class rep: 7
|
||||||
|
8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1, // quitte: 197 <-> mc: 201, class rep: 29
|
||||||
|
9, 1, 0, 5, 3, 10, 5, 7, 3, 3, 2, 10, -1, -1, -1, -1, // quitte: 198 <-> mc: 202, class rep: 30
|
||||||
|
9, 2, 8, 9, 1, 2, 8, 2, 7, 10, 5, 2, 7, 2, 5, -1, // quitte: 199 <-> mc: 203, class rep: 25
|
||||||
|
11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1, // quitte: 200 <-> mc: 196, class rep: 7
|
||||||
|
0, 3, 8, 1, 7, 2, 1, 5, 7, 7, 11, 2, -1, -1, -1, -1, // quitte: 201 <-> mc: 197, class rep: 30
|
||||||
|
9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1, // quitte: 202 <-> mc: 198, class rep: 27
|
||||||
|
7, 2, 5, 7, 11, 2, 5, 2, 9, 3, 8, 2, 9, 2, 8, -1, // quitte: 203 <-> mc: 199, class rep: 25
|
||||||
|
1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 204 <-> mc: 204, class rep: 15
|
||||||
|
0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1, // quitte: 205 <-> mc: 205, class rep: 7
|
||||||
|
9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1, // quitte: 206 <-> mc: 206, class rep: 7
|
||||||
|
9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 207 <-> mc: 207, class rep: 3
|
||||||
|
5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, // quitte: 208 <-> mc: 208, class rep: 7
|
||||||
|
5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1, // quitte: 209 <-> mc: 209, class rep: 27
|
||||||
|
0, 9, 1, 8, 10, 4, 8, 11, 10, 10, 5, 4, -1, -1, -1, -1, // quitte: 210 <-> mc: 210, class rep: 30
|
||||||
|
10, 4, 11, 10, 5, 4, 11, 4, 3, 9, 1, 4, 3, 4, 1, -1, // quitte: 211 <-> mc: 211, class rep: 25
|
||||||
|
2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1, // quitte: 212 <-> mc: 216, class rep: 23
|
||||||
|
5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1, // quitte: 213 <-> mc: 217, class rep: 7
|
||||||
|
3, 2, 10, 3, 10, 5, 3, 5, 8, 4, 8, 5, 0, 9, 1, -1, // quitte: 214 <-> mc: 218, class rep: 22
|
||||||
|
5, 2, 10, 5, 4, 2, 1, 2, 9, 9, 2, 4, -1, -1, -1, -1, // quitte: 215 <-> mc: 219, class rep: 6
|
||||||
|
2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1, // quitte: 216 <-> mc: 212, class rep: 29
|
||||||
|
0, 11, 4, 0, 3, 11, 4, 11, 5, 2, 1, 11, 5, 11, 1, -1, // quitte: 217 <-> mc: 213, class rep: 25
|
||||||
|
0, 5, 2, 0, 9, 5, 2, 5, 11, 4, 8, 5, 11, 5, 8, -1, // quitte: 218 <-> mc: 214, class rep: 25
|
||||||
|
9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 219 <-> mc: 215, class rep: 24
|
||||||
|
8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1, // quitte: 220 <-> mc: 220, class rep: 7
|
||||||
|
0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 221 <-> mc: 221, class rep: 3
|
||||||
|
8, 5, 4, 8, 3, 5, 9, 5, 0, 0, 5, 3, -1, -1, -1, -1, // quitte: 222 <-> mc: 222, class rep: 6
|
||||||
|
9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 223 <-> mc: 223, class rep: 1
|
||||||
|
4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1, // quitte: 224 <-> mc: 224, class rep: 7
|
||||||
|
0, 3, 8, 4, 7, 9, 9, 7, 11, 9, 11, 10, -1, -1, -1, -1, // quitte: 225 <-> mc: 225, class rep: 30
|
||||||
|
1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1, // quitte: 226 <-> mc: 226, class rep: 29
|
||||||
|
3, 4, 1, 3, 8, 4, 1, 4, 10, 7, 11, 4, 10, 4, 11, -1, // quitte: 227 <-> mc: 227, class rep: 25
|
||||||
|
2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1, // quitte: 228 <-> mc: 232, class rep: 27
|
||||||
|
9, 7, 10, 9, 4, 7, 10, 7, 2, 8, 0, 7, 2, 7, 0, -1, // quitte: 229 <-> mc: 233, class rep: 25
|
||||||
|
3, 10, 7, 3, 2, 10, 7, 10, 4, 1, 0, 10, 4, 10, 0, -1, // quitte: 230 <-> mc: 234, class rep: 25
|
||||||
|
1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 231 <-> mc: 235, class rep: 24
|
||||||
|
4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1, // quitte: 232 <-> mc: 228, class rep: 23
|
||||||
|
9, 4, 7, 9, 7, 11, 9, 11, 1, 2, 1, 11, 0, 3, 8, -1, // quitte: 233 <-> mc: 229, class rep: 22
|
||||||
|
11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1, // quitte: 234 <-> mc: 230, class rep: 7
|
||||||
|
11, 4, 7, 11, 2, 4, 8, 4, 3, 3, 4, 2, -1, -1, -1, -1, // quitte: 235 <-> mc: 231, class rep: 6
|
||||||
|
4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1, // quitte: 236 <-> mc: 236, class rep: 7
|
||||||
|
4, 1, 9, 4, 7, 1, 0, 1, 8, 8, 1, 7, -1, -1, -1, -1, // quitte: 237 <-> mc: 237, class rep: 6
|
||||||
|
4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 238 <-> mc: 238, class rep: 3
|
||||||
|
4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 239 <-> mc: 239, class rep: 1
|
||||||
|
9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 240 <-> mc: 240, class rep: 15
|
||||||
|
3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1, // quitte: 241 <-> mc: 241, class rep: 7
|
||||||
|
0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1, // quitte: 242 <-> mc: 242, class rep: 7
|
||||||
|
3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 243 <-> mc: 243, class rep: 3
|
||||||
|
2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1, // quitte: 244 <-> mc: 248, class rep: 7
|
||||||
|
9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 245 <-> mc: 249, class rep: 3
|
||||||
|
2, 8, 3, 2, 10, 8, 0, 8, 1, 1, 8, 10, -1, -1, -1, -1, // quitte: 246 <-> mc: 250, class rep: 6
|
||||||
|
1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 247 <-> mc: 251, class rep: 1
|
||||||
|
1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1, // quitte: 248 <-> mc: 244, class rep: 7
|
||||||
|
3, 9, 0, 3, 11, 9, 1, 9, 2, 2, 9, 11, -1, -1, -1, -1, // quitte: 249 <-> mc: 245, class rep: 6
|
||||||
|
0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 250 <-> mc: 246, class rep: 3
|
||||||
|
3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 251 <-> mc: 247, class rep: 1
|
||||||
|
1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 252 <-> mc: 252, class rep: 3
|
||||||
|
0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 253 <-> mc: 253, class rep: 1
|
||||||
|
0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // quitte: 254 <-> mc: 254, class rep: 1
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // quitte: 255 <-> mc: 255, class rep: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// list of ambiguous cases
|
||||||
|
constexpr int t_ambig[256] =
|
||||||
|
{
|
||||||
|
0, // quitte: 0 <-> mc: 0, class representative: 0
|
||||||
|
1, // quitte: 1 <-> mc: 1, class representative: 1
|
||||||
|
2, // quitte: 2 <-> mc: 2, class representative: 1
|
||||||
|
3, // quitte: 3 <-> mc: 3, class representative: 3
|
||||||
|
4, // quitte: 4 <-> mc: 8, class representative: 1
|
||||||
|
5, // quitte: 5 <-> mc: 9, class representative: 3
|
||||||
|
105, // quitte: 6 <-> mc: 10, class representative: 6
|
||||||
|
7, // quitte: 7 <-> mc: 11, class representative: 7
|
||||||
|
8, // quitte: 8 <-> mc: 4, class representative: 1
|
||||||
|
105, // quitte: 9 <-> mc: 5, class representative: 6
|
||||||
|
10, // quitte: 10 <-> mc: 6, class representative: 3
|
||||||
|
11, // quitte: 11 <-> mc: 7, class representative: 7
|
||||||
|
12, // quitte: 12 <-> mc: 12, class representative: 3
|
||||||
|
13, // quitte: 13 <-> mc: 13, class representative: 7
|
||||||
|
14, // quitte: 14 <-> mc: 14, class representative: 7
|
||||||
|
15, // quitte: 15 <-> mc: 15, class representative: 15
|
||||||
|
16, // quitte: 16 <-> mc: 16, class representative: 1
|
||||||
|
17, // quitte: 17 <-> mc: 17, class representative: 3
|
||||||
|
105, // quitte: 18 <-> mc: 18, class representative: 6
|
||||||
|
19, // quitte: 19 <-> mc: 19, class representative: 7
|
||||||
|
105, // quitte: 20 <-> mc: 24, class representative: 6
|
||||||
|
21, // quitte: 21 <-> mc: 25, class representative: 7
|
||||||
|
105, // quitte: 22 <-> mc: 26, class representative: 22
|
||||||
|
23, // quitte: 23 <-> mc: 27, class representative: 23
|
||||||
|
105, // quitte: 24 <-> mc: 20, class representative: 24
|
||||||
|
105, // quitte: 25 <-> mc: 21, class representative: 25
|
||||||
|
105, // quitte: 26 <-> mc: 22, class representative: 25
|
||||||
|
27, // quitte: 27 <-> mc: 23, class representative: 27
|
||||||
|
105, // quitte: 28 <-> mc: 28, class representative: 25
|
||||||
|
29, // quitte: 29 <-> mc: 29, class representative: 29
|
||||||
|
105, // quitte: 30 <-> mc: 30, class representative: 30
|
||||||
|
31, // quitte: 31 <-> mc: 31, class representative: 7
|
||||||
|
32, // quitte: 32 <-> mc: 32, class representative: 1
|
||||||
|
105, // quitte: 33 <-> mc: 33, class representative: 6
|
||||||
|
34, // quitte: 34 <-> mc: 34, class representative: 3
|
||||||
|
35, // quitte: 35 <-> mc: 35, class representative: 7
|
||||||
|
105, // quitte: 36 <-> mc: 40, class representative: 24
|
||||||
|
105, // quitte: 37 <-> mc: 41, class representative: 25
|
||||||
|
105, // quitte: 38 <-> mc: 42, class representative: 25
|
||||||
|
39, // quitte: 39 <-> mc: 43, class representative: 29
|
||||||
|
105, // quitte: 40 <-> mc: 36, class representative: 6
|
||||||
|
105, // quitte: 41 <-> mc: 37, class representative: 22
|
||||||
|
42, // quitte: 42 <-> mc: 38, class representative: 7
|
||||||
|
43, // quitte: 43 <-> mc: 39, class representative: 23
|
||||||
|
105, // quitte: 44 <-> mc: 44, class representative: 25
|
||||||
|
105, // quitte: 45 <-> mc: 45, class representative: 30
|
||||||
|
46, // quitte: 46 <-> mc: 46, class representative: 27
|
||||||
|
47, // quitte: 47 <-> mc: 47, class representative: 7
|
||||||
|
48, // quitte: 48 <-> mc: 48, class representative: 3
|
||||||
|
49, // quitte: 49 <-> mc: 49, class representative: 7
|
||||||
|
50, // quitte: 50 <-> mc: 50, class representative: 7
|
||||||
|
51, // quitte: 51 <-> mc: 51, class representative: 15
|
||||||
|
105, // quitte: 52 <-> mc: 56, class representative: 25
|
||||||
|
53, // quitte: 53 <-> mc: 57, class representative: 27
|
||||||
|
105, // quitte: 54 <-> mc: 58, class representative: 30
|
||||||
|
55, // quitte: 55 <-> mc: 59, class representative: 7
|
||||||
|
105, // quitte: 56 <-> mc: 52, class representative: 25
|
||||||
|
105, // quitte: 57 <-> mc: 53, class representative: 30
|
||||||
|
58, // quitte: 58 <-> mc: 54, class representative: 29
|
||||||
|
59, // quitte: 59 <-> mc: 55, class representative: 7
|
||||||
|
105, // quitte: 60 <-> mc: 60, class representative: 60
|
||||||
|
105, // quitte: 61 <-> mc: 61, class representative: 25
|
||||||
|
105, // quitte: 62 <-> mc: 62, class representative: 25
|
||||||
|
63, // quitte: 63 <-> mc: 63, class representative: 3
|
||||||
|
64, // quitte: 64 <-> mc: 128, class representative: 1
|
||||||
|
105, // quitte: 65 <-> mc: 129, class representative: 6
|
||||||
|
105, // quitte: 66 <-> mc: 130, class representative: 24
|
||||||
|
105, // quitte: 67 <-> mc: 131, class representative: 25
|
||||||
|
68, // quitte: 68 <-> mc: 136, class representative: 3
|
||||||
|
69, // quitte: 69 <-> mc: 137, class representative: 7
|
||||||
|
105, // quitte: 70 <-> mc: 138, class representative: 25
|
||||||
|
71, // quitte: 71 <-> mc: 139, class representative: 27
|
||||||
|
105, // quitte: 72 <-> mc: 132, class representative: 6
|
||||||
|
105, // quitte: 73 <-> mc: 133, class representative: 22
|
||||||
|
105, // quitte: 74 <-> mc: 134, class representative: 25
|
||||||
|
105, // quitte: 75 <-> mc: 135, class representative: 30
|
||||||
|
76, // quitte: 76 <-> mc: 140, class representative: 7
|
||||||
|
77, // quitte: 77 <-> mc: 141, class representative: 23
|
||||||
|
78, // quitte: 78 <-> mc: 142, class representative: 29
|
||||||
|
79, // quitte: 79 <-> mc: 143, class representative: 7
|
||||||
|
80, // quitte: 80 <-> mc: 144, class representative: 3
|
||||||
|
81, // quitte: 81 <-> mc: 145, class representative: 7
|
||||||
|
105, // quitte: 82 <-> mc: 146, class representative: 25
|
||||||
|
83, // quitte: 83 <-> mc: 147, class representative: 29
|
||||||
|
84, // quitte: 84 <-> mc: 152, class representative: 7
|
||||||
|
85, // quitte: 85 <-> mc: 153, class representative: 15
|
||||||
|
105, // quitte: 86 <-> mc: 154, class representative: 30
|
||||||
|
87, // quitte: 87 <-> mc: 155, class representative: 7
|
||||||
|
105, // quitte: 88 <-> mc: 148, class representative: 25
|
||||||
|
105, // quitte: 89 <-> mc: 149, class representative: 30
|
||||||
|
105, // quitte: 90 <-> mc: 150, class representative: 60
|
||||||
|
105, // quitte: 91 <-> mc: 151, class representative: 25
|
||||||
|
92, // quitte: 92 <-> mc: 156, class representative: 27
|
||||||
|
93, // quitte: 93 <-> mc: 157, class representative: 7
|
||||||
|
105, // quitte: 94 <-> mc: 158, class representative: 25
|
||||||
|
95, // quitte: 95 <-> mc: 159, class representative: 3
|
||||||
|
105, // quitte: 96 <-> mc: 160, class representative: 6
|
||||||
|
105, // quitte: 97 <-> mc: 161, class representative: 22
|
||||||
|
105, // quitte: 98 <-> mc: 162, class representative: 25
|
||||||
|
105, // quitte: 99 <-> mc: 163, class representative: 30
|
||||||
|
105, // quitte: 100 <-> mc: 168, class representative: 25
|
||||||
|
105, // quitte: 101 <-> mc: 169, class representative: 30
|
||||||
|
105, // quitte: 102 <-> mc: 170, class representative: 60
|
||||||
|
105, // quitte: 103 <-> mc: 171, class representative: 25
|
||||||
|
105, // quitte: 104 <-> mc: 164, class representative: 22
|
||||||
|
105, // quitte: 105 <-> mc: 165, class representative: 105
|
||||||
|
105, // quitte: 106 <-> mc: 166, class representative: 30
|
||||||
|
105, // quitte: 107 <-> mc: 167, class representative: 22
|
||||||
|
105, // quitte: 108 <-> mc: 172, class representative: 30
|
||||||
|
105, // quitte: 109 <-> mc: 173, class representative: 22
|
||||||
|
105, // quitte: 110 <-> mc: 174, class representative: 25
|
||||||
|
105, // quitte: 111 <-> mc: 175, class representative: 6
|
||||||
|
112, // quitte: 112 <-> mc: 176, class representative: 7
|
||||||
|
113, // quitte: 113 <-> mc: 177, class representative: 23
|
||||||
|
114, // quitte: 114 <-> mc: 178, class representative: 27
|
||||||
|
115, // quitte: 115 <-> mc: 179, class representative: 7
|
||||||
|
116, // quitte: 116 <-> mc: 184, class representative: 29
|
||||||
|
117, // quitte: 117 <-> mc: 185, class representative: 7
|
||||||
|
105, // quitte: 118 <-> mc: 186, class representative: 25
|
||||||
|
119, // quitte: 119 <-> mc: 187, class representative: 3
|
||||||
|
105, // quitte: 120 <-> mc: 180, class representative: 30
|
||||||
|
105, // quitte: 121 <-> mc: 181, class representative: 22
|
||||||
|
105, // quitte: 122 <-> mc: 182, class representative: 25
|
||||||
|
105, // quitte: 123 <-> mc: 183, class representative: 6
|
||||||
|
105, // quitte: 124 <-> mc: 188, class representative: 25
|
||||||
|
105, // quitte: 125 <-> mc: 189, class representative: 6
|
||||||
|
105, // quitte: 126 <-> mc: 190, class representative: 24
|
||||||
|
127, // quitte: 127 <-> mc: 191, class representative: 1
|
||||||
|
128, // quitte: 128 <-> mc: 64, class representative: 1
|
||||||
|
105, // quitte: 129 <-> mc: 65, class representative: 24
|
||||||
|
105, // quitte: 130 <-> mc: 66, class representative: 6
|
||||||
|
105, // quitte: 131 <-> mc: 67, class representative: 25
|
||||||
|
105, // quitte: 132 <-> mc: 72, class representative: 6
|
||||||
|
105, // quitte: 133 <-> mc: 73, class representative: 25
|
||||||
|
105, // quitte: 134 <-> mc: 74, class representative: 22
|
||||||
|
105, // quitte: 135 <-> mc: 75, class representative: 30
|
||||||
|
136, // quitte: 136 <-> mc: 68, class representative: 3
|
||||||
|
105, // quitte: 137 <-> mc: 69, class representative: 25
|
||||||
|
138, // quitte: 138 <-> mc: 70, class representative: 7
|
||||||
|
139, // quitte: 139 <-> mc: 71, class representative: 29
|
||||||
|
140, // quitte: 140 <-> mc: 76, class representative: 7
|
||||||
|
141, // quitte: 141 <-> mc: 77, class representative: 27
|
||||||
|
142, // quitte: 142 <-> mc: 78, class representative: 23
|
||||||
|
143, // quitte: 143 <-> mc: 79, class representative: 7
|
||||||
|
105, // quitte: 144 <-> mc: 80, class representative: 6
|
||||||
|
105, // quitte: 145 <-> mc: 81, class representative: 25
|
||||||
|
105, // quitte: 146 <-> mc: 82, class representative: 22
|
||||||
|
105, // quitte: 147 <-> mc: 83, class representative: 30
|
||||||
|
105, // quitte: 148 <-> mc: 88, class representative: 22
|
||||||
|
105, // quitte: 149 <-> mc: 89, class representative: 30
|
||||||
|
105, // quitte: 150 <-> mc: 90, class representative: 105
|
||||||
|
105, // quitte: 151 <-> mc: 91, class representative: 22
|
||||||
|
105, // quitte: 152 <-> mc: 84, class representative: 25
|
||||||
|
105, // quitte: 153 <-> mc: 85, class representative: 60
|
||||||
|
105, // quitte: 154 <-> mc: 86, class representative: 30
|
||||||
|
105, // quitte: 155 <-> mc: 87, class representative: 25
|
||||||
|
105, // quitte: 156 <-> mc: 92, class representative: 30
|
||||||
|
105, // quitte: 157 <-> mc: 93, class representative: 25
|
||||||
|
105, // quitte: 158 <-> mc: 94, class representative: 22
|
||||||
|
105, // quitte: 159 <-> mc: 95, class representative: 6
|
||||||
|
160, // quitte: 160 <-> mc: 96, class representative: 3
|
||||||
|
105, // quitte: 161 <-> mc: 97, class representative: 25
|
||||||
|
162, // quitte: 162 <-> mc: 98, class representative: 7
|
||||||
|
163, // quitte: 163 <-> mc: 99, class representative: 27
|
||||||
|
105, // quitte: 164 <-> mc: 104, class representative: 25
|
||||||
|
105, // quitte: 165 <-> mc: 105, class representative: 60
|
||||||
|
105, // quitte: 166 <-> mc: 106, class representative: 30
|
||||||
|
105, // quitte: 167 <-> mc: 107, class representative: 25
|
||||||
|
168, // quitte: 168 <-> mc: 100, class representative: 7
|
||||||
|
105, // quitte: 169 <-> mc: 101, class representative: 30
|
||||||
|
170, // quitte: 170 <-> mc: 102, class representative: 15
|
||||||
|
171, // quitte: 171 <-> mc: 103, class representative: 7
|
||||||
|
172, // quitte: 172 <-> mc: 108, class representative: 29
|
||||||
|
105, // quitte: 173 <-> mc: 109, class representative: 25
|
||||||
|
174, // quitte: 174 <-> mc: 110, class representative: 7
|
||||||
|
175, // quitte: 175 <-> mc: 111, class representative: 3
|
||||||
|
176, // quitte: 176 <-> mc: 112, class representative: 7
|
||||||
|
177, // quitte: 177 <-> mc: 113, class representative: 29
|
||||||
|
178, // quitte: 178 <-> mc: 114, class representative: 23
|
||||||
|
179, // quitte: 179 <-> mc: 115, class representative: 7
|
||||||
|
105, // quitte: 180 <-> mc: 120, class representative: 30
|
||||||
|
105, // quitte: 181 <-> mc: 121, class representative: 25
|
||||||
|
105, // quitte: 182 <-> mc: 122, class representative: 22
|
||||||
|
105, // quitte: 183 <-> mc: 123, class representative: 6
|
||||||
|
184, // quitte: 184 <-> mc: 116, class representative: 27
|
||||||
|
105, // quitte: 185 <-> mc: 117, class representative: 25
|
||||||
|
186, // quitte: 186 <-> mc: 118, class representative: 7
|
||||||
|
187, // quitte: 187 <-> mc: 119, class representative: 3
|
||||||
|
105, // quitte: 188 <-> mc: 124, class representative: 25
|
||||||
|
105, // quitte: 189 <-> mc: 125, class representative: 24
|
||||||
|
105, // quitte: 190 <-> mc: 126, class representative: 6
|
||||||
|
191, // quitte: 191 <-> mc: 127, class representative: 1
|
||||||
|
192, // quitte: 192 <-> mc: 192, class representative: 3
|
||||||
|
105, // quitte: 193 <-> mc: 193, class representative: 25
|
||||||
|
105, // quitte: 194 <-> mc: 194, class representative: 25
|
||||||
|
105, // quitte: 195 <-> mc: 195, class representative: 60
|
||||||
|
196, // quitte: 196 <-> mc: 200, class representative: 7
|
||||||
|
197, // quitte: 197 <-> mc: 201, class representative: 29
|
||||||
|
105, // quitte: 198 <-> mc: 202, class representative: 30
|
||||||
|
105, // quitte: 199 <-> mc: 203, class representative: 25
|
||||||
|
200, // quitte: 200 <-> mc: 196, class representative: 7
|
||||||
|
105, // quitte: 201 <-> mc: 197, class representative: 30
|
||||||
|
202, // quitte: 202 <-> mc: 198, class representative: 27
|
||||||
|
105, // quitte: 203 <-> mc: 199, class representative: 25
|
||||||
|
204, // quitte: 204 <-> mc: 204, class representative: 15
|
||||||
|
205, // quitte: 205 <-> mc: 205, class representative: 7
|
||||||
|
206, // quitte: 206 <-> mc: 206, class representative: 7
|
||||||
|
207, // quitte: 207 <-> mc: 207, class representative: 3
|
||||||
|
208, // quitte: 208 <-> mc: 208, class representative: 7
|
||||||
|
209, // quitte: 209 <-> mc: 209, class representative: 27
|
||||||
|
105, // quitte: 210 <-> mc: 210, class representative: 30
|
||||||
|
105, // quitte: 211 <-> mc: 211, class representative: 25
|
||||||
|
212, // quitte: 212 <-> mc: 216, class representative: 23
|
||||||
|
213, // quitte: 213 <-> mc: 217, class representative: 7
|
||||||
|
105, // quitte: 214 <-> mc: 218, class representative: 22
|
||||||
|
105, // quitte: 215 <-> mc: 219, class representative: 6
|
||||||
|
216, // quitte: 216 <-> mc: 212, class representative: 29
|
||||||
|
105, // quitte: 217 <-> mc: 213, class representative: 25
|
||||||
|
105, // quitte: 218 <-> mc: 214, class representative: 25
|
||||||
|
105, // quitte: 219 <-> mc: 215, class representative: 24
|
||||||
|
220, // quitte: 220 <-> mc: 220, class representative: 7
|
||||||
|
221, // quitte: 221 <-> mc: 221, class representative: 3
|
||||||
|
105, // quitte: 222 <-> mc: 222, class representative: 6
|
||||||
|
223, // quitte: 223 <-> mc: 223, class representative: 1
|
||||||
|
224, // quitte: 224 <-> mc: 224, class representative: 7
|
||||||
|
105, // quitte: 225 <-> mc: 225, class representative: 30
|
||||||
|
226, // quitte: 226 <-> mc: 226, class representative: 29
|
||||||
|
105, // quitte: 227 <-> mc: 227, class representative: 25
|
||||||
|
228, // quitte: 228 <-> mc: 232, class representative: 27
|
||||||
|
105, // quitte: 229 <-> mc: 233, class representative: 25
|
||||||
|
105, // quitte: 230 <-> mc: 234, class representative: 25
|
||||||
|
105, // quitte: 231 <-> mc: 235, class representative: 24
|
||||||
|
232, // quitte: 232 <-> mc: 228, class representative: 23
|
||||||
|
105, // quitte: 233 <-> mc: 229, class representative: 22
|
||||||
|
234, // quitte: 234 <-> mc: 230, class representative: 7
|
||||||
|
105, // quitte: 235 <-> mc: 231, class representative: 6
|
||||||
|
236, // quitte: 236 <-> mc: 236, class representative: 7
|
||||||
|
105, // quitte: 237 <-> mc: 237, class representative: 6
|
||||||
|
238, // quitte: 238 <-> mc: 238, class representative: 3
|
||||||
|
239, // quitte: 239 <-> mc: 239, class representative: 1
|
||||||
|
240, // quitte: 240 <-> mc: 240, class representative: 15
|
||||||
|
241, // quitte: 241 <-> mc: 241, class representative: 7
|
||||||
|
242, // quitte: 242 <-> mc: 242, class representative: 7
|
||||||
|
243, // quitte: 243 <-> mc: 243, class representative: 3
|
||||||
|
244, // quitte: 244 <-> mc: 248, class representative: 7
|
||||||
|
245, // quitte: 245 <-> mc: 249, class representative: 3
|
||||||
|
105, // quitte: 246 <-> mc: 250, class representative: 6
|
||||||
|
247, // quitte: 247 <-> mc: 251, class representative: 1
|
||||||
|
248, // quitte: 248 <-> mc: 244, class representative: 7
|
||||||
|
105, // quitte: 249 <-> mc: 245, class representative: 6
|
||||||
|
250, // quitte: 250 <-> mc: 246, class representative: 3
|
||||||
|
251, // quitte: 251 <-> mc: 247, class representative: 1
|
||||||
|
252, // quitte: 252 <-> mc: 252, class representative: 3
|
||||||
|
253, // quitte: 253 <-> mc: 253, class representative: 1
|
||||||
|
254, // quitte: 254 <-> mc: 254, class representative: 1
|
||||||
|
255 // quitte: 255 <-> mc: 255, class representative: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The asymptotic decider can be unstable if certain corner points have similar function values.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
constexpr int asymptotic_remap[][11] = {
|
||||||
|
{0, 1, 2, 3, 4, 5, 6, 7, 0b0000, 0b0001, 0b0010}, // regular, 4th bit invert coordinates, 3th bit swap coordinates, first two bits axis of coordinates
|
||||||
|
{0, 2, 4, 6, 1, 3, 5, 7, 0b0110, 0b0100, 0b0001}, // rot 0-7
|
||||||
|
{0, 4, 1, 5, 2, 6, 3, 7, 0b0101, 0b0010, 0b0100}, // 2# rot 0-7
|
||||||
|
{3, 1, 7, 5, 2, 0, 6, 4, 0b1110, 0b1100, 0b0001}, // rot 1-6 (0, 3, 5) (2, 7, 4)
|
||||||
|
{5, 1, 4, 0, 7, 3, 6, 2, 0b1101, 0b0010, 0b1100}, // 2# rot 1-6
|
||||||
|
{3, 7, 2, 6, 1, 5, 0, 4, 0b1101, 0b1010, 0b0100}, // rot 2-5 (0, 3, 6) (1, 4, 7)
|
||||||
|
{6, 4, 2, 0, 7, 5, 3, 1, 0b0110, 0b1100, 0b1001}, // 2# rot 2-5 (0, 3, 6) (1, 4, 7)
|
||||||
|
{5, 7, 1, 3, 4, 6, 0, 2, 0b1110, 0b0100, 0b1001}, // rot 3-4 (1, 2, 7) (0, 5, 6)
|
||||||
|
{6, 2, 7, 3, 4, 0, 5, 1, 0b0101, 0b1010, 0b1100}, // 2# rot 3-4 (1, 2, 7) (0, 5, 6)
|
||||||
|
{7, 6, 5, 4, 3, 2, 1, 0, 0b1100, 0b1101, 0b1110} // mirrored regular
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Cube_table
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace Isosurfacing
|
||||||
|
} // namespace CGAL
|
||||||
|
|
||||||
|
#endif // CGAL_ISOSURFACING_3_INTERNAL_TABLES_H
|
||||||
|
|
@ -0,0 +1,241 @@
|
||||||
|
// Copyright (c) 2022-2024 INRIA Sophia-Antipolis (France), GeometryFactory (France).
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of CGAL (www.cgal.org).
|
||||||
|
//
|
||||||
|
// $URL$
|
||||||
|
// $Id$
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
|
||||||
|
//
|
||||||
|
// Author(s) : Mael Rouxel-Labbé
|
||||||
|
|
||||||
|
#ifndef CGAL_ISOSURFACING_3_INTERNAL_INTERPOLATION_SCHEMES_3_H
|
||||||
|
#define CGAL_ISOSURFACING_3_INTERNAL_INTERPOLATION_SCHEMES_3_H
|
||||||
|
|
||||||
|
#include <CGAL/license/Isosurfacing_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/number_utils.h>
|
||||||
|
#include <CGAL/enum.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace CGAL {
|
||||||
|
namespace Isosurfacing {
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \ingroup IS_Fields_helpers_grp
|
||||||
|
*
|
||||||
|
* \cgalModels{IsosurfacingInterpolationScheme_3}
|
||||||
|
*
|
||||||
|
* The class `Trilinear_interpolation` is the standard interpolation scheme
|
||||||
|
* to define continuous fields of scalar values and gradients from data defined
|
||||||
|
* only at the vertices of a %Cartesian grid.
|
||||||
|
*
|
||||||
|
* \tparam Grid must be `CGAL::Isosurfacing::Cartesian_grid_3<GeomTraits>`, with `GeomTraits`
|
||||||
|
* a model of `IsosurfacingTraits_3`
|
||||||
|
*/
|
||||||
|
template <typename Grid>
|
||||||
|
class Trilinear_interpolation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Geom_traits = typename Grid::Geom_traits;
|
||||||
|
using FT = typename Geom_traits::FT;
|
||||||
|
using Point_3 = typename Geom_traits::Point_3;
|
||||||
|
using Vector_3 = typename Geom_traits::Vector_3;
|
||||||
|
using Iso_cuboid_3 = typename Geom_traits::Iso_cuboid_3;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*!
|
||||||
|
* \brief interpolates the values at a given point using trilinear interpolation.
|
||||||
|
*
|
||||||
|
* \param p the point at which values are interpolated
|
||||||
|
* \param g the grid
|
||||||
|
* \param values the continuous field of scalar values, defined over the geometric span of `g`
|
||||||
|
*/
|
||||||
|
FT interpolated_value(const Point_3& p,
|
||||||
|
const Grid& g,
|
||||||
|
const std::vector<FT>& values) const
|
||||||
|
{
|
||||||
|
typename Geom_traits::Compute_x_3 x_coord = g.geom_traits().compute_x_3_object();
|
||||||
|
typename Geom_traits::Compute_y_3 y_coord = g.geom_traits().compute_y_3_object();
|
||||||
|
typename Geom_traits::Compute_z_3 z_coord = g.geom_traits().compute_z_3_object();
|
||||||
|
typename Geom_traits::Construct_vertex_3 vertex = g.geom_traits().construct_vertex_3_object();
|
||||||
|
|
||||||
|
// trilinear interpolation of stored values
|
||||||
|
const Iso_cuboid_3& span = g.span();
|
||||||
|
const Vector_3& spacing = g.spacing();
|
||||||
|
|
||||||
|
// calculate min index including border case
|
||||||
|
const Point_3& min_p = vertex(span, 0);
|
||||||
|
std::size_t i = std::size_t(CGAL::to_double((x_coord(p) - x_coord(min_p)) / x_coord(spacing)));
|
||||||
|
std::size_t j = std::size_t(CGAL::to_double((y_coord(p) - y_coord(min_p)) / y_coord(spacing)));
|
||||||
|
std::size_t k = std::size_t(CGAL::to_double((z_coord(p) - z_coord(min_p)) / z_coord(spacing)));
|
||||||
|
|
||||||
|
if(i == g.xdim() - 1)
|
||||||
|
--i;
|
||||||
|
if(j == g.ydim() - 1)
|
||||||
|
--j;
|
||||||
|
if(k == g.zdim() - 1)
|
||||||
|
--k;
|
||||||
|
|
||||||
|
// calculate coordinates of min index
|
||||||
|
const FT min_x = x_coord(min_p) + FT(i) * spacing[0];
|
||||||
|
const FT min_y = y_coord(min_p) + FT(j) * spacing[1];
|
||||||
|
const FT min_z = z_coord(min_p) + FT(k) * spacing[2];
|
||||||
|
|
||||||
|
// interpolation factors between 0 and 1
|
||||||
|
const FT f_i = std::clamp<FT>((x_coord(p) - min_x) / spacing[0], 0, 1);
|
||||||
|
const FT f_j = std::clamp<FT>((y_coord(p) - min_y) / spacing[1], 0, 1);
|
||||||
|
const FT f_k = std::clamp<FT>((z_coord(p) - min_z) / spacing[2], 0, 1);
|
||||||
|
|
||||||
|
// read the value at all 8 corner points
|
||||||
|
const FT g000 = values[g.linear_index(i + 0, j + 0, k + 0)];
|
||||||
|
const FT g001 = values[g.linear_index(i + 0, j + 0, k + 1)];
|
||||||
|
const FT g010 = values[g.linear_index(i + 0, j + 1, k + 0)];
|
||||||
|
const FT g011 = values[g.linear_index(i + 0, j + 1, k + 1)];
|
||||||
|
const FT g100 = values[g.linear_index(i + 1, j + 0, k + 0)];
|
||||||
|
const FT g101 = values[g.linear_index(i + 1, j + 0, k + 1)];
|
||||||
|
const FT g110 = values[g.linear_index(i + 1, j + 1, k + 0)];
|
||||||
|
const FT g111 = values[g.linear_index(i + 1, j + 1, k + 1)];
|
||||||
|
|
||||||
|
// interpolate along all axes by weighting the corner points
|
||||||
|
const FT lambda000 = (FT(1) - f_i) * (FT(1) - f_j) * (FT(1) - f_k);
|
||||||
|
const FT lambda001 = (FT(1) - f_i) * (FT(1) - f_j) * f_k;
|
||||||
|
const FT lambda010 = (FT(1) - f_i) * f_j * (FT(1) - f_k);
|
||||||
|
const FT lambda011 = (FT(1) - f_i) * f_j * f_k;
|
||||||
|
const FT lambda100 = f_i * (FT(1) - f_j) * (FT(1) - f_k);
|
||||||
|
const FT lambda101 = f_i * (FT(1) - f_j) * f_k;
|
||||||
|
const FT lambda110 = f_i * f_j * (FT(1) - f_k);
|
||||||
|
const FT lambda111 = f_i * f_j * f_k;
|
||||||
|
|
||||||
|
// add weighted corners
|
||||||
|
return g000 * lambda000 + g001 * lambda001 +
|
||||||
|
g010 * lambda010 + g011 * lambda011 +
|
||||||
|
g100 * lambda100 + g101 * lambda101 +
|
||||||
|
g110 * lambda110 + g111 * lambda111;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief interpolates the gradients at a given point using trilinear interpolation.
|
||||||
|
*
|
||||||
|
* \param p the point at which to interpolate the gradients
|
||||||
|
* \param g the grid
|
||||||
|
* \param gradients the continuous field of vector values, defined over the geometric span of `g`
|
||||||
|
*/
|
||||||
|
Vector_3 interpolated_gradient(const Point_3& p,
|
||||||
|
const Grid& g,
|
||||||
|
const std::vector<Vector_3>& gradients) const
|
||||||
|
{
|
||||||
|
typename Geom_traits::Compute_x_3 x_coord = g.geom_traits().compute_x_3_object();
|
||||||
|
typename Geom_traits::Compute_y_3 y_coord = g.geom_traits().compute_y_3_object();
|
||||||
|
typename Geom_traits::Compute_z_3 z_coord = g.geom_traits().compute_z_3_object();
|
||||||
|
typename Geom_traits::Construct_vector_3 vector = g.geom_traits().construct_vector_3_object();
|
||||||
|
typename Geom_traits::Construct_vertex_3 vertex = g.geom_traits().construct_vertex_3_object();
|
||||||
|
|
||||||
|
// trilinear interpolation of stored gradients
|
||||||
|
const Iso_cuboid_3& span = g.span();
|
||||||
|
const Vector_3& spacing = g.spacing();
|
||||||
|
|
||||||
|
// calculate min index including border case
|
||||||
|
const Point_3& min_p = vertex(span, 0);
|
||||||
|
std::size_t i = static_cast<std::size_t>((x_coord(p) - x_coord(min_p)) / x_coord(spacing));
|
||||||
|
std::size_t j = static_cast<std::size_t>((y_coord(p) - y_coord(min_p)) / y_coord(spacing));
|
||||||
|
std::size_t k = static_cast<std::size_t>((z_coord(p) - z_coord(min_p)) / z_coord(spacing));
|
||||||
|
|
||||||
|
if(i == g.xdim() - 1) // dim is the point number
|
||||||
|
--i;
|
||||||
|
if(j == g.ydim() - 1)
|
||||||
|
--j;
|
||||||
|
if(k == g.zdim() - 1)
|
||||||
|
--k;
|
||||||
|
|
||||||
|
// calculate coordinates of min index
|
||||||
|
const FT min_x = i * spacing[0] + x_coord(min_p);
|
||||||
|
const FT min_y = j * spacing[1] + y_coord(min_p);
|
||||||
|
const FT min_z = k * spacing[2] + z_coord(min_p);
|
||||||
|
|
||||||
|
// interpolation factors between 0 and 1
|
||||||
|
const FT f_i = std::clamp<FT>((x_coord(p) - min_x) / spacing[0], 0, 1);
|
||||||
|
const FT f_j = std::clamp<FT>((y_coord(p) - min_y) / spacing[1], 0, 1);
|
||||||
|
const FT f_k = std::clamp<FT>((z_coord(p) - min_z) / spacing[2], 0, 1);
|
||||||
|
|
||||||
|
// read the value at all 8 corner points
|
||||||
|
const Vector_3& g000 = gradients[g.linear_index(i + 0, j + 0, k + 0)];
|
||||||
|
const Vector_3& g001 = gradients[g.linear_index(i + 0, j + 0, k + 1)];
|
||||||
|
const Vector_3& g010 = gradients[g.linear_index(i + 0, j + 1, k + 0)];
|
||||||
|
const Vector_3& g011 = gradients[g.linear_index(i + 0, j + 1, k + 1)];
|
||||||
|
const Vector_3& g100 = gradients[g.linear_index(i + 1, j + 0, k + 0)];
|
||||||
|
const Vector_3& g101 = gradients[g.linear_index(i + 1, j + 0, k + 1)];
|
||||||
|
const Vector_3& g110 = gradients[g.linear_index(i + 1, j + 1, k + 0)];
|
||||||
|
const Vector_3& g111 = gradients[g.linear_index(i + 1, j + 1, k + 1)];
|
||||||
|
|
||||||
|
// interpolate along all axes by weighting the corner points
|
||||||
|
const FT lambda000 = (FT(1) - f_i) * (FT(1) - f_j) * (FT(1) - f_k);
|
||||||
|
const FT lambda001 = (FT(1) - f_i) * (FT(1) - f_j) * f_k;
|
||||||
|
const FT lambda010 = (FT(1) - f_i) * f_j * (FT(1) - f_k);
|
||||||
|
const FT lambda011 = (FT(1) - f_i) * f_j * f_k;
|
||||||
|
const FT lambda100 = f_i * (FT(1) - f_j) * (FT(1) - f_k);
|
||||||
|
const FT lambda101 = f_i * (FT(1) - f_j) * f_k;
|
||||||
|
const FT lambda110 = f_i * f_j * (FT(1) - f_k);
|
||||||
|
const FT lambda111 = f_i * f_j * f_k;
|
||||||
|
|
||||||
|
// add weighted corners
|
||||||
|
return vector(x_coord(g000) * lambda000 + x_coord(g001) * lambda001 +
|
||||||
|
x_coord(g010) * lambda010 + x_coord(g011) * lambda011 +
|
||||||
|
x_coord(g100) * lambda100 + x_coord(g101) * lambda101 +
|
||||||
|
x_coord(g110) * lambda110 + x_coord(g111) * lambda111,
|
||||||
|
y_coord(g000) * lambda000 + y_coord(g001) * lambda001 +
|
||||||
|
y_coord(g010) * lambda010 + y_coord(g011) * lambda011 +
|
||||||
|
y_coord(g100) * lambda100 + y_coord(g101) * lambda101 +
|
||||||
|
y_coord(g110) * lambda110 + y_coord(g111) * lambda111,
|
||||||
|
z_coord(g000) * lambda000 + z_coord(g001) * lambda001 +
|
||||||
|
z_coord(g010) * lambda010 + z_coord(g011) * lambda011 +
|
||||||
|
z_coord(g100) * lambda100 + z_coord(g101) * lambda101 +
|
||||||
|
z_coord(g110) * lambda110 + z_coord(g111) * lambda111);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef DOXYGEN_RUNNING
|
||||||
|
// [undocumented]
|
||||||
|
//
|
||||||
|
// \ingroup IS_Fields_helpers_grp
|
||||||
|
//
|
||||||
|
// This can be used for example when we have implicit functions for data (values & gradients),
|
||||||
|
// but use an interpolated values field as to store data.
|
||||||
|
template <typename Grid>
|
||||||
|
class Function_evaluation
|
||||||
|
{
|
||||||
|
using Geom_traits = typename Grid::Geom_traits;
|
||||||
|
using FT = typename Geom_traits::FT;
|
||||||
|
using Point_3 = typename Geom_traits::Point_3;
|
||||||
|
using Vector_3 = typename Geom_traits::Vector_3;
|
||||||
|
|
||||||
|
std::function<FT(const Point_3&)> m_value_fn;
|
||||||
|
std::function<Vector_3(const Point_3&)> m_gradient_fn;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <typename ValueFunction, typename GradientFunction>
|
||||||
|
Function_evaluation(const ValueFunction& value_fn,
|
||||||
|
const GradientFunction& gradient_fn = [](const Point_3&) -> Vector_3 { return CGAL::NULL_VECTOR; })
|
||||||
|
: m_value_fn{value_fn},
|
||||||
|
m_gradient_fn{gradient_fn}
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public:
|
||||||
|
FT interpolated_value(const Point_3& p, const Grid&, const std::vector<FT>&) const
|
||||||
|
{
|
||||||
|
return m_value_fn(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector_3 interpolated_gradient(const Point_3& p, const Grid&, const std::vector<Vector_3>&) const
|
||||||
|
{
|
||||||
|
return m_gradient_fn(p);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace Isosurfacing
|
||||||
|
} // namespace CGAL
|
||||||
|
|
||||||
|
#endif // CGAL_ISOSURFACING_3_INTERNAL_INTERPOLATION_SCHEMES_3_H
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
// Copyright (c) 2022-2024 INRIA Sophia-Antipolis (France), GeometryFactory (France).
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of CGAL (www.cgal.org).
|
||||||
|
//
|
||||||
|
// $URL$
|
||||||
|
// $Id$
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
|
||||||
|
//
|
||||||
|
// Author(s) : Julian Stahl
|
||||||
|
// Mael Rouxel-Labbé
|
||||||
|
|
||||||
|
#ifndef CGAL_ISOSURFACING_3_MARCHING_CUBES_3_H
|
||||||
|
#define CGAL_ISOSURFACING_3_MARCHING_CUBES_3_H
|
||||||
|
|
||||||
|
#include <CGAL/license/Isosurfacing_3.h>
|
||||||
|
|
||||||
|
#include <CGAL/Isosurfacing_3/internal/marching_cubes_functors.h>
|
||||||
|
#include <CGAL/Isosurfacing_3/internal/topologically_correct_marching_cubes_functors.h>
|
||||||
|
|
||||||
|
#include <CGAL/Named_function_parameters.h>
|
||||||
|
#include <CGAL/tags.h>
|
||||||
|
|
||||||
|
namespace CGAL {
|
||||||
|
namespace Isosurfacing {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup IS_Methods_grp
|
||||||
|
*
|
||||||
|
* \brief creates a triangle soup that represents an isosurface generated by the Marching Cubes algorithm.
|
||||||
|
*
|
||||||
|
* \tparam ConcurrencyTag enables sequential versus parallel algorithm.
|
||||||
|
* Possible values are `Sequential_tag`, `Parallel_if_available_tag`, or `Parallel_tag`.
|
||||||
|
* \tparam Domain must be a model of `IsosurfacingDomain_3`.
|
||||||
|
* \tparam PointRange must be a model of the concepts `RandomAccessContainer` and `BackInsertionSequence`
|
||||||
|
* whose value type can be constructed from the point type of the domain.
|
||||||
|
* \tparam TriangleRange must be a model of the concepts `RandomAccessContainer` and `BackInsertionSequence`
|
||||||
|
* whose value type is itself a model of the concepts `RandomAccessContainer`
|
||||||
|
* and `BackInsertionSequence` whose value type is `std::size_t`.
|
||||||
|
* \tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters"
|
||||||
|
*
|
||||||
|
* \param domain the domain providing the spatial partition and the data
|
||||||
|
* \param isovalue the value defining the isosurface
|
||||||
|
* \param points the points of the triangles in the output triangle soup
|
||||||
|
* \param triangles the faces of the output triangle soup. Each element in the vector describes a triangle using the indices of the points in `points`.
|
||||||
|
* \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below
|
||||||
|
*
|
||||||
|
* \cgalNamedParamsBegin
|
||||||
|
* \cgalParamNBegin{use_topologically_correct_marching_cubes}
|
||||||
|
* \cgalParamDescription{whether the topologically correct variant of Marching Cubes \cgalCite{cgal:g-ctcmi-16} should be used, or the base version \cgalCite{cgal:c-mcctci-95}.}
|
||||||
|
* \cgalParamType{Boolean}
|
||||||
|
* \cgalParamDefault{`true`}
|
||||||
|
* \cgalParamNEnd
|
||||||
|
*
|
||||||
|
* \cond SKIP_IN_MANUAL
|
||||||
|
* \cgalParamNBegin{isovalue_nudging}
|
||||||
|
* \cgalParamDescription{snapping of function value at corner points to an epsilon below the isovalue if the function value is within an epsilon range around the isovalue}
|
||||||
|
* \cgalParamType{Boolean}
|
||||||
|
* \cgalParamDefault{`true`}
|
||||||
|
* \cgalParamExtra{Snapping the function value at corner points prevents singular cases. The geometry is changed only in case of the function value being within a small epsilon range. The potential change is small. }
|
||||||
|
* \cgalParamNEnd
|
||||||
|
*
|
||||||
|
* \cgalParamNBegin{constrain_to_cell}
|
||||||
|
* \cgalParamDescription{whether to exclude the cell boundary from the interior vertex placement. }
|
||||||
|
* \cgalParamType{Boolean}
|
||||||
|
* \cgalParamDefault{`true`}
|
||||||
|
* \cgalParamExtra{Prevents degenerate or duplicate triangles.}
|
||||||
|
* \cgalParamNEnd
|
||||||
|
* \endcond
|
||||||
|
*
|
||||||
|
* \cgalNamedParamsEnd
|
||||||
|
*
|
||||||
|
* \sa `CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh()`
|
||||||
|
*/
|
||||||
|
template <typename ConcurrencyTag = CGAL::Sequential_tag,
|
||||||
|
typename Domain,
|
||||||
|
typename PointRange,
|
||||||
|
typename TriangleRange,
|
||||||
|
typename NamedParameters = parameters::Default_named_parameters>
|
||||||
|
void marching_cubes(const Domain& domain,
|
||||||
|
const typename Domain::Geom_traits::FT isovalue,
|
||||||
|
PointRange& points,
|
||||||
|
TriangleRange& triangles,
|
||||||
|
const NamedParameters& np = parameters::default_values())
|
||||||
|
{
|
||||||
|
using parameters::choose_parameter;
|
||||||
|
using parameters::get_parameter;
|
||||||
|
|
||||||
|
const bool use_tmc = choose_parameter(get_parameter(np, internal_np::use_topologically_correct_marching_cubes), true);
|
||||||
|
const bool isovalue_nudging = choose_parameter(get_parameter(np, internal_np::isovalue_nudging), true);
|
||||||
|
const bool constrain_to_cell = choose_parameter(get_parameter(np, internal_np::constrain_to_cell), true);
|
||||||
|
|
||||||
|
if(use_tmc)
|
||||||
|
{
|
||||||
|
internal::TMC_functor<Domain, PointRange, TriangleRange> functor(domain, isovalue, isovalue_nudging, constrain_to_cell);
|
||||||
|
domain.template for_each_cell<ConcurrencyTag>(functor);
|
||||||
|
internal::triangles_to_polygon_soup(functor.triangles(), points, triangles);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// run marching cubes
|
||||||
|
internal::Marching_cubes_3<Domain> functor(domain, isovalue, isovalue_nudging);
|
||||||
|
domain.template for_each_cell<ConcurrencyTag>(functor);
|
||||||
|
internal::triangles_to_polygon_soup(functor.triangles(), points, triangles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Isosurfacing
|
||||||
|
} // namespace CGAL
|
||||||
|
|
||||||
|
#endif // CGAL_ISOSURFACING_3_MARCHING_CUBES_3_H
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
Inria Sophia-Antipolis (France)
|
||||||
|
GeometryFactory
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
Algebraic_foundations
|
||||||
|
BGL
|
||||||
|
CGAL_ImageIO
|
||||||
|
Cartesian_kernel
|
||||||
|
Circulator
|
||||||
|
Distance_2
|
||||||
|
Distance_3
|
||||||
|
Installation
|
||||||
|
Intersections_2
|
||||||
|
Intersections_3
|
||||||
|
Interval_support
|
||||||
|
Isosurfacing_3
|
||||||
|
Kernel_23
|
||||||
|
Modular_arithmetic
|
||||||
|
Number_types
|
||||||
|
Orthtree
|
||||||
|
Profiling_tools
|
||||||
|
Property_map
|
||||||
|
STL_Extension
|
||||||
|
Solver_interface
|
||||||
|
Stream_support
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
The 3D Isosurfacing package provides several isosurfacing algorithms (marching cubes, dual contouring) to generate triangle and quadrangle surface meshes from an input 3D domain and a user-defined isovalue.
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
GPL (v3 or later)
|
||||||
|
GPL (v3 or later) AND MIT/X11 (BSD like)
|
||||||