mirror of https://github.com/CGAL/cgal
Merge two tangential relaxation functions into one
This commit is contained in:
parent
618fb4b027
commit
c41a0e38c2
|
|
@ -116,6 +116,13 @@ struct Allow_all_moves{
|
||||||
* \cgalParamDefault{If not provided, all moves are allowed.}
|
* \cgalParamDefault{If not provided, all moves are allowed.}
|
||||||
* \cgalParamNEnd
|
* \cgalParamNEnd
|
||||||
*
|
*
|
||||||
|
* \cgalParamNBegin{sizing_function}
|
||||||
|
* \cgalParamDescription{An object containing sizing field for individual vertices.
|
||||||
|
* Used to derive smoothing weights.}
|
||||||
|
* \cgalParamType{A model of `PMPSizingField`.}
|
||||||
|
* \cgalParamDefault{If not provided, smoothing weights are the same for all vertices.}
|
||||||
|
* \cgalParamNEnd
|
||||||
|
*
|
||||||
* \cgalNamedParamsEnd
|
* \cgalNamedParamsEnd
|
||||||
*
|
*
|
||||||
* \todo check if it should really be a triangle mesh or if a polygon mesh is fine
|
* \todo check if it should really be a triangle mesh or if a polygon mesh is fine
|
||||||
|
|
@ -232,12 +239,6 @@ void tangential_relaxation(const VertexRange& vertices,
|
||||||
auto gt_barycenter = gt.construct_barycenter_3_object();
|
auto gt_barycenter = gt.construct_barycenter_3_object();
|
||||||
auto gt_project = gt.construct_projected_point_3_object();
|
auto gt_project = gt.construct_projected_point_3_object();
|
||||||
|
|
||||||
// if constexpr (is_default_parameter<CGAL_NP_CLASS, internal_np::sizing_function_t>::value)
|
|
||||||
// {
|
|
||||||
// auto gt_area = gt.compute_area_3_object();
|
|
||||||
// auto gt_centroid = gt.construct_centroid_3_object();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// at each vertex, compute vertex normal
|
// at each vertex, compute vertex normal
|
||||||
std::unordered_map<vertex_descriptor, Vector_3> vnormals;
|
std::unordered_map<vertex_descriptor, Vector_3> vnormals;
|
||||||
compute_vertex_normals(tm, boost::make_assoc_property_map(vnormals), np);
|
compute_vertex_normals(tm, boost::make_assoc_property_map(vnormals), np);
|
||||||
|
|
@ -287,7 +288,9 @@ void tangential_relaxation(const VertexRange& vertices,
|
||||||
|
|
||||||
const double tri_area = gt_area(get(vpm, v), get(vpm, v1), get(vpm, v2));
|
const double tri_area = gt_area(get(vpm, v), get(vpm, v1), get(vpm, v2));
|
||||||
const double face_weight = tri_area
|
const double face_weight = tri_area
|
||||||
/ (1. / 3. * (sizing.get_sizing(v) + sizing.get_sizing(v1) + sizing.get_sizing(v2)));
|
/ (1. / 3. * (sizing.get_sizing(v)
|
||||||
|
+ sizing.get_sizing(v1)
|
||||||
|
+ sizing.get_sizing(v2)));
|
||||||
weight += face_weight;
|
weight += face_weight;
|
||||||
|
|
||||||
const Point_3 centroid = gt_centroid(get(vpm, v), get(vpm, v1), get(vpm, v2));
|
const Point_3 centroid = gt_centroid(get(vpm, v), get(vpm, v1), get(vpm, v2));
|
||||||
|
|
@ -369,305 +372,6 @@ void tangential_relaxation(const VertexRange& vertices,
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
* \ingroup PMP_meshing_grp
|
|
||||||
* applies an iterative area-based tangential smoothing to the given range of vertices based on the
|
|
||||||
* underlying sizing field.
|
|
||||||
* Each vertex `v` is relocated to its weighted centroid, where weights depend on the area of the
|
|
||||||
* adjacent triangle and its averaged vertex sizing values.
|
|
||||||
* The relocation vector is projected back to the tangent plane to the surface at `v`, iteratively.
|
|
||||||
* The connectivity remains unchanged.
|
|
||||||
*
|
|
||||||
* @tparam TriangleMesh model of `FaceGraph` and `VertexListGraph`.
|
|
||||||
* The descriptor types `boost::graph_traits<TriangleMesh>::%face_descriptor`
|
|
||||||
* and `boost::graph_traits<TriangleMesh>::%halfedge_descriptor` must be
|
|
||||||
* models of `Hashable`.
|
|
||||||
* @tparam VertexRange range of `boost::graph_traits<TriangleMesh>::%vertex_descriptor`,
|
|
||||||
* model of `Range`. Its iterator type is `ForwardIterator`.
|
|
||||||
* @tparam SizingFunction model of `PMPSizingField`
|
|
||||||
* @tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters"
|
|
||||||
*
|
|
||||||
* @param vertices the range of vertices which will be relocated by relaxation
|
|
||||||
* @param tm the triangle mesh to which `vertices` belong
|
|
||||||
* @param sizing a map containing sizing field for individual vertices.
|
|
||||||
* Used to derive smoothing weights.
|
|
||||||
* @param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below
|
|
||||||
*
|
|
||||||
* \cgalNamedParamsBegin
|
|
||||||
* \cgalParamNBegin{vertex_point_map}
|
|
||||||
* \cgalParamDescription{a property map associating points to the vertices of `tm`}
|
|
||||||
* \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits<TriangleMesh>::%vertex_descriptor`
|
|
||||||
* as key type and `%Point_3` as value type}
|
|
||||||
* \cgalParamDefault{`boost::get(CGAL::vertex_point, tm)`}
|
|
||||||
* \cgalParamExtra{If this parameter is omitted, an internal property map for `CGAL::vertex_point_t`
|
|
||||||
* must be available in `TriangleMesh`.}
|
|
||||||
* \cgalParamNEnd
|
|
||||||
*
|
|
||||||
* \cgalParamNBegin{geom_traits}
|
|
||||||
* \cgalParamDescription{an instance of a geometric traits class}
|
|
||||||
* \cgalParamType{a class model of `Kernel`}
|
|
||||||
* \cgalParamDefault{a \cgal Kernel deduced from the `Point_3` type, using `CGAL::Kernel_traits`}
|
|
||||||
* \cgalParamExtra{The geometric traits class must be compatible with the vertex `Point_3` type.}
|
|
||||||
* \cgalParamExtra{Exact constructions kernels are not supported by this function.}
|
|
||||||
* \cgalParamNEnd
|
|
||||||
*
|
|
||||||
* \cgalParamNBegin{number_of_iterations}
|
|
||||||
* \cgalParamDescription{the number of smoothing iterations}
|
|
||||||
* \cgalParamType{unsigned int}
|
|
||||||
* \cgalParamDefault{`1`}
|
|
||||||
* \cgalParamNEnd
|
|
||||||
*
|
|
||||||
* \cgalParamNBegin{edge_is_constrained_map}
|
|
||||||
* \cgalParamDescription{a property map containing the constrained-or-not status of each edge of `tm`.
|
|
||||||
* The endpoints of a constrained edge cannot be moved by relaxation, unless `relax_constraints` is `true`.
|
|
||||||
* The endpoints of a constrained polyline can never be moved by relaxation.
|
|
||||||
* \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits<TriangleMesh>::%edge_descriptor`
|
|
||||||
* as key type and `bool` as value type. It must be default constructible.}
|
|
||||||
* \cgalParamDefault{a default property map where no edges are constrained}
|
|
||||||
* \cgalParamExtra{Boundary edges are always considered as constrained edges.}
|
|
||||||
* \cgalParamNEnd
|
|
||||||
*
|
|
||||||
* \cgalParamNBegin{vertex_is_constrained_map}
|
|
||||||
* \cgalParamDescription{a property map containing the constrained-or-not status of each vertex of `tm`.
|
|
||||||
* A constrained vertex cannot be modified during relaxation.}
|
|
||||||
* \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits<PolygonMesh>::%vertex_descriptor`
|
|
||||||
* as key type and `bool` as value type. It must be default constructible.}
|
|
||||||
* \cgalParamDefault{a default property map where no vertices are constrained}
|
|
||||||
* \cgalParamNEnd
|
|
||||||
*
|
|
||||||
* \cgalParamNBegin{relax_constraints}
|
|
||||||
* \cgalParamDescription{If `true`, the end vertices of the edges set as constrained
|
|
||||||
* in `edge_is_constrained_map` and boundary edges move along the
|
|
||||||
* constrained polylines they belong to.}
|
|
||||||
* \cgalParamType{Boolean}
|
|
||||||
* \cgalParamDefault{`false`}
|
|
||||||
* \cgalParamNEnd
|
|
||||||
*
|
|
||||||
* \cgalParamNBegin{allow_move_functor}
|
|
||||||
* \cgalParamDescription{A function object used to determinate if a vertex move should be allowed or not}
|
|
||||||
* \cgalParamType{Unary functor that provides `bool operator()(vertex_descriptor v, Point_3 src, Point_3 tgt)` returning `true`
|
|
||||||
* if the vertex `v` can be moved from `src` to `tgt`; `Point_3` being the value type of the vertex point map }
|
|
||||||
* \cgalParamDefault{If not provided, all moves are allowed.}
|
|
||||||
* \cgalParamNEnd
|
|
||||||
*
|
|
||||||
* \cgalNamedParamsEnd
|
|
||||||
*
|
|
||||||
* \todo check if it should really be a triangle mesh or if a polygon mesh is fine
|
|
||||||
*/
|
|
||||||
template <typename VertexRange,
|
|
||||||
class TriangleMesh,
|
|
||||||
class SizingFunction,
|
|
||||||
typename CGAL_NP_TEMPLATE_PARAMETERS>
|
|
||||||
void tangential_relaxation_with_sizing(const VertexRange& vertices,
|
|
||||||
TriangleMesh& tm,
|
|
||||||
const SizingFunction& sizing,
|
|
||||||
const CGAL_NP_CLASS& np = parameters::default_values())
|
|
||||||
{
|
|
||||||
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
|
|
||||||
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
|
|
||||||
typedef typename boost::graph_traits<TriangleMesh>::edge_descriptor edge_descriptor;
|
|
||||||
|
|
||||||
using parameters::get_parameter;
|
|
||||||
using parameters::choose_parameter;
|
|
||||||
|
|
||||||
typedef typename GetGeomTraits<TriangleMesh, CGAL_NP_CLASS>::type GT;
|
|
||||||
GT gt = choose_parameter(get_parameter(np, internal_np::geom_traits), GT());
|
|
||||||
|
|
||||||
typedef typename GetVertexPointMap<TriangleMesh, CGAL_NP_CLASS>::type VPMap;
|
|
||||||
VPMap vpm = choose_parameter(get_parameter(np, internal_np::vertex_point),
|
|
||||||
get_property_map(vertex_point, tm));
|
|
||||||
|
|
||||||
typedef Static_boolean_property_map<edge_descriptor, false> Default_ECM;
|
|
||||||
typedef typename internal_np::Lookup_named_param_def <
|
|
||||||
internal_np::edge_is_constrained_t,
|
|
||||||
CGAL_NP_CLASS,
|
|
||||||
Static_boolean_property_map<edge_descriptor, false> // default (no constraint)
|
|
||||||
> ::type ECM;
|
|
||||||
ECM ecm = choose_parameter(get_parameter(np, internal_np::edge_is_constrained),
|
|
||||||
Default_ECM());
|
|
||||||
|
|
||||||
typedef typename internal_np::Lookup_named_param_def <
|
|
||||||
internal_np::vertex_is_constrained_t,
|
|
||||||
CGAL_NP_CLASS,
|
|
||||||
Static_boolean_property_map<vertex_descriptor, false> // default (no constraint)
|
|
||||||
> ::type VCM;
|
|
||||||
VCM vcm = choose_parameter(get_parameter(np, internal_np::vertex_is_constrained),
|
|
||||||
Static_boolean_property_map<vertex_descriptor, false>());
|
|
||||||
|
|
||||||
const bool relax_constraints = choose_parameter(get_parameter(np, internal_np::relax_constraints), false);
|
|
||||||
const unsigned int nb_iterations = choose_parameter(get_parameter(np, internal_np::number_of_iterations), 1);
|
|
||||||
|
|
||||||
typedef typename GT::Vector_3 Vector_3;
|
|
||||||
typedef typename GT::Point_3 Point_3;
|
|
||||||
|
|
||||||
auto check_normals = [&](vertex_descriptor v)
|
|
||||||
{
|
|
||||||
bool first_run = true;
|
|
||||||
Vector_3 prev = NULL_VECTOR, first = NULL_VECTOR;
|
|
||||||
halfedge_descriptor first_h = boost::graph_traits<TriangleMesh>::null_halfedge();
|
|
||||||
for (halfedge_descriptor hd : CGAL::halfedges_around_target(v, tm))
|
|
||||||
{
|
|
||||||
if (is_border(hd, tm)) continue;
|
|
||||||
|
|
||||||
Vector_3 n = compute_face_normal(face(hd, tm), tm, np);
|
|
||||||
if (n == CGAL::NULL_VECTOR) //for degenerate faces
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (first_run)
|
|
||||||
{
|
|
||||||
first_run = false;
|
|
||||||
first = n;
|
|
||||||
first_h = hd;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!get(ecm, edge(hd, tm)))
|
|
||||||
if (to_double(n * prev) <= 0)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
prev = n;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (first_run)
|
|
||||||
return true; //vertex incident only to degenerate faces
|
|
||||||
|
|
||||||
if (!get(ecm, edge(first_h, tm)))
|
|
||||||
if (to_double(first * prev) <= 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef typename internal_np::Lookup_named_param_def <
|
|
||||||
internal_np::allow_move_functor_t,
|
|
||||||
CGAL_NP_CLASS,
|
|
||||||
internal::Allow_all_moves// default
|
|
||||||
> ::type Shall_move;
|
|
||||||
Shall_move shall_move = choose_parameter(get_parameter(np, internal_np::allow_move_functor),
|
|
||||||
internal::Allow_all_moves());
|
|
||||||
|
|
||||||
for (unsigned int nit = 0; nit < nb_iterations; ++nit)
|
|
||||||
{
|
|
||||||
#ifdef CGAL_PMP_TANGENTIAL_RELAXATION_VERBOSE
|
|
||||||
std::cout << "\r\t(Tangential relaxation iteration " << (nit + 1) << " / ";
|
|
||||||
std::cout << nb_iterations << ") ";
|
|
||||||
std::cout.flush();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef std::tuple<vertex_descriptor, Vector_3, Point_3> VNP;
|
|
||||||
std::vector< VNP > barycenters;
|
|
||||||
auto gt_barycenter = gt.construct_barycenter_3_object();
|
|
||||||
auto gt_centroid = gt.construct_centroid_3_object();
|
|
||||||
auto gt_area = gt.compute_area_3_object();
|
|
||||||
|
|
||||||
// at each vertex, compute vertex normal
|
|
||||||
std::unordered_map<vertex_descriptor, Vector_3> vnormals;
|
|
||||||
compute_vertex_normals(tm, boost::make_assoc_property_map(vnormals), np);
|
|
||||||
|
|
||||||
// at each vertex, compute centroids of neighbouring faces weighted by
|
|
||||||
// area and sizing field
|
|
||||||
for(vertex_descriptor v : vertices)
|
|
||||||
{
|
|
||||||
if (get(vcm, v) || CGAL::internal::is_isolated(v, tm))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// collect hedges to detect if we have to handle boundary cases
|
|
||||||
std::vector<halfedge_descriptor> interior_hedges, border_halfedges;
|
|
||||||
for(halfedge_descriptor h : halfedges_around_target(v, tm))
|
|
||||||
{
|
|
||||||
if (is_border_edge(h, tm) || get(ecm, edge(h, tm)))
|
|
||||||
border_halfedges.push_back(h);
|
|
||||||
else
|
|
||||||
interior_hedges.push_back(h);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (border_halfedges.empty())
|
|
||||||
{
|
|
||||||
const Vector_3& vn = vnormals.at(v);
|
|
||||||
Vector_3 move = CGAL::NULL_VECTOR;
|
|
||||||
double weight = 0;
|
|
||||||
for(halfedge_descriptor h :interior_hedges)
|
|
||||||
{
|
|
||||||
// calculate weight
|
|
||||||
// need v, v1 and v2
|
|
||||||
const vertex_descriptor v1 = target(next(h, tm), tm);
|
|
||||||
const vertex_descriptor v2 = source(h, tm);
|
|
||||||
|
|
||||||
const double tri_area = gt_area(get(vpm, v), get(vpm, v1), get(vpm, v2));
|
|
||||||
const double face_weight = tri_area
|
|
||||||
/ (1. / 3. * (sizing.get_sizing(v) + sizing.get_sizing(v1) + sizing.get_sizing(v2)));
|
|
||||||
weight += face_weight;
|
|
||||||
|
|
||||||
const Point_3 centroid = gt_centroid(get(vpm, v), get(vpm, v1), get(vpm, v2));
|
|
||||||
move = move + Vector_3(get(vpm, v), centroid) * face_weight;
|
|
||||||
}
|
|
||||||
move = move / weight; //todo ip: what if weight ends up being close to 0?
|
|
||||||
|
|
||||||
barycenters.emplace_back(v, vn, get(vpm, v) + move);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!relax_constraints) continue;
|
|
||||||
Vector_3 vn(NULL_VECTOR);
|
|
||||||
|
|
||||||
if (border_halfedges.size() == 2)// corners are constrained
|
|
||||||
{
|
|
||||||
vertex_descriptor ph0 = source(border_halfedges[0], tm);
|
|
||||||
vertex_descriptor ph1 = source(border_halfedges[1], tm);
|
|
||||||
double dot = to_double(Vector_3(get(vpm, v), get(vpm, ph0))
|
|
||||||
* Vector_3(get(vpm, v), get(vpm, ph1)));
|
|
||||||
// \todo shouldn't it be an input parameter?
|
|
||||||
//check squared cosine is < 0.25 (~120 degrees)
|
|
||||||
if (0.25 < dot*dot / ( squared_distance(get(vpm,ph0), get(vpm, v)) *
|
|
||||||
squared_distance(get(vpm,ph1), get(vpm, v))) )
|
|
||||||
barycenters.emplace_back(v, vn,
|
|
||||||
gt_barycenter(get(vpm, ph0), 0.25, get(vpm, ph1), 0.25, get(vpm, v), 0.5));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// compute moves
|
|
||||||
typedef std::pair<vertex_descriptor, Point_3> VP_pair;
|
|
||||||
std::vector< std::pair<vertex_descriptor, Point_3> > new_locations;
|
|
||||||
new_locations.reserve(barycenters.size());
|
|
||||||
for(const VNP& vnp : barycenters)
|
|
||||||
{
|
|
||||||
vertex_descriptor v = std::get<0>(vnp);
|
|
||||||
const Point_3& pv = get(vpm, v);
|
|
||||||
const Vector_3& nv = std::get<1>(vnp);
|
|
||||||
const Point_3& qv = std::get<2>(vnp); //barycenter at v
|
|
||||||
|
|
||||||
new_locations.emplace_back(v, qv + (nv * Vector_3(qv, pv)) * nv);
|
|
||||||
}
|
|
||||||
|
|
||||||
// perform moves
|
|
||||||
for(const VP_pair& vp : new_locations)
|
|
||||||
{
|
|
||||||
const Point_3 initial_pos = get(vpm, vp.first); // make a copy on purpose
|
|
||||||
const Vector_3 move(initial_pos, vp.second);
|
|
||||||
|
|
||||||
put(vpm, vp.first, vp.second);
|
|
||||||
|
|
||||||
//check that no inversion happened
|
|
||||||
double frac = 1.;
|
|
||||||
while (frac > 0.03 //5 attempts maximum
|
|
||||||
&& ( !check_normals(vp.first)
|
|
||||||
|| !shall_move(vp.first, initial_pos, get(vpm, vp.first)))) //if a face has been inverted
|
|
||||||
{
|
|
||||||
frac = 0.5 * frac;
|
|
||||||
put(vpm, vp.first, initial_pos + frac * move);//shorten the move by 2
|
|
||||||
}
|
|
||||||
if (frac <= 0.02)
|
|
||||||
put(vpm, vp.first, initial_pos);//cancel move
|
|
||||||
}
|
|
||||||
}//end for loop (nit == nb_iterations)
|
|
||||||
|
|
||||||
#ifdef CGAL_PMP_TANGENTIAL_RELAXATION_VERBOSE
|
|
||||||
std::cout << "\rTangential relaxation : "
|
|
||||||
<< nb_iterations << " iterations done." << std::endl;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \ingroup PMP_meshing_grp
|
* \ingroup PMP_meshing_grp
|
||||||
* applies `tangential_relaxation()` to all the vertices of `tm`.
|
* applies `tangential_relaxation()` to all the vertices of `tm`.
|
||||||
|
|
@ -680,16 +384,6 @@ void tangential_relaxation(TriangleMesh& tm,
|
||||||
tangential_relaxation(vertices(tm), tm, np);
|
tangential_relaxation(vertices(tm), tm, np);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class TriangleMesh,
|
|
||||||
typename SizingFunction,
|
|
||||||
typename CGAL_NP_TEMPLATE_PARAMETERS>
|
|
||||||
void tangential_relaxation_with_sizing(TriangleMesh& tm,
|
|
||||||
const SizingFunction& sizing,
|
|
||||||
const CGAL_NP_CLASS& np = parameters::default_values())
|
|
||||||
{
|
|
||||||
tangential_relaxation(vertices(tm), tm, sizing, np);
|
|
||||||
}
|
|
||||||
|
|
||||||
} } // CGAL::Polygon_mesh_processing
|
} } // CGAL::Polygon_mesh_processing
|
||||||
|
|
||||||
#endif //CGAL_POLYGON_MESH_PROCESSING_TANGENTIAL_RELAXATION_H
|
#endif //CGAL_POLYGON_MESH_PROCESSING_TANGENTIAL_RELAXATION_H
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue