mirror of https://github.com/CGAL/cgal
704 lines
28 KiB
C++
704 lines
28 KiB
C++
// Copyright (c) 2010-2012-2016 GeometryFactory Sarl (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) : Laurent Rineau
|
|
//
|
|
|
|
#ifndef CGAL_MESH_3_SIZING_FIELD_WITH_AABB_TREE_H
|
|
#define CGAL_MESH_3_SIZING_FIELD_WITH_AABB_TREE_H
|
|
|
|
#include <CGAL/license/Mesh_3.h>
|
|
|
|
#include <CGAL/Profile_counter.h>
|
|
#include <CGAL/Delaunay_triangulation_3.h>
|
|
#include <CGAL/Mesh_3/Protect_edges_sizing_field.h> // for weight_modifier
|
|
|
|
#include <cstddef>
|
|
#include <memory>
|
|
#include <limits>
|
|
|
|
#include <CGAL/Mesh_3/experimental/Facet_patch_id_map.h>
|
|
#include <CGAL/Mesh_3/experimental/Get_curve_index.h>
|
|
#include <CGAL/Mesh_3/experimental/AABB_filtered_projection_traits.h>
|
|
|
|
#include <boost/container/flat_set.hpp>
|
|
#if defined(CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY) || defined(CGAL_NO_ASSERTIONS) == 0
|
|
# include <sstream>
|
|
# include <boost/format.hpp>
|
|
#endif // CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY || (! CGAL_NO_ASSERTIONS)
|
|
|
|
namespace CGAL {
|
|
/*!
|
|
* @ingroup PkgMesh3DomainFields
|
|
*
|
|
* The class `Sizing_field_with_aabb_tree` is a model of concept `MeshDomainField_3`.
|
|
* It provides a sizing field to be used in the meshing process of a polyhedral surface with features.
|
|
*
|
|
* At each query point `p`, the field value is the minimum of the distances to
|
|
* - the surface patches `p` does not belong to, and
|
|
* - for each curve `C` it does belong to, the intersection points of `C` with
|
|
* the plane orthogonal to `C` and passing through `p`.
|
|
*
|
|
* This sizing field is designed to be used for the edges of the mesh complex,
|
|
* in `Mesh_edge_criteria_3` or as `edge_size` in `Mesh_criteria_3`.
|
|
* Using this sizing field for complex edges provides a high-quality hint
|
|
* to the protecting balls placement algorithm, since it ensures that no
|
|
* protecting ball will intersect a surface patch to which the
|
|
* corresponding vertex does not belong.
|
|
*
|
|
* @tparam GT is the geometric traits class. It must match the type `Triangulation::Geom_traits`,
|
|
* where `Triangulation` is the nested type of the model of `MeshComplex_3InTriangulation_3` used
|
|
* in the meshing process.
|
|
* @tparam MeshDomain is the type of the domain. It must be a model of `MeshDomainWithFeatures_3`,
|
|
* and derive from `CGAL::Polyhedral_mesh_domain_with_features_3`
|
|
|
|
|
|
* @cgalModels{MeshDomainField_3}
|
|
*/
|
|
template <typename GT,
|
|
typename MeshDomain
|
|
#ifndef DOXYGEN_RUNNING
|
|
, typename Input_facets_AABB_tree_ = CGAL::Default
|
|
, typename Get_curve_index_ = CGAL::Default
|
|
, typename Facet_patch_id_map_ = CGAL::Default
|
|
#endif
|
|
>
|
|
struct Sizing_field_with_aabb_tree
|
|
{
|
|
using FT = typename GT::FT;
|
|
using Point_3 = typename GT::Point_3;
|
|
using Index = typename MeshDomain::Index;
|
|
|
|
private:
|
|
typedef typename MeshDomain::Corner_index Corner_index;
|
|
typedef typename MeshDomain::Curve_index Curve_index;
|
|
typedef typename MeshDomain::Surface_patch_index Patch_index;
|
|
|
|
typedef boost::container::flat_set<Curve_index> Curves_ids;
|
|
typedef boost::container::flat_set<Patch_index> Patches_ids;
|
|
|
|
typedef std::map<Point_3, Corner_index> Corners_indices;
|
|
typedef std::vector<Point_3> Corners;
|
|
typedef std::vector<Curves_ids> Corners_incident_curves;
|
|
typedef std::vector<Patches_ids> Corners_incident_patches;
|
|
typedef std::vector<Patches_ids> Curves_incident_patches;
|
|
|
|
typedef GT Kernel_;
|
|
typedef CGAL::Delaunay_triangulation_3<Kernel_> Dt;
|
|
|
|
using Input_facets_AABB_tree = typename CGAL::Default::Get<
|
|
Input_facets_AABB_tree_,
|
|
typename MeshDomain::AABB_tree
|
|
>::type;
|
|
using AABB_traits = typename Input_facets_AABB_tree::AABB_traits;
|
|
using Point_and_primitive_id = typename AABB_traits::Point_and_primitive_id;
|
|
typedef typename Input_facets_AABB_tree::Primitive Input_facets_AABB_tree_primitive_;
|
|
typedef typename MeshDomain::Curves_AABB_tree Input_curves_AABB_tree_;
|
|
typedef typename Input_curves_AABB_tree_::Primitive Input_curves_AABB_tree_primitive_;
|
|
|
|
using Get_curve_index = typename CGAL::Default::Get<
|
|
Get_curve_index_,
|
|
CGAL::Mesh_3::Get_curve_index<typename MeshDomain::Curves_AABB_tree::Primitive>
|
|
>::type;
|
|
using Facet_patch_id_map = typename CGAL::Default::Get<
|
|
Facet_patch_id_map_,
|
|
CGAL::Mesh_3::Facet_patch_id_map<MeshDomain,
|
|
typename Input_facets_AABB_tree::Primitive>
|
|
>::type;
|
|
|
|
public:
|
|
/// \name Creation
|
|
/// @{
|
|
|
|
/*!
|
|
* Constructor for the sizing field.
|
|
* @param d the maximal value returned by `operator()`, corresponding
|
|
* to an upper bound on feature edge lengths in the output mesh.
|
|
* @param domain the mesh domain to be meshed
|
|
*/
|
|
Sizing_field_with_aabb_tree(const FT& d, const MeshDomain& domain)
|
|
: Sizing_field_with_aabb_tree(d,
|
|
domain,
|
|
domain.aabb_tree(),
|
|
Get_curve_index(),
|
|
Facet_patch_id_map())
|
|
{}
|
|
|
|
/// @}
|
|
|
|
struct Face_ids_traversal_traits {
|
|
using Limits = std::numeric_limits<Patch_index>;
|
|
Patch_index min{(Limits::max)()};
|
|
Patch_index max{Limits::lowest()};
|
|
Facet_patch_id_map index_map{};
|
|
|
|
bool go_further() const { return true; }
|
|
template <typename T> bool do_intersect(std::nullptr_t, const T&) { return true; }
|
|
|
|
template <typename P> void intersection(std::nullptr_t, const P& primitive) {
|
|
const Patch_index id = get(index_map, primitive.id());
|
|
if (id < min) min = id;
|
|
if (id > max) max = id;
|
|
}
|
|
};
|
|
|
|
#ifndef DOXYGEN_RUNNING
|
|
Sizing_field_with_aabb_tree
|
|
(const typename Kernel_::FT d,
|
|
const MeshDomain& domain,
|
|
const Input_facets_AABB_tree& aabb_tree,
|
|
Get_curve_index get_curve_index = Get_curve_index(),
|
|
Facet_patch_id_map facet_patch_id_map = Facet_patch_id_map()
|
|
)
|
|
: d_ptr{std::make_shared<Private_data>(d,
|
|
domain,
|
|
aabb_tree,
|
|
get_curve_index,
|
|
facet_patch_id_map)}
|
|
{
|
|
if(!d_ptr->aabb_tree.empty()) {
|
|
// Initialize the per-patch kd-trees
|
|
// - First compute the number of patches and min/max patch ids
|
|
Face_ids_traversal_traits traversal_traits;
|
|
d_ptr->aabb_tree.traversal(nullptr, traversal_traits);
|
|
#ifdef CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY
|
|
std::cerr << "min: " << traversal_traits.min << ", max: " << traversal_traits.max << '\n';
|
|
#endif // CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY
|
|
d_ptr->min_patch_id = traversal_traits.min;
|
|
d_ptr->kd_trees_ptrs.resize(traversal_traits.max - d_ptr->min_patch_id + 1);
|
|
|
|
using Node = CGAL::AABB_node<AABB_traits>;
|
|
using Primitive = typename AABB_traits::Primitive;
|
|
using Point_and_primitive_id = typename AABB_traits::Point_and_primitive_id;
|
|
|
|
std::vector<std::set<Point_and_primitive_id>> vertices_per_patch;
|
|
vertices_per_patch.resize(d_ptr->kd_trees_ptrs.size());
|
|
|
|
// - then fill sets of vertices per patch
|
|
auto push_vertex = [&](const auto& primitive) {
|
|
const Patch_index id = get(d_ptr->facet_patch_id_map, primitive.id());
|
|
const Point_3& p = primitive.reference_point();
|
|
vertices_per_patch[id - d_ptr->min_patch_id].emplace(p, primitive.id());
|
|
};
|
|
|
|
struct Visit_all_primitives {
|
|
decltype(push_vertex) register_vertex;
|
|
bool go_further() const { return true; }
|
|
bool do_intersect(std::nullptr_t, const Node&) { return true; }
|
|
void intersection(std::nullptr_t, const Primitive& primitive)
|
|
{
|
|
register_vertex(primitive);
|
|
}
|
|
} visit_all_primitives{push_vertex};
|
|
d_ptr->aabb_tree.traversal(nullptr, visit_all_primitives);
|
|
|
|
// - then create the kd-trees
|
|
for(std::size_t i = 0; i < vertices_per_patch.size(); ++i) {
|
|
if(!vertices_per_patch[i].empty()) {
|
|
d_ptr->kd_trees_ptrs[i] = std::make_unique<Kd_tree>(vertices_per_patch[i].begin(),
|
|
vertices_per_patch[i].end());
|
|
}
|
|
}
|
|
}
|
|
{
|
|
Corner_index maximal_corner_index = 0;
|
|
typedef std::pair<Corner_index, Point_3> Corner_index_and_point;
|
|
std::vector<Corner_index_and_point > corners_tmp;
|
|
d_ptr->domain.get_corners(std::back_inserter(corners_tmp));
|
|
for(const Corner_index_and_point& pair : corners_tmp)
|
|
{
|
|
// Fill `corners_indices`
|
|
if(pair.first > maximal_corner_index) maximal_corner_index = pair.first;
|
|
d_ptr->corners_indices.
|
|
insert(typename Corners_indices::value_type(pair.second, pair.first));
|
|
}
|
|
d_ptr->corners.resize(maximal_corner_index+1);
|
|
for(const Corner_index_and_point& pair : corners_tmp)
|
|
{
|
|
// Fill `corners`
|
|
d_ptr->corners[pair.first] = pair.second;
|
|
}
|
|
}
|
|
|
|
//fill incidences of corners with curves
|
|
d_ptr->corners_incident_curves.resize(d_ptr->corners.size());
|
|
for(const typename Corners_indices::value_type& pair : d_ptr->corners_indices) {
|
|
d_ptr->dt.insert(pair.first);
|
|
|
|
// Fill `corners_incident_curves[corner_id]`
|
|
Curves_ids& incident_curves = d_ptr->corners_incident_curves[pair.second];
|
|
d_ptr->domain.get_corner_incident_curves(pair.second,
|
|
std::inserter(incident_curves,
|
|
incident_curves.end()));
|
|
// For each incident loops, insert a point on the loop, as far as
|
|
// possible.
|
|
for(Curve_index curve_index : incident_curves) {
|
|
if(domain.is_loop(curve_index)) {
|
|
FT curve_length = d_ptr->domain.curve_length(curve_index);
|
|
Point_3 other_point =
|
|
d_ptr->domain.construct_point_on_curve(pair.first,
|
|
curve_index,
|
|
curve_length / 2);
|
|
d_ptr->dt.insert(other_point);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (d_ptr->aabb_tree.empty())
|
|
return;//there is no surface --> no surface patches
|
|
|
|
//fill incidences with patches
|
|
d_ptr->curves_incident_patches.resize(domain.maximal_curve_index()+1);
|
|
d_ptr->corners_incident_patches.resize(d_ptr->corners.size());
|
|
for (Curve_index curve_id = 1;
|
|
std::size_t(curve_id) < d_ptr->curves_incident_patches.size();
|
|
++curve_id)
|
|
{
|
|
const typename MeshDomain::Surface_patch_index_set& incident_patches =
|
|
d_ptr->domain.get_incidences(curve_id);
|
|
|
|
// Fill `curves_incident_patches[curve_id]`
|
|
d_ptr->curves_incident_patches[curve_id].
|
|
insert(boost::container::ordered_unique_range,
|
|
incident_patches.begin(), incident_patches.end());
|
|
}
|
|
for(const typename Corners_indices::value_type& pair : d_ptr->corners_indices) {
|
|
// Get `corners_incident_curves[corner_id]`
|
|
Curves_ids& incident_curves = d_ptr->corners_incident_curves[pair.second];
|
|
|
|
// Fill `corners_incident_patches[corner_id]`
|
|
Patches_ids& incident_patches = d_ptr->corners_incident_patches[pair.second];
|
|
for(Curve_index curve_index : incident_curves) {
|
|
const Patches_ids& curve_incident_patches =
|
|
d_ptr->curves_incident_patches[curve_index];
|
|
incident_patches.insert(boost::container::ordered_unique_range,
|
|
curve_incident_patches.begin(),
|
|
curve_incident_patches.end());
|
|
}
|
|
}
|
|
}
|
|
|
|
std::optional<Point_and_primitive_id>
|
|
closest_point_on_surfaces(const Point_3& p,
|
|
const Patches_ids& patch_ids_to_ignore) const
|
|
{
|
|
std::optional<Point_and_primitive_id> result{};
|
|
if(d_ptr->aabb_tree.empty()) return result;
|
|
for(std::size_t i = 0; i < d_ptr->kd_trees_ptrs.size(); ++i) {
|
|
const auto patch_id = static_cast<Patch_index>(i + d_ptr->min_patch_id);
|
|
if(patch_ids_to_ignore.find(patch_id) != patch_ids_to_ignore.end()) continue;
|
|
if(d_ptr->kd_trees_ptrs[i]) {
|
|
const Kd_tree& kd_tree = *d_ptr->kd_trees_ptrs[i];
|
|
const Point_and_primitive_id closest_point = kd_tree.closest_point(p);
|
|
if(!result || CGAL::compare_distance(p, closest_point.first, result->first) == CGAL::SMALLER)
|
|
{
|
|
result = closest_point;
|
|
}
|
|
}
|
|
}
|
|
if(!result) return result;
|
|
CGAL::Mesh_3::Filtered_projection_traits<
|
|
AABB_traits,
|
|
Facet_patch_id_map
|
|
> projection_traits(*result,
|
|
patch_ids_to_ignore.begin(), patch_ids_to_ignore.end(),
|
|
d_ptr->aabb_tree.traits(),
|
|
d_ptr->facet_patch_id_map);
|
|
d_ptr->aabb_tree.traversal(p, projection_traits);
|
|
result = projection_traits.closest_point_and_primitive();
|
|
return result;
|
|
}
|
|
#endif // DOXYGEN_RUNNING
|
|
|
|
public:
|
|
/// \name Operations
|
|
/// @{
|
|
|
|
/*!
|
|
* returns the value of the sizing field at the point `p`,
|
|
* assumed to be included in the input complex feature with dimension `dimension`
|
|
* and mesh subcomplex index `id`.
|
|
*/
|
|
double operator()(const Point_3& p,
|
|
const int dim,
|
|
const Index& id) const
|
|
{
|
|
CGAL_PROFILER("Sizing field");
|
|
#ifdef CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY
|
|
if(dim <= 1) {
|
|
std::cerr << "Sizing(" << p << ", dim=" << dim
|
|
<< ", index=#" << CGAL::IO::oformat(id) << "): ";
|
|
}
|
|
#endif // CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY
|
|
FT result = d_ptr->d_;
|
|
if(dim == 0) {
|
|
if(d_ptr->dt.dimension() < 1) {
|
|
#ifdef CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY
|
|
std::cerr << result << "(dt.dimension() < 1)\n";
|
|
#endif // CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY
|
|
return result;
|
|
}
|
|
typename Dt::Point nearest;
|
|
|
|
typename Dt::Locate_type lt;
|
|
int li, lj;
|
|
const typename Dt::Cell_handle ch = d_ptr->dt.locate(p, lt, li, lj);
|
|
if(lt == Dt::VERTEX) {
|
|
// std::cerr << "lt == Dt::VERTEX\n";
|
|
const typename Dt::Vertex_handle vh = ch->vertex(li);
|
|
std::vector<typename Dt::Vertex_handle> vs;
|
|
vs.reserve(32);
|
|
d_ptr->dt.finite_adjacent_vertices(vh, std::back_inserter(vs));
|
|
CGAL_assertion(!vs.empty());
|
|
nearest = d_ptr->dt.point(vs[0]);
|
|
// std::cerr << "sq_dist = " << CGAL::squared_distance(p, nearest)
|
|
// << std::endl;
|
|
typename Kernel_::Compare_distance_3 compare_dist;
|
|
for (typename std::vector<typename Dt::Vertex_handle>::const_iterator
|
|
it = vs.begin(); it != vs.end(); ++it)
|
|
{
|
|
// std::cerr << "sq_dist = " << CGAL::squared_distance(p, dt.point(*it))
|
|
// << std::endl;
|
|
if(compare_dist(p, d_ptr->dt.point(*it), nearest) == CGAL::SMALLER) {
|
|
// std::cerr << " nearest!\n";
|
|
nearest = d_ptr->dt.point(*it);
|
|
}
|
|
}
|
|
} else {
|
|
// std::cerr << "lt=" << lt << std::endl;
|
|
const typename Dt::Vertex_handle vh = d_ptr->dt.nearest_vertex(p, ch);
|
|
nearest = d_ptr->dt.point(vh);
|
|
}
|
|
const FT dist = CGAL_NTS sqrt(CGAL::squared_distance( nearest, p));
|
|
// std::cerr << (std::min)(dist / FT(1.5), d_) << "\n";
|
|
result = (std::min)(dist * FT(0.5), result);
|
|
|
|
// now search in the AABB tree
|
|
typename Corners_indices::const_iterator ids_it =
|
|
d_ptr->corners_indices.find(p);
|
|
if(ids_it == d_ptr->corners_indices.end()) {
|
|
std::cerr << "ERROR at " << __FILE__ << " line " << __LINE__ << "\n";
|
|
}
|
|
else if(!d_ptr->aabb_tree.empty()) {
|
|
const Patches_ids& ids = d_ptr->corners_incident_patches[ids_it->second];
|
|
CGAL_assertion(! ids.empty());
|
|
|
|
const auto closest_point_and_primitive = closest_point_on_surfaces(p, ids);
|
|
|
|
if(closest_point_and_primitive != std::nullopt) {
|
|
result =
|
|
(std::min)(FT(0.9 / CGAL::sqrt(CGAL::Mesh_3::internal::weight_modifier) *
|
|
CGAL_NTS
|
|
sqrt(CGAL::squared_distance(p,
|
|
closest_point_and_primitive->first))),
|
|
result);
|
|
#ifdef CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY
|
|
{
|
|
std::stringstream s;
|
|
|
|
using boost::io::group;
|
|
using std::setprecision;
|
|
s << boost::format("\nSizing field is %1% at point (%2%)"
|
|
" on corner!\n"
|
|
"Closest face id: %3%\n"
|
|
"Closest point: %4%\n"
|
|
"Ids are { ")
|
|
% group(setprecision(17),result)
|
|
% group(setprecision(17),p)
|
|
% CGAL::IO::oformat(get(d_ptr->facet_patch_id_map,
|
|
closest_point_and_primitive->second))
|
|
% group(setprecision(17),
|
|
closest_point_and_primitive->first);
|
|
for(Patch_index i : ids) {
|
|
s << CGAL::IO::oformat(i) << " ";
|
|
}
|
|
s << "}\n";
|
|
std::cerr << s.str();
|
|
std::cerr << result << " (result)\n";
|
|
}
|
|
#endif // CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY
|
|
} else {
|
|
#ifdef CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY
|
|
std::cerr << result << " (projection not found)\n";
|
|
#endif // CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY
|
|
}
|
|
} // end if(corners.find(p) != corners.end()) and !aabb_tree.empty()
|
|
} // end if(dim == 0)
|
|
else if(dim != 1) {
|
|
#ifdef CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY
|
|
std::cerr << result << "\n";
|
|
#endif // CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY
|
|
return result;
|
|
}
|
|
else { // dim == 1
|
|
const typename MeshDomain::Curve_index& curve_id =
|
|
d_ptr->domain.curve_index(id);
|
|
const Patches_ids& ids = d_ptr->curves_incident_patches[curve_id];
|
|
CGAL_assertion(! ids.empty());
|
|
|
|
if(!d_ptr->aabb_tree.empty()) {
|
|
//Compute distance to surface patches
|
|
const auto closest_point_and_primitive = closest_point_on_surfaces(p, ids);
|
|
if(closest_point_and_primitive == std::nullopt) {
|
|
#ifdef CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY
|
|
std::cerr << result << " (projection not found) ids:";
|
|
for(Patch_index i : ids) {
|
|
std::cerr << " " << CGAL::IO::oformat(i);
|
|
}
|
|
std::cerr << "\n";
|
|
|
|
#endif // CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY
|
|
return result;
|
|
}
|
|
|
|
CGAL_assertion(ids.count(get(d_ptr->facet_patch_id_map,
|
|
closest_point_and_primitive->second)) == 0);
|
|
|
|
result =
|
|
(std::min)(FT(0.9 / CGAL::sqrt(CGAL::Mesh_3::internal::weight_modifier) *
|
|
CGAL_NTS
|
|
sqrt(CGAL::squared_distance(p,
|
|
closest_point_and_primitive->first))),
|
|
result);
|
|
|
|
#ifdef CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY
|
|
{
|
|
std::stringstream s;
|
|
|
|
s << boost::format("\nSizing field is %1% at point (%2%)"
|
|
" on curve #%3% !\n"
|
|
"Closest face id: %4%\n"
|
|
"Ids are { ")
|
|
% result % p % curve_id
|
|
% CGAL::IO::oformat(get(d_ptr->facet_patch_id_map,
|
|
closest_point_and_primitive->second));
|
|
for(Patch_index i : ids) {
|
|
s << CGAL::IO::oformat(i) << " ";
|
|
}
|
|
s << "}\n";
|
|
std::cerr << s.str();
|
|
std::cerr << result << " (result)\n";
|
|
}
|
|
#endif // CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY
|
|
#ifndef CGAL_NO_ASSERTIONS
|
|
if(result <= 0) {
|
|
std::stringstream s;
|
|
|
|
s << boost::format("Sizing field is %1% at point (%2%)"
|
|
" on curve #%3% !\n"
|
|
"Closest face id: %4%\n"
|
|
"Ids are { ")
|
|
% result % p % curve_id
|
|
% CGAL::IO::oformat(get(d_ptr->facet_patch_id_map,
|
|
closest_point_and_primitive->second));
|
|
for(Patch_index i : ids) {
|
|
s << CGAL::IO::oformat(i) << " ";
|
|
}
|
|
s << "}\n";
|
|
CGAL_assertion_msg(result <=0, s.str().c_str());
|
|
}
|
|
#ifdef PROTECTION_DEBUG
|
|
else if (result <= (d_ / 1e7)) {
|
|
std::stringstream s;
|
|
s << boost::format("Sizing field is %1% at point (%2%)"
|
|
" on curve #%3% !\n"
|
|
"Closest face id: %4%\n"
|
|
"Ids are { ")
|
|
% result % p % curve_id
|
|
% closest_point_and_primitive->second->patch_id();
|
|
for(Patch_index i : ids) {
|
|
s << CGAL::IO::oformat(i) << " ";
|
|
}
|
|
s << "}\n";
|
|
std::cerr << "ERROR at " << __FILE__ << " line " << __LINE__ << " :\n"
|
|
<< s.str() << std::endl;
|
|
}
|
|
#endif // PROTECTION_DEBUG
|
|
#endif // CGAL_NO_ASSERTIONS
|
|
} // end if(!aabb_tree.empty())
|
|
//Compute distance to the curves, and exclude the one on which p lies
|
|
CGAL::Mesh_3::Filtered_projection_traits<typename Input_curves_AABB_tree_::AABB_traits,
|
|
Get_curve_index >
|
|
curves_projection_traits(curve_id,
|
|
d_ptr->domain.curves_aabb_tree().traits(),
|
|
d_ptr->get_curve_index);
|
|
|
|
d_ptr->domain.curves_aabb_tree().traversal(p, curves_projection_traits);
|
|
|
|
//Compute distance to the curve on which p lies
|
|
typedef typename GT::Segment_3 Segment_3;
|
|
typedef typename GT::Plane_3 Plane_3;
|
|
|
|
const typename Input_curves_AABB_tree_::Point_and_primitive_id& ppid
|
|
= d_ptr->domain.curves_aabb_tree().closest_point_and_primitive(p);
|
|
|
|
Segment_3 curr_segment(*ppid.second.second, *(ppid.second.second + 1));
|
|
Plane_3 curr_ortho_plane(p, curr_segment.to_vector()/*normal*/);
|
|
Input_curves_AABB_tree_primitive_ curr_prim(ppid.second);
|
|
|
|
std::vector<Input_curves_AABB_tree_primitive_> prims;
|
|
d_ptr->domain.curves_aabb_tree().
|
|
all_intersected_primitives(curr_ortho_plane, std::back_inserter(prims));
|
|
|
|
#ifdef CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY
|
|
std::cerr << std::endl;
|
|
std::cerr << "p = " << p << std::endl;
|
|
std::cerr << "curr_ortho_plane = " << curr_ortho_plane << std::endl;
|
|
std::cerr << "PRIMITIVES FOUND : " << prims.size() << std::endl;
|
|
#endif
|
|
|
|
Point_3 closest_intersection;
|
|
Input_curves_AABB_tree_primitive_ closest_primitive = prims[0];
|
|
FT sqd_intersection = -1;
|
|
for(Input_curves_AABB_tree_primitive_ prim : prims)
|
|
{
|
|
if (prim.id() == curr_prim.id())
|
|
continue;//curr_prim is the closest primitive
|
|
|
|
if (curve_id != prim.id().first->first)
|
|
continue;//don't deal with the same curves as what is done above
|
|
|
|
const auto int_res = CGAL::intersection(prim.datum(), curr_ortho_plane);
|
|
if (int_res)
|
|
{
|
|
if (const Point_3* pp = std::get_if<Point_3>(&*int_res))
|
|
{
|
|
FT new_sqd = CGAL::squared_distance(p, *pp);
|
|
FT dist = CGAL::abs(d_ptr->domain.signed_geodesic_distance(p, *pp, curve_id));
|
|
|
|
#ifdef CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY
|
|
std::cerr << "Intersection point : Point_3(" << *pp << ") ";
|
|
std::cerr << "\n new_sqd = " << new_sqd ;
|
|
std::cerr << "\n dist = " << dist << "\n";
|
|
#endif
|
|
if (new_sqd * 1e10 < CGAL::squared_distance(curr_segment.source(),
|
|
curr_segment.target()))
|
|
{
|
|
#ifdef CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY
|
|
std::cerr << " too close, compared to possible rounding errors, "
|
|
<< "SKIPPED\n";
|
|
#endif
|
|
continue;
|
|
}
|
|
if (CGAL_NTS sqrt(new_sqd) > 0.9 * dist)
|
|
continue;
|
|
if (sqd_intersection == -1 || new_sqd < sqd_intersection)
|
|
{
|
|
sqd_intersection = new_sqd;
|
|
closest_intersection = *pp;
|
|
closest_primitive = prim;
|
|
}
|
|
}
|
|
else
|
|
continue;// intersection is a segment : collinear case
|
|
}
|
|
else
|
|
CGAL_assertion(false);//prim was returned as an intersected primitive
|
|
}
|
|
|
|
//compare closest_projection and closest_intersection, and keep the closest
|
|
if (curves_projection_traits.found())
|
|
{
|
|
FT tmp_sqd = CGAL::squared_distance(p, curves_projection_traits.closest_point());
|
|
if (sqd_intersection == -1 || tmp_sqd < sqd_intersection)
|
|
{
|
|
sqd_intersection = tmp_sqd;
|
|
closest_intersection = curves_projection_traits.closest_point();
|
|
closest_primitive = Input_curves_AABB_tree_primitive_(
|
|
curves_projection_traits.closest_point_and_primitive().second);
|
|
}
|
|
}
|
|
#ifdef CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY
|
|
std::cout << " curve_id = " << curve_id
|
|
<< " proj_cid = " << closest_primitive.id().first->first
|
|
<< " (" << get(d_ptr->get_curve_index, closest_primitive.id()) << ")"
|
|
<< std::endl;
|
|
std::cerr << " --- domain.curves_aabb_tree().traversal \n";
|
|
#endif // CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY
|
|
if (sqd_intersection > 0)
|
|
{
|
|
#ifdef CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY
|
|
std::cerr << "FOUND!\n";
|
|
std::cerr << " closest_point: " << closest_intersection << "\n"
|
|
<< " distance = " << CGAL_NTS sqrt(sqd_intersection) << std::endl;
|
|
#endif // CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY
|
|
FT new_result =
|
|
(std::min)(FT(0.45 / CGAL::sqrt(CGAL::Mesh_3::internal::weight_modifier) *
|
|
CGAL_NTS sqrt(sqd_intersection)),
|
|
d_ptr->d_);
|
|
|
|
#ifdef CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY
|
|
std::cerr << "result = " << result << "\n";
|
|
std::cerr << "new_result = " << new_result << "\n";
|
|
#endif // CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY
|
|
if(result > new_result) {
|
|
result = new_result;
|
|
#ifdef CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY
|
|
std::stringstream s;
|
|
|
|
s << boost::format("\nSizing field is %1% at point (%2%)"
|
|
" on curve #%3% !\n"
|
|
"Closest CURVE id: %4%\n"
|
|
"Ids are { ")
|
|
% result % p % curve_id
|
|
% closest_primitive.id().first->first;
|
|
for(int i : ids) {
|
|
s << i << " ";
|
|
}
|
|
s << "}\n";
|
|
std::cerr << s.str();
|
|
std::cerr << result << " (result)\n";
|
|
#endif // CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY
|
|
}
|
|
}
|
|
} // end dim == 1
|
|
#ifdef CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY
|
|
std::cerr << result << std::endl;
|
|
#endif // CGAL_MESH_3_PROTECTION_HIGH_VERBOSITY
|
|
return result;
|
|
}
|
|
|
|
/// @}
|
|
|
|
private:
|
|
using Kd_tree = CGAL::AABB_search_tree<AABB_traits>;
|
|
struct Private_data {
|
|
using FT = typename Kernel_::FT;
|
|
Private_data(FT d,
|
|
const MeshDomain& domain,
|
|
const Input_facets_AABB_tree& aabb_tree,
|
|
Get_curve_index get_curve_index,
|
|
Facet_patch_id_map facet_patch_id_map)
|
|
: d_(d)
|
|
, domain(domain)
|
|
, aabb_tree(aabb_tree)
|
|
, get_curve_index(get_curve_index)
|
|
, facet_patch_id_map(facet_patch_id_map)
|
|
{}
|
|
FT d_;
|
|
const MeshDomain& domain;
|
|
const Input_facets_AABB_tree& aabb_tree;
|
|
Get_curve_index get_curve_index;
|
|
Facet_patch_id_map facet_patch_id_map;
|
|
Dt dt{};
|
|
Corners corners{};
|
|
Corners_indices corners_indices{};
|
|
Curves_incident_patches curves_incident_patches{};
|
|
Corners_incident_patches corners_incident_patches{};
|
|
Corners_incident_curves corners_incident_curves{};
|
|
std::vector<std::unique_ptr<Kd_tree>> kd_trees_ptrs{};
|
|
Patch_index min_patch_id{};
|
|
};
|
|
std::shared_ptr<Private_data> d_ptr;
|
|
};
|
|
|
|
}//end namespace CGAL
|
|
|
|
#endif // CGAL_MESH_3_SIZING_FIELD_WITH_AABB_TREE_H
|