mirror of https://github.com/CGAL/cgal
store average edge length in the kd-tree of adaptive sizing field
this replaces circumradius and makes it more reliable for remeshing
This commit is contained in:
parent
8be3e34c59
commit
d750394de1
|
|
@ -61,7 +61,8 @@ int main(int argc, char* argv[])
|
|||
// Mesh criteria
|
||||
Mesh_criteria criteria(facet_angle = 25,
|
||||
facet_distance = 0.2,
|
||||
cell_radius_edge_ratio = 3);
|
||||
cell_size = 10.,
|
||||
cell_radius_edge_ratio = 3.);
|
||||
|
||||
// Mesh generation
|
||||
C3t3 c3t3 = CGAL::make_mesh_3<C3t3>(domain, criteria, no_perturb().no_exude());
|
||||
|
|
@ -83,12 +84,12 @@ int main(int argc, char* argv[])
|
|||
std::cout << "Remeshing...";
|
||||
std::cout.flush();
|
||||
|
||||
CGAL::Tetrahedral_remeshing::Adaptive_remeshing_sizing_field<T3>
|
||||
adaptive_field(tr);
|
||||
using Adaptive_SF = CGAL::Tetrahedral_remeshing::Adaptive_remeshing_sizing_field<T3>;
|
||||
|
||||
CGAL::tetrahedral_isotropic_remeshing(tr,
|
||||
adaptive_field,
|
||||
CGAL::parameters::number_of_iterations(5));
|
||||
Adaptive_SF::create_adaptive_sizing_field(tr),
|
||||
CGAL::parameters::number_of_iterations(5)
|
||||
.nb_flip_smooth_iterations(10));
|
||||
|
||||
std::cout << "\rRemeshing done." << std::endl;
|
||||
|
||||
|
|
|
|||
|
|
@ -25,11 +25,12 @@
|
|||
#include <CGAL/Search_traits_adapter.h>
|
||||
#include <CGAL/Orthogonal_k_neighbor_search.h>
|
||||
|
||||
#include <CGAL/AABB_tree.h>
|
||||
#include <CGAL/AABB_traits.h>
|
||||
#include <CGAL/AABB_triangle_primitive.h>
|
||||
#include <CGAL/boost/graph/named_params_helper.h>
|
||||
|
||||
#include <CGAL/Tetrahedral_remeshing/internal/Medial_axis_kd_tree.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>
|
||||
|
|
@ -43,8 +44,6 @@ namespace Tetrahedral_remeshing
|
|||
/**
|
||||
* @class Adaptive_remeshing_sizing_field
|
||||
* @tparam Tr a triangulation
|
||||
*
|
||||
* @todo add template parameter to take an input aabb_tree
|
||||
*/
|
||||
template <typename Tr>
|
||||
class Adaptive_remeshing_sizing_field
|
||||
|
|
@ -88,38 +87,38 @@ private:
|
|||
using Distance = typename Neighbor_search::Distance;
|
||||
using Splitter = typename Neighbor_search::Splitter;
|
||||
|
||||
using Medial_axis_kd_tree = internal::Medial_axis_kd_tree<Tr>;
|
||||
|
||||
using Triangle_vec = std::vector<typename Tr::Triangle>;
|
||||
using Triangle_iter = typename Triangle_vec::iterator;
|
||||
using Triangle_primitive = CGAL::AABB_triangle_primitive<GT, Triangle_iter>;
|
||||
using AABB_triangle_traits = CGAL::AABB_traits<GT, Triangle_primitive>;
|
||||
using AABB_triangle_tree = CGAL::AABB_tree<AABB_triangle_traits>;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Adaptive_remeshing_sizing_field(const Tr& tr)
|
||||
protected:
|
||||
template<typename VCMap, typename ECMap, typename FCMap, typename CellSelector>
|
||||
Adaptive_remeshing_sizing_field(const Tr& tr,
|
||||
const VCMap& vcmap,
|
||||
const ECMap& ecmap,
|
||||
const FCMap& fcmap,
|
||||
const CellSelector& cell_selector)
|
||||
: m_gt(tr.geom_traits())
|
||||
, m_kd_tree(points_with_info(tr), Splitter(), Kd_traits(Point_property_map()))
|
||||
, m_k_lipschitz(0.5)
|
||||
, m_medial_axis_kd_tree(tr)
|
||||
, m_kd_tree(points_with_info(tr, vcmap, ecmap, fcmap, cell_selector),
|
||||
Splitter(),
|
||||
Kd_traits(Point_property_map()))
|
||||
{
|
||||
m_kd_tree.build();
|
||||
build_aabb_trees(tr);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Point_with_info> points_with_info(const Tr& tr) const
|
||||
|
||||
template<typename VCMap, typename ECMap, typename FCMap, typename CellSelector>
|
||||
std::vector<Point_with_info> points_with_info(const Tr& tr,
|
||||
const VCMap& vcmap,
|
||||
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_circumradius_around(v, tr),
|
||||
v->in_dimension() });
|
||||
points.push_back(
|
||||
Point_with_info{ cp(tr.point(v)),
|
||||
average_edge_length_around(v, tr, vcmap, ecmap, fcmap, cell_selector),
|
||||
v->in_dimension() });
|
||||
}
|
||||
return points;
|
||||
}
|
||||
|
|
@ -131,120 +130,74 @@ public:
|
|||
template <typename Index>
|
||||
FT operator()(const Bare_point& p, const int& dim, const Index& i) const
|
||||
{
|
||||
// if(dim < 3)
|
||||
// return FT(0);//automatically adapt to 3D sizing around the point
|
||||
const int nb_neighbors = 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
|
||||
1, //nb nearest neighbors
|
||||
nb_neighbors, //nb nearest neighbors
|
||||
0, //epsilon
|
||||
true, //search nearest
|
||||
dist);
|
||||
const auto [pi, size, dimension] = search.begin()->first;
|
||||
|
||||
// measure distance to input surfaces
|
||||
Bare_point closest_point = p;
|
||||
FT shortest_distance = (std::numeric_limits<FT>::max)();
|
||||
Surface_patch_index closest_patch{};
|
||||
for(std::size_t i = 0; i < m_aabb_trees.size(); ++i)
|
||||
FT sum = 0;
|
||||
for (const auto& neighbor : search)
|
||||
{
|
||||
const Bare_point closest = m_aabb_trees[i].closest_point(p);
|
||||
const FT sq_dist = m_gt.compute_squared_distance_3_object()(p, closest);
|
||||
if(sq_dist < shortest_distance)
|
||||
{
|
||||
shortest_distance = sq_dist;
|
||||
closest_point = closest;
|
||||
closest_patch = m_i2p[i];
|
||||
}
|
||||
const auto& [pi, size, dimension] = neighbor.first;
|
||||
// todo : should we discard points when dim != dimension?
|
||||
|
||||
sum += size;
|
||||
}
|
||||
shortest_distance = CGAL::approximate_sqrt(shortest_distance);
|
||||
|
||||
FT lfs = m_medial_axis_kd_tree.distance_to_medial_axis(p);// closest_point);
|
||||
FT res = m_k_lipschitz * shortest_distance + lfs;
|
||||
|
||||
// std::cout << "res = " << res
|
||||
// << "\tshortest_distance = " << shortest_distance
|
||||
// << "\tlfs = " << lfs << std::endl;
|
||||
|
||||
const FT min_size = 0.5;
|
||||
return (std::max)(res, min_size);
|
||||
CGAL_assertion(sum > 0);
|
||||
return sum / static_cast<FT>(nb_neighbors);
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Fills sizing field, using size associated to points in `tr_`
|
||||
*/
|
||||
auto build_kd_tree(const Tr& tr);
|
||||
|
||||
/**
|
||||
* Fills aabb trees, using triangles in `tr_`, for projection to input surfaces
|
||||
*/
|
||||
void build_aabb_trees(const Tr& tr);
|
||||
|
||||
/**
|
||||
* Returns size at point `p`, by interpolation into tetrahedron.
|
||||
* TODO : interpolate
|
||||
*/
|
||||
FT interpolate_on_four_vertices(
|
||||
const Bare_point& p,
|
||||
const std::array<Point_with_info, 4>& vertices) const;
|
||||
|
||||
FT sq_circumradius_length(const Cell_handle cell, const Vertex_handle v, const Tr& tr) const;
|
||||
FT average_circumradius_around(const Vertex_handle v, const Tr& tr) const;
|
||||
template<typename VCMap, typename ECMap, typename FCMap, typename CellSelector>
|
||||
FT average_edge_length_around(const Vertex_handle v, const Tr& tr,
|
||||
const VCMap& vcmap, const ECMap& ecmap, const FCMap& fcmap, const CellSelector& cell_selector) const;
|
||||
|
||||
private:
|
||||
Kd_tree m_kd_tree;
|
||||
|
||||
using Surface_patch_index = typename Tr::Cell::Surface_patch_index;
|
||||
std::map<Surface_patch_index, std::size_t> m_p2i;
|
||||
std::vector<Surface_patch_index> m_i2p;
|
||||
std::vector<Triangle_vec> m_triangles;
|
||||
std::vector<AABB_triangle_tree> m_aabb_trees;
|
||||
|
||||
const GT& m_gt;
|
||||
const FT m_k_lipschitz;
|
||||
|
||||
Medial_axis_kd_tree m_medial_axis_kd_tree;
|
||||
public:
|
||||
template<typename CGAL_NP_TEMPLATE_PARAMETERS>
|
||||
static Adaptive_remeshing_sizing_field
|
||||
create_adaptive_sizing_field(const Tr& tr,
|
||||
const CGAL_NP_CLASS& np = parameters::default_values())
|
||||
{
|
||||
using parameters::choose_parameter;
|
||||
using parameters::get_parameter;
|
||||
|
||||
using Edge = typename Tr::Edge;
|
||||
using Facet = typename Tr::Facet;
|
||||
using Cell_handle = typename Tr::Cell_handle;
|
||||
|
||||
auto vcmap = choose_parameter(get_parameter(np, internal_np::vertex_is_constrained),
|
||||
CGAL::Constant_property_map<Vertex_handle, bool>(false));
|
||||
auto ecmap = choose_parameter(get_parameter(np, internal_np::edge_is_constrained),
|
||||
CGAL::Constant_property_map<Edge, 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, vcmap, ecmap, fcmap, cell_selector);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename Tr>
|
||||
void
|
||||
Adaptive_remeshing_sizing_field<Tr>::
|
||||
build_aabb_trees(const Tr& tr)
|
||||
{
|
||||
// collect patch indices, and triangles for each patch
|
||||
for (const auto& f : tr.finite_facets())
|
||||
{
|
||||
if (!f.first->is_facet_on_surface(f.second))
|
||||
continue;
|
||||
|
||||
const Surface_patch_index patch = f.first->surface_patch_index(f.second);
|
||||
if (m_p2i.find(patch) == m_p2i.end())
|
||||
{
|
||||
m_p2i.insert({patch, m_aabb_trees.size()});
|
||||
m_i2p.push_back(patch);
|
||||
|
||||
m_triangles.emplace_back();
|
||||
m_triangles.back().push_back(tr.triangle(f));
|
||||
}
|
||||
else
|
||||
m_triangles[m_p2i[patch]].push_back(tr.triangle(f));
|
||||
}
|
||||
|
||||
CGAL_assertion(m_triangles.size() == m_i2p.size());
|
||||
CGAL_assertion(m_triangles.size() == m_p2i.size());
|
||||
|
||||
for (std::size_t i = 0; i < m_triangles.size(); ++i)
|
||||
{
|
||||
m_aabb_trees.push_back(AABB_triangle_tree(m_triangles[i].begin(), m_triangles[i].end()));
|
||||
m_aabb_trees.back().build();
|
||||
m_aabb_trees.back().accelerate_distance_queries();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Tr>
|
||||
typename Adaptive_remeshing_sizing_field<Tr>::FT
|
||||
Adaptive_remeshing_sizing_field<Tr>::
|
||||
|
|
@ -277,117 +230,80 @@ interpolate_on_four_vertices(
|
|||
return (wa * va + wb * vb + wc * vc + wd * vd) / (wa + wb + wc + wd);
|
||||
}
|
||||
|
||||
|
||||
//template <typename Tr>
|
||||
//typename Adaptive_remeshing_sizing_field<Tr>::FT
|
||||
//Adaptive_remeshing_sizing_field<Tr>::
|
||||
//interpolate_on_facet_vertices(const Bare_point& p, const Cell_handle& cell) const
|
||||
//{
|
||||
// typename GT::Compute_area_3 area = tr_.geom_traits().compute_area_3_object();
|
||||
//
|
||||
// typename GT::Construct_point_3 cp = tr_.geom_traits().construct_point_3_object();
|
||||
// // Find infinite vertex and put it in k0
|
||||
// int k0 = 0;
|
||||
// int k1 = 1;
|
||||
// int k2 = 2;
|
||||
// int k3 = 3;
|
||||
//
|
||||
// if ( tr_.is_infinite(cell->vertex(1)) )
|
||||
// std::swap(k0,k1);
|
||||
// if ( tr_.is_infinite(cell->vertex(2)) )
|
||||
// std::swap(k0,k2);
|
||||
// if ( tr_.is_infinite(cell->vertex(3)) )
|
||||
// std::swap(k0,k3);
|
||||
//
|
||||
// // Interpolate value using tet vertices values
|
||||
// const FT& va = cell->vertex(k1)->meshing_info();
|
||||
// const FT& vb = cell->vertex(k2)->meshing_info();
|
||||
// const FT& vc = cell->vertex(k3)->meshing_info();
|
||||
//
|
||||
// const Tr_point& wa = tr_.point(cell, k1);
|
||||
// const Tr_point& wb = tr_.point(cell, k2);
|
||||
// const Tr_point& wc = tr_.point(cell, k3);
|
||||
// const Bare_point& a = cp(wa);
|
||||
// const Bare_point& b = cp(wb);
|
||||
// const Bare_point& c = cp(wc);
|
||||
//
|
||||
// const FT abp = area(a, b, p);
|
||||
// const FT acp = area(a, c, p);
|
||||
// const FT bcp = area(b, c, p);
|
||||
//
|
||||
// CGAL_assertion(abp >= 0);
|
||||
// CGAL_assertion(acp >= 0);
|
||||
// CGAL_assertion(bcp >= 0);
|
||||
//
|
||||
// // If area is 0, then compute the average value
|
||||
// if ( is_zero(abp+acp+bcp) )
|
||||
// return (va+vb+vc)/3.;
|
||||
//
|
||||
// return ( (abp*vc + acp*vb + bcp*va ) / (abp+acp+bcp) );
|
||||
//}
|
||||
|
||||
template <typename Tr>
|
||||
template <typename VCMap, typename ECMap, typename FCMap, typename CellSelector>
|
||||
typename Adaptive_remeshing_sizing_field<Tr>::FT
|
||||
Adaptive_remeshing_sizing_field<Tr>::
|
||||
sq_circumradius_length(const Cell_handle cell,
|
||||
const Vertex_handle v,
|
||||
const Tr& tr) const
|
||||
average_edge_length_around(const Vertex_handle v, const Tr& tr,
|
||||
const VCMap& vcmap, const ECMap& ecmap, const FCMap& fcmap,
|
||||
const CellSelector& cell_selector) const
|
||||
{
|
||||
auto cp = tr.geom_traits().construct_point_3_object();
|
||||
auto sq_distance = tr.geom_traits().compute_squared_distance_3_object();
|
||||
auto cc = tr.geom_traits().construct_circumcenter_3_object();
|
||||
using Edge = typename Tr::Edge;
|
||||
using Facet = typename Tr::Facet;
|
||||
|
||||
const auto t = tr.tetrahedron(cell);
|
||||
const Bare_point circumcenter = cc(t[0], t[1], t[2], t[3]);
|
||||
const Tr_point& position = tr.point(cell, cell->index(v));
|
||||
std::vector<Edge> tmp_edges;
|
||||
tr.incident_edges(v, std::back_inserter(tmp_edges));
|
||||
|
||||
return sq_distance(cp(position), circumcenter);
|
||||
}
|
||||
|
||||
template <typename Tr>
|
||||
typename Adaptive_remeshing_sizing_field<Tr>::FT
|
||||
Adaptive_remeshing_sizing_field<Tr>::
|
||||
average_circumradius_around(const Vertex_handle v, const Tr& tr) const
|
||||
{
|
||||
std::vector<Cell_handle> incident_cells;
|
||||
incident_cells.reserve(64);
|
||||
tr.incident_cells(v, std::back_inserter(incident_cells));
|
||||
|
||||
using SI = typename Tr::Triangulation_data_structure::Cell::Subdomain_index;
|
||||
|
||||
FT sum_len(0);
|
||||
unsigned int nb = 0;
|
||||
|
||||
for (Cell_handle c : incident_cells)
|
||||
std::vector<Edge> edges;
|
||||
switch (v->in_dimension())
|
||||
{
|
||||
if (c->subdomain_index() != SI())
|
||||
{
|
||||
sum_len += CGAL::approximate_sqrt(sq_circumradius_length(c, v, tr));
|
||||
++nb;
|
||||
}
|
||||
}
|
||||
|
||||
// nb == 0 could happen if there is an isolated point.
|
||||
if (0 != nb)
|
||||
{
|
||||
return sum_len / nb;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use outside cells to compute size of point
|
||||
for (Cell_handle c : incident_cells)
|
||||
{
|
||||
if (!tr.is_infinite(c))
|
||||
case 3:
|
||||
std::copy_if(tmp_edges.begin(), tmp_edges.end(),
|
||||
std::back_inserter(edges),
|
||||
[&tr, &cell_selector](const Edge& e)
|
||||
{
|
||||
sum_len += CGAL::approximate_sqrt(sq_circumradius_length(c, v, tr));
|
||||
++nb;
|
||||
}
|
||||
}
|
||||
return is_selected(e, tr, cell_selector);
|
||||
});
|
||||
break;
|
||||
|
||||
CGAL_assertion(nb != 0);
|
||||
CGAL_assertion(sum_len != 0);
|
||||
return sum_len / nb;
|
||||
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)
|
||||
{
|
||||
return get(ecmap, e);
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
std::cout << "dimension = " << v->in_dimension() << std::endl;
|
||||
break;
|
||||
}
|
||||
|
||||
CGAL_assertion(!edges.empty());
|
||||
if (edges.empty())
|
||||
return 0;
|
||||
|
||||
auto cp = m_gt.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());
|
||||
}
|
||||
|
||||
} // end namespace Tetrahedral_remeshing
|
||||
|
|
|
|||
Loading…
Reference in New Issue