mirror of https://github.com/CGAL/cgal
360 lines
11 KiB
C++
360 lines
11 KiB
C++
// Copyright (c) 2023 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) : Jane Tournois
|
|
//
|
|
//******************************************************************************
|
|
// File Description : Defines a sizing field adapted to a triangulation
|
|
//******************************************************************************
|
|
|
|
#ifndef CGAL_TETRAHEDRAL_REMESHING_ADAPTIVE_SIZING_FIELD_H
|
|
#define CGAL_TETRAHEDRAL_REMESHING_ADAPTIVE_SIZING_FIELD_H
|
|
|
|
#include <CGAL/license/Tetrahedral_remeshing.h>
|
|
|
|
#include <CGAL/Tetrahedral_remeshing_sizing_field.h>
|
|
|
|
#include <CGAL/Search_traits_3.h>
|
|
#include <CGAL/Search_traits_adapter.h>
|
|
#include <CGAL/Orthogonal_k_neighbor_search.h>
|
|
|
|
#include <CGAL/boost/graph/named_params_helper.h>
|
|
|
|
#include <CGAL/property_map.h>
|
|
|
|
#include <CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h>
|
|
#include <CGAL/Tetrahedral_remeshing/internal/property_maps.h>
|
|
|
|
#include <vector>
|
|
#include <array>
|
|
|
|
|
|
namespace CGAL
|
|
{
|
|
|
|
/**
|
|
* @ingroup PkgTetrahedralRemeshingSizing
|
|
*
|
|
* @class Adaptive_remeshing_sizing_field
|
|
* @tparam Tr underlying triangulation type
|
|
*
|
|
* A sizing field for tetrahedral remeshing,
|
|
* that keeps the same mesh density throughout the remeshing process.
|
|
*
|
|
* \cgalModels{RemeshingSizingField_3}
|
|
*/
|
|
template <typename Tr>
|
|
class Adaptive_remeshing_sizing_field
|
|
: public Tetrahedral_remeshing_sizing_field<typename Tr::Geom_traits>
|
|
{
|
|
// Types
|
|
public:
|
|
typedef typename Tr::Geom_traits GT;
|
|
typedef typename GT::FT FT;
|
|
typedef typename Tr::Geom_traits::Point_3 Point_3; //Bare_point
|
|
typedef typename Tr::Vertex::Index Index;
|
|
|
|
private:
|
|
typedef typename Tr::Point Tr_point;
|
|
typedef typename Tr::Vertex_handle Vertex_handle;
|
|
typedef typename Tr::Cell_handle Cell_handle;
|
|
|
|
struct Point_with_info
|
|
{
|
|
Point_3 p;
|
|
FT size;
|
|
int dimension;
|
|
};
|
|
|
|
private:
|
|
struct Point_property_map
|
|
{
|
|
using Self = Point_property_map;
|
|
using value_type = Point_3;
|
|
using reference = value_type; //TODO : why can't that be value_type& ?
|
|
using key_type = Point_with_info;
|
|
using category = boost::readable_property_map_tag;
|
|
|
|
const value_type operator[](const key_type& pwi) const { return pwi.p; }
|
|
friend const value_type get(const Self&, const key_type& pwi) { return pwi.p; }
|
|
};
|
|
|
|
private:
|
|
using Kd_traits = CGAL::Search_traits_adapter<Point_with_info,
|
|
Point_property_map,
|
|
CGAL::Search_traits_3<GT> >;
|
|
using Neighbor_search = CGAL::Orthogonal_k_neighbor_search<Kd_traits>;
|
|
using Kd_tree = typename Neighbor_search::Tree;
|
|
using Distance = typename Neighbor_search::Distance;
|
|
using Splitter = typename Neighbor_search::Splitter;
|
|
|
|
#ifndef DOXYGEN_RUNNING
|
|
public:
|
|
template<typename ECMap, typename FCMap, typename CellSelector>
|
|
Adaptive_remeshing_sizing_field(const Tr& tr,
|
|
const ECMap& ecmap,
|
|
const FCMap& fcmap,
|
|
const CellSelector& cell_selector)
|
|
: m_kd_tree(points_with_info(tr, ecmap, fcmap, cell_selector),
|
|
Splitter(),
|
|
Kd_traits(Point_property_map()))
|
|
{
|
|
m_kd_tree.build();
|
|
}
|
|
#endif
|
|
|
|
private:
|
|
|
|
template<typename ECMap, typename FCMap, typename CellSelector>
|
|
std::vector<Point_with_info> points_with_info(const Tr& tr,
|
|
const ECMap& ecmap,
|
|
const FCMap& fcmap,
|
|
const CellSelector& cell_selector) const
|
|
{
|
|
auto cp = tr.geom_traits().construct_point_3_object();
|
|
std::vector<Point_with_info> points;
|
|
for (const Vertex_handle v : tr.finite_vertex_handles())
|
|
{
|
|
points.push_back(
|
|
Point_with_info{ cp(tr.point(v)),
|
|
average_edge_length_around(v, tr, ecmap, fcmap, cell_selector),
|
|
v->in_dimension() });
|
|
}
|
|
return points;
|
|
}
|
|
|
|
public:
|
|
/**
|
|
* Returns size at point `p`, assumed to be included in the input
|
|
* subcomplex with dimension `dim` and index `index`.
|
|
*/
|
|
#ifdef DOXYGEN_RUNNING
|
|
FT operator()(const Point_3& p, const int& dim, const Index& index) const
|
|
#else
|
|
FT operator()(const Point_3& p, const int& dim, const Index& ) const
|
|
#endif
|
|
{
|
|
const int nb_neighbors = (dim == 3) ? 20 : 6;
|
|
|
|
// Find nearest vertex and local size before remeshing
|
|
Point_property_map pp_map;
|
|
Distance dist(pp_map);
|
|
Neighbor_search search(m_kd_tree,
|
|
p, //query point
|
|
nb_neighbors, //nb nearest neighbors
|
|
0, //epsilon
|
|
true, //search nearest
|
|
dist);
|
|
|
|
FT sum = 0;
|
|
for (const auto& neighbor : search)
|
|
{
|
|
[[maybe_unused]] const auto& [pi, size, dimension] = neighbor.first;
|
|
// todo : should we discard points when dim != dimension?
|
|
|
|
sum += size;
|
|
}
|
|
|
|
CGAL_assertion(sum > 0);
|
|
return sum / static_cast<FT>(nb_neighbors);
|
|
}
|
|
|
|
private:
|
|
/**
|
|
* Returns size at point `p`, by interpolation into tetrahedron.
|
|
* TODO : interpolate
|
|
*/
|
|
FT interpolate_on_four_vertices(
|
|
const Point_3& p,
|
|
const std::array<Point_with_info, 4>& vertices) const;
|
|
|
|
template<typename ECMap, typename FCMap, typename CellSelector>
|
|
FT average_edge_length_around(const Vertex_handle v, const Tr& tr,
|
|
const ECMap& ecmap, const FCMap& fcmap, const CellSelector& cell_selector) const;
|
|
|
|
private:
|
|
Kd_tree m_kd_tree;
|
|
|
|
};//end of class Adaptive_remeshing_sizing_field
|
|
|
|
/*!
|
|
* @ingroup PkgTetrahedralRemeshingSizing
|
|
*
|
|
* @brief Create an adaptive sizing field for tetrahedral remeshing
|
|
*
|
|
* \relates Adaptive_remeshing_sizing_field
|
|
*
|
|
* This method is a <em>named constructor</em>. It constructs a
|
|
* sizing field of type `Adaptive_remeshing_sizing_field<Tr>`,
|
|
* designed to keep the density unchanged throughout the remeshing
|
|
* process.
|
|
*
|
|
* @returns an `Adaptive_remeshing_sizing_field<Tr>`
|
|
*
|
|
* \tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters"
|
|
* \param tr the input triangulation
|
|
* \param np an optional sequence of \ref bgl_namedparameters "Named Parameters"
|
|
* among the ones listed below. All of them must be the same as the ones
|
|
* given to `CGAL::tetrahedral_isotropic_remeshing()`.
|
|
*
|
|
*\cgalNamedParamsBegin
|
|
* \cgalParamNBegin{ edge_is_constrained_map }
|
|
* \cgalParamDescription{a property map containing the constrained-or-not
|
|
* status of each edge of `tr`.}
|
|
* \cgalParamDefault{a default property map where no edge is constrained}
|
|
* \cgalParamNEnd
|
|
* \cgalParamNBegin{ facet_is_constrained_map }
|
|
* \cgalParamDescription{a property map containing the constrained-or-not
|
|
* status of each facet of `tr`.}
|
|
* \cgalParamDefault{a default property map where no facet is constrained}
|
|
* \cgalParamNEnd
|
|
* \cgalParamNBegin{ cell_is_selected_map }
|
|
* \cgalParamDescription{a property map containing the selected
|
|
* - or - not status for each cell of `tr` for remeshing.}
|
|
* \cgalParamDefault{a default property map where all cells of the domain are selected}
|
|
* \cgalParamNEnd
|
|
*\cgalNamedParamsEnd
|
|
*/
|
|
template<typename Tr, typename CGAL_NP_TEMPLATE_PARAMETERS>
|
|
Adaptive_remeshing_sizing_field<Tr>
|
|
create_adaptive_remeshing_sizing_field(const Tr& tr,
|
|
const CGAL_NP_CLASS& np = parameters::default_values())
|
|
{
|
|
using parameters::choose_parameter;
|
|
using parameters::get_parameter;
|
|
|
|
using V = typename Tr::Vertex_handle;
|
|
using Edge_vv = std::pair<V, V>;
|
|
using Facet = typename Tr::Facet;
|
|
|
|
auto ecmap = choose_parameter(get_parameter(np, internal_np::edge_is_constrained),
|
|
CGAL::Constant_property_map<Edge_vv, bool>(false));
|
|
auto fcmap = choose_parameter(get_parameter(np, internal_np::facet_is_constrained),
|
|
CGAL::Constant_property_map<Facet, bool>(false));
|
|
auto cell_selector = choose_parameter(get_parameter(np, internal_np::cell_selector),
|
|
CGAL::Tetrahedral_remeshing::internal::All_cells_selected<Tr>());
|
|
|
|
return Adaptive_remeshing_sizing_field<Tr>(tr, ecmap, fcmap, cell_selector);
|
|
}
|
|
|
|
|
|
template <typename Tr>
|
|
typename Adaptive_remeshing_sizing_field<Tr>::FT
|
|
Adaptive_remeshing_sizing_field<Tr>::
|
|
interpolate_on_four_vertices(
|
|
const Point_3& p,
|
|
const std::array<Point_with_info, 4>& vertices) const
|
|
{
|
|
// Interpolate value using values at vertices
|
|
const FT& va = vertices[0].size;
|
|
const FT& vb = vertices[1].size;
|
|
const FT& vc = vertices[2].size;
|
|
const FT& vd = vertices[3].size;
|
|
|
|
const Point_3& a = vertices[0].p;
|
|
const Point_3& b = vertices[1].p;
|
|
const Point_3& c = vertices[2].p;
|
|
const Point_3& d = vertices[3].p;
|
|
|
|
const auto sqd = FT().compute_squared_distance_3_object();
|
|
|
|
const FT wa = 1. / sqd(a, p);
|
|
const FT wb = 1. / sqd(b, p);
|
|
const FT wc = 1. / sqd(c, p);
|
|
const FT wd = 1. / sqd(d, p);
|
|
|
|
// If den is 0, then compute the average value
|
|
if (is_zero(wa + wb + wc + wd))
|
|
return (va + vb + vc + vd) / 4.;
|
|
else
|
|
return (wa * va + wb * vb + wc * vc + wd * vd) / (wa + wb + wc + wd);
|
|
}
|
|
|
|
template <typename Tr>
|
|
template <typename ECMap, typename FCMap, typename CellSelector>
|
|
typename Adaptive_remeshing_sizing_field<Tr>::FT
|
|
Adaptive_remeshing_sizing_field<Tr>::
|
|
average_edge_length_around(const Vertex_handle v, const Tr& tr,
|
|
const ECMap& ecmap, const FCMap& fcmap,
|
|
const CellSelector& cell_selector) const
|
|
{
|
|
using Edge = typename Tr::Edge;
|
|
using Facet = typename Tr::Facet;
|
|
using namespace CGAL::Tetrahedral_remeshing;
|
|
|
|
std::vector<Edge> tmp_edges;
|
|
tr.incident_edges(v, std::back_inserter(tmp_edges));
|
|
|
|
std::vector<Edge> edges;
|
|
switch (v->in_dimension())
|
|
{
|
|
case 3:
|
|
std::copy_if(tmp_edges.begin(), tmp_edges.end(),
|
|
std::back_inserter(edges),
|
|
[&tr, &cell_selector](const Edge& e)
|
|
{
|
|
return is_selected(e, tr, cell_selector);
|
|
});
|
|
break;
|
|
|
|
case 2:
|
|
std::copy_if(tmp_edges.begin(), tmp_edges.end(),
|
|
std::back_inserter(edges),
|
|
[&fcmap, &cell_selector, &tr](const Edge& e)
|
|
{
|
|
auto fcirc = tr.incident_facets(e);
|
|
const auto fend = fcirc;
|
|
do
|
|
{
|
|
const Facet& f = *fcirc;
|
|
if ( get(fcmap, f)
|
|
|| get(cell_selector, f.first) != get(cell_selector, f.first->neighbor(f.second)))
|
|
return true;
|
|
} while (++fcirc != fend);
|
|
|
|
return false;
|
|
});
|
|
break;
|
|
|
|
case 1:
|
|
case 0:
|
|
std::copy_if(tmp_edges.begin(), tmp_edges.end(),
|
|
std::back_inserter(edges),
|
|
[&ecmap](const Edge& e)
|
|
{
|
|
const auto evv = make_vertex_pair(e);
|
|
return get(ecmap, evv);
|
|
});
|
|
break;
|
|
|
|
default: // could be -1 because of far points
|
|
break;
|
|
}
|
|
|
|
CGAL_assertion(!edges.empty());
|
|
if (edges.empty())
|
|
return 0;
|
|
|
|
auto cp = tr.geom_traits().construct_point_3_object();
|
|
FT sum = 0.;
|
|
for (const Edge& e : edges)
|
|
{
|
|
sum += CGAL::approximate_sqrt(
|
|
CGAL::squared_distance(cp(e.first->vertex(e.second)->point()),
|
|
cp(e.first->vertex(e.third)->point())));;
|
|
}
|
|
|
|
return sum / static_cast<FT>(edges.size());
|
|
}
|
|
|
|
} //namespace CGAL
|
|
|
|
#endif // CGAL_TETRAHEDRAL_REMESHING_ADAPTIVE_SIZING_FIELD_H
|