Merge branch 'Weights-v2.0-GF-5.5.x' into Weights-v2.0-GF-master

This commit is contained in:
Mael Rouxel-Labbé 2022-11-07 14:23:29 +01:00
commit 83b0b0bdef
60 changed files with 6434 additions and 6452 deletions

View File

@ -587,8 +587,7 @@ namespace Barycentric_coordinates {
const auto& p0 = m_domain.vertex(neighbors[jm]);
const auto& p1 = m_domain.vertex(neighbors[j]);
const auto& p2 = m_domain.vertex(neighbors[jp]);
const FT w = -Weights::cotangent_weight(
p0, p1, p2, query, m_traits) / FT(2);
const FT w = -Weights::cotangent_weight(p0, p1, p2, query, m_traits) / FT(2);
W -= w;
if (m_domain.is_on_boundary(idx)) {

View File

@ -529,22 +529,19 @@ private:
pj = p_j;
pk = p_k;
const double cotan_i = CGAL::to_double(
CGAL::Weights::cotangent(pk, pi, pj, traits));
const double cotan_i = CGAL::to_double(CGAL::Weights::cotangent(pk, pi, pj, traits));
m_cotan_matrix.add_coef(j, k, -(1./2) * cotan_i);
m_cotan_matrix.add_coef(k, j, -(1./2) * cotan_i);
m_cotan_matrix.add_coef(j, j, (1./2) * cotan_i);
m_cotan_matrix.add_coef(k, k, (1./2) * cotan_i);
const double cotan_j = CGAL::to_double(
CGAL::Weights::cotangent(pk, pj, pi, traits));
const double cotan_j = CGAL::to_double(CGAL::Weights::cotangent(pk, pj, pi, traits));
m_cotan_matrix.add_coef(i, k, -(1./2) * cotan_j);
m_cotan_matrix.add_coef(k, i, -(1./2) * cotan_j);
m_cotan_matrix.add_coef(i, i, (1./2) * cotan_j);
m_cotan_matrix.add_coef(k, k, (1./2) * cotan_j);
const double cotan_k = CGAL::to_double(
CGAL::Weights::cotangent(pj, pk, pi, traits));
const double cotan_k = CGAL::to_double(CGAL::Weights::cotangent(pj, pk, pi, traits));
m_cotan_matrix.add_coef(i, j, -(1./2) * cotan_k);
m_cotan_matrix.add_coef(j, i, -(1./2) * cotan_k);
m_cotan_matrix.add_coef(i, i, (1./2) * cotan_k);
@ -553,8 +550,7 @@ private:
const Vector_3 v_ij = construct_vector(p_i, p_j);
const Vector_3 v_ik = construct_vector(p_i, p_k);
const Vector_3 cross = cross_product(v_ij, v_ik);
const double norm_cross = CGAL::sqrt(
CGAL::to_double(scalar_product(cross, cross)));
const double norm_cross = CGAL::sqrt(CGAL::to_double(scalar_product(cross, cross)));
//double area_face = CGAL::Polygon_mesh_processing::face_area(f,tm);
//cross is 2*area

View File

@ -1058,7 +1058,7 @@ private:
*/
FT cotangent(const FT& value) const
{
return FT(1/std::tan(CGAL::to_double(value)));
return FT(1./std::tan(CGAL::to_double(value)));
}
/**

View File

@ -41,18 +41,18 @@ namespace internal {
typename TriangleMesh,
typename VertexRange,
typename VertexPointMap>
bool fair(TriangleMesh& tmesh,
const VertexRange& vertices,
SparseLinearSolver solver,
WeightCalculator weight_calculator,
unsigned int continuity,
VertexPointMap vpmap)
{
CGAL::Polygon_mesh_processing::internal::Fair_Polyhedron_3
<TriangleMesh, SparseLinearSolver, WeightCalculator, VertexPointMap>
fair_functor(tmesh, vpmap, weight_calculator);
return fair_functor.fair(vertices, solver, continuity);
}
bool fair(TriangleMesh& tmesh,
const VertexRange& vertices,
SparseLinearSolver solver,
WeightCalculator weight_calculator,
unsigned int continuity,
VertexPointMap vpmap)
{
CGAL::Polygon_mesh_processing::internal::Fair_Polyhedron_3
<TriangleMesh, SparseLinearSolver, WeightCalculator, VertexPointMap>
fair_functor(tmesh, vpmap, weight_calculator);
return fair_functor.fair(vertices, solver, continuity);
}
} //end namespace internal
@ -161,25 +161,27 @@ namespace internal {
#endif
typedef typename GetVertexPointMap < TriangleMesh, NamedParameters>::type VPMap;
VPMap vpmap = choose_parameter(get_parameter(np, internal_np::vertex_point),
get_property_map(vertex_point, tmesh));
typedef typename GetGeomTraits < TriangleMesh, NamedParameters>::type GT;
GT gt = choose_parameter<GT>(get_parameter(np, internal_np::geom_traits));
// Cotangent_weight_with_voronoi_area_fairing has been changed to the version:
// Cotangent_weight_with_voronoi_area_fairing_secure to avoid imprecisions from
// Secure_cotangent_weight_with_voronoi_area to avoid imprecisions from
// the issue #4706 - https://github.com/CGAL/cgal/issues/4706.
typedef CGAL::Weights::Secure_cotangent_weight_with_voronoi_area<TriangleMesh, VPMap> Default_weight_calculator;
VPMap vpmap_ = choose_parameter(get_parameter(np, internal_np::vertex_point),
get_property_map(vertex_point, tmesh));
typedef CGAL::Weights::Secure_cotangent_weight_with_voronoi_area<TriangleMesh, VPMap, GT> Default_weight_calculator;
return internal::fair(tmesh, vertices,
choose_parameter<Default_solver>(get_parameter(np, internal_np::sparse_linear_solver)),
choose_parameter(get_parameter(np, internal_np::weight_calculator), Default_weight_calculator(tmesh, vpmap_)),
choose_parameter(get_parameter(np, internal_np::weight_calculator), Default_weight_calculator(tmesh, vpmap, gt)),
choose_parameter(get_parameter(np, internal_np::fairing_continuity), 1),
vpmap_);
vpmap);
}
} //end namespace Polygon_mesh_processing
} // namespace Polygon_mesh_processing
} //end namespace CGAL
} // namespace CGAL
#include <CGAL/enable_warnings.h>

View File

@ -383,9 +383,9 @@ class Weight_incomplete
private:
template<class Point_3, class LookupTable>
Weight_incomplete(const std::vector<Point_3>& P,
const std::vector<Point_3>& Q,
int i, int j, int k,
const LookupTable& lambda)
const std::vector<Point_3>& Q,
int i, int j, int k,
const LookupTable& lambda)
: weight(P,Q,i,j,k,lambda), patch_size(1)
{ }
@ -442,9 +442,9 @@ struct Weight_calculator
template<class Point_3, class LookupTable>
Weight operator()(const std::vector<Point_3>& P,
const std::vector<Point_3>& Q,
int i, int j, int k,
const LookupTable& lambda) const
const std::vector<Point_3>& Q,
int i, int j, int k,
const LookupTable& lambda) const
{
if( !is_valid(P,i,j,k) )
{ return Weight::NOT_VALID(); }

View File

@ -78,7 +78,7 @@ public:
vimap_(get(Vertex_local_index(), mesh_)),
scale_volume_after_smoothing(true),
traits_(traits),
weight_calculator_(mesh, vpmap)
weight_calculator_(mesh_, vpmap_, traits_, false /*no clamping*/, false /*no bounding from below*/)
{ }
template<typename FaceRange>
@ -177,7 +177,8 @@ public:
if(is_source_constrained && is_target_constrained)
continue;
const FT Lij = weight_calculator_(hi);
// Cotangent_weight returns (cot(beta) + cot(gamma)) / 2
const FT Lij = FT(2) * weight_calculator_(hi);
const std::size_t i_source = get(vimap_, v_source);
const std::size_t i_target = get(vimap_, v_target);
@ -185,14 +186,14 @@ public:
// note that these constraints create asymmetry in the matrix
if(!is_source_constrained)
{
stiffness_elements.push_back(Triplet(i_source, i_target, Lij));
diag_coeff.insert(std::make_pair(i_source, 0)).first->second -= Lij;
stiffness_elements.emplace_back(i_source, i_target, Lij);
diag_coeff.emplace(i_source, 0).first->second -= Lij;
}
if(!is_target_constrained)
{
stiffness_elements.push_back(Triplet(i_target, i_source, Lij));
diag_coeff.insert(std::make_pair(i_target, 0)).first->second -= Lij;
stiffness_elements.emplace_back(i_target, i_source, Lij);
diag_coeff.emplace(i_target, 0).first->second -= Lij;
}
}
}
@ -200,7 +201,7 @@ public:
typename std::unordered_map<std::size_t, double>::iterator it = diag_coeff.begin(),
end = diag_coeff.end();
for(; it!=end; ++it)
stiffness_elements.push_back(Triplet(it->first, it->first, it->second));
stiffness_elements.emplace_back(it->first, it->first, it->second);
}
void update_mesh_no_scaling(const Eigen_vector& Xx, const Eigen_vector& Xy, const Eigen_vector& Xz)
@ -368,8 +369,8 @@ private:
std::vector<double> diagonal_; // index of vector -> index of vimap_
std::vector<bool> constrained_flags_;
const GeomTraits& traits_;
const CGAL::Weights::Edge_cotangent_weight<TriangleMesh, VertexPointMap> weight_calculator_;
GeomTraits traits_;
const CGAL::Weights::Cotangent_weight<TriangleMesh, VertexPointMap, GeomTraits> weight_calculator_;
};
} // internal

View File

@ -725,14 +725,15 @@ bool Polyhedron_demo_hole_filling_plugin::fill
use_delaunay_triangulation(use_DT)));
}
else {
auto pmap = get_property_map(CGAL::vertex_point, poly);
auto vpm = get_property_map(CGAL::vertex_point, poly);
auto weight_calc = CGAL::Weights::Secure_cotangent_weight_with_voronoi_area<Face_graph, decltype(vpm), EPICK>(poly, vpm, EPICK());
success = std::get<0>(CGAL::Polygon_mesh_processing::triangulate_refine_and_fair_hole(poly,
it, std::back_inserter(patch), CGAL::Emptyset_iterator(),
CGAL::parameters::
weight_calculator(CGAL::Weights::Secure_cotangent_weight_with_voronoi_area<Face_graph, decltype(pmap)>(poly, pmap)).
density_control_factor(alpha).
fairing_continuity(continuity).
use_delaunay_triangulation(use_DT)));
CGAL::parameters::weight_calculator(weight_calc).
density_control_factor(alpha).
fairing_continuity(continuity).
use_delaunay_triangulation(use_DT)));
}
if(!success) { print_message("Error: fairing is not successful, only triangulation and refinement are applied!"); }

View File

@ -55,49 +55,72 @@ enum Deformation_algorithm_tag
/// @cond CGAL_DOCUMENT_INTERNAL
namespace internal {
template<class TriangleMesh, Deformation_algorithm_tag deformation_algorithm_tag>
// property map that create a Simple_cartesian<double>::Point_3
// on the fly in order the deformation class to be used
// with points with minimal requirements
template <class Vertex_point_map>
struct SC_on_the_fly_pmap
: public Vertex_point_map
{
typedef boost::readable_property_map_tag category;
typedef CGAL::Simple_cartesian<double>::Point_3 value_type;
typedef value_type reference;
typedef typename boost::property_traits<Vertex_point_map>::key_type key_type;
SC_on_the_fly_pmap(Vertex_point_map base):
Vertex_point_map(base) {}
friend value_type
get(const SC_on_the_fly_pmap map, key_type k)
{
typename boost::property_traits<Vertex_point_map>::reference base=
get(static_cast<const Vertex_point_map&>(map), k);
return value_type(base[0], base[1], base[2]);
}
};
template<typename TriangleMesh, typename VertexPointMap,
Deformation_algorithm_tag deformation_algorithm_tag>
struct Types_selectors;
template<class TriangleMesh>
struct Types_selectors<TriangleMesh, CGAL::SPOKES_AND_RIMS> {
template<typename TriangleMesh, typename VertexPointMap>
struct Types_selectors<TriangleMesh, VertexPointMap, CGAL::SPOKES_AND_RIMS>
{
typedef SC_on_the_fly_pmap<VertexPointMap> Wrapped_VertexPointMap;
typedef CGAL::Weights::Single_cotangent_weight<TriangleMesh, Wrapped_VertexPointMap> Weight_calculator;
// Get weight from the weight interface.
typedef CGAL::Weights::Single_cotangent_weight<TriangleMesh> Weight_calculator;
struct ARAP_visitor{
template <class VertexPointMap>
struct ARAP_visitor
{
void init(TriangleMesh, VertexPointMap){}
void rotation_matrix_pre(
typename boost::graph_traits<TriangleMesh>::vertex_descriptor,
TriangleMesh&){}
void rotation_matrix_pre(typename boost::graph_traits<TriangleMesh>::vertex_descriptor,
TriangleMesh&){}
template <class Square_matrix_3>
void update_covariance_matrix(
Square_matrix_3&,
const Square_matrix_3&){}
void update_covariance_matrix(Square_matrix_3&,
const Square_matrix_3&){}
void set_sre_arap_alpha(double){}
};
};
template<class TriangleMesh>
struct Types_selectors<TriangleMesh, CGAL::ORIGINAL_ARAP> {
template<class TriangleMesh, typename VertexPointMap>
struct Types_selectors<TriangleMesh, VertexPointMap, CGAL::ORIGINAL_ARAP>
{
typedef SC_on_the_fly_pmap<VertexPointMap> Wrapped_VertexPointMap;
typedef CGAL::Weights::Cotangent_weight<TriangleMesh, Wrapped_VertexPointMap> Weight_calculator;
// Get weight from the weight interface.
typedef CGAL::Weights::Cotangent_weight<TriangleMesh> Weight_calculator;
typedef typename Types_selectors<TriangleMesh, CGAL::SPOKES_AND_RIMS>
::ARAP_visitor ARAP_visitor;
typedef typename Types_selectors<TriangleMesh, VertexPointMap, CGAL::SPOKES_AND_RIMS>::ARAP_visitor ARAP_visitor;
};
template<class TriangleMesh>
struct Types_selectors<TriangleMesh, CGAL::SRE_ARAP> {
template<class TriangleMesh, typename VertexPointMap>
struct Types_selectors<TriangleMesh, VertexPointMap, CGAL::SRE_ARAP>
{
typedef SC_on_the_fly_pmap<VertexPointMap> Wrapped_VertexPointMap;
typedef CGAL::Weights::Cotangent_weight<TriangleMesh, Wrapped_VertexPointMap> Weight_calculator;
// Get weight from the weight interface.
typedef CGAL::Weights::Cotangent_weight<TriangleMesh> Weight_calculator;
class ARAP_visitor{
class ARAP_visitor
{
double m_nb_edges_incident;
double m_area;
double m_alpha;
@ -105,7 +128,6 @@ struct Types_selectors<TriangleMesh, CGAL::SRE_ARAP> {
public:
ARAP_visitor(): m_alpha(0.02) {}
template<class VertexPointMap>
void init(TriangleMesh triangle_mesh, const VertexPointMap& vpmap)
{
// calculate area
@ -144,31 +166,6 @@ struct Types_selectors<TriangleMesh, CGAL::SRE_ARAP> {
};
};
// property map that create a Simple_cartesian<double>::Point_3
// on the fly in order the deformation class to be used
// with points with minimal requirements
template <class Vertex_point_map>
struct SC_on_the_fly_pmap
: public Vertex_point_map
{
typedef boost::readable_property_map_tag category;
typedef CGAL::Simple_cartesian<double>::Point_3 value_type;
typedef value_type reference;
typedef typename boost::property_traits<Vertex_point_map>::key_type key_type;
SC_on_the_fly_pmap(Vertex_point_map base):
Vertex_point_map(base) {}
friend value_type
get(const SC_on_the_fly_pmap map, key_type k)
{
typename boost::property_traits<Vertex_point_map>::reference base=
get(static_cast<const Vertex_point_map&>(map), k);
return value_type(base[0], base[1], base[2]);
}
};
}//namespace internal
/// @endcond
@ -235,17 +232,6 @@ public:
typedef HIM Hedge_index_map;
#endif
// weight calculator
#ifndef DOXYGEN_RUNNING
typedef typename Default::Get<
WC,
typename internal::Types_selectors<TM, TAG>::Weight_calculator
>::type Weight_calculator;
#else
/// weight calculator functor type
typedef WC Weight_calculator;
#endif
// sparse linear solver
#ifndef DOXYGEN_RUNNING
typedef typename Default::Get<
@ -290,6 +276,17 @@ public:
typedef VPM Vertex_point_map;
#endif
// weight calculator
#ifndef DOXYGEN_RUNNING
typedef typename Default::Get<
WC,
typename internal::Types_selectors<Triangle_mesh, Vertex_point_map, TAG>::Weight_calculator
>::type Weight_calculator;
#else
/// weight calculator functor type
typedef WC Weight_calculator;
#endif
/// The type for vertex descriptor
typedef typename boost::graph_traits<Triangle_mesh>::vertex_descriptor vertex_descriptor;
/// The type for halfedge descriptor
@ -304,8 +301,6 @@ public:
private:
typedef Surface_mesh_deformation<TM, VIM, HIM, TAG, WC, ST, CR> Self;
// Repeat Triangle_mesh types
typedef typename boost::graph_traits<Triangle_mesh>::vertex_iterator vertex_iterator;
typedef typename boost::graph_traits<Triangle_mesh>::halfedge_iterator halfedge_iterator;
typedef typename boost::graph_traits<Triangle_mesh>::in_edge_iterator in_edge_iterator;
typedef typename boost::graph_traits<Triangle_mesh>::out_edge_iterator out_edge_iterator;
@ -340,12 +335,11 @@ private:
bool last_preprocess_successful; ///< stores the result of last call to preprocess()
Vertex_point_map vertex_point_map;
Weight_calculator weight_calculator;
Vertex_point_map vertex_point_map;
public:
typename internal::Types_selectors<TM, TAG>::ARAP_visitor arap_visitor;
typename internal::Types_selectors<Triangle_mesh, Vertex_point_map, TAG>::ARAP_visitor arap_visitor;
private:
#ifdef CGAL_DEFORM_MESH_USE_EXPERIMENTAL_SCALE
@ -359,68 +353,13 @@ public:
public:
/// \cond SKIP_FROM_MANUAL
//vertex_point_map set by default
Surface_mesh_deformation(Triangle_mesh& triangle_mesh,
Vertex_index_map vertex_index_map,
Hedge_index_map hedge_index_map)
: m_triangle_mesh(triangle_mesh),
vertex_index_map(vertex_index_map),
hedge_index_map(hedge_index_map),
ros_id_map(std::vector<std::size_t>(num_vertices(triangle_mesh), (std::numeric_limits<std::size_t>::max)() )),
is_roi_map(std::vector<bool>(num_vertices(triangle_mesh), false)),
is_ctrl_map(std::vector<bool>(num_vertices(triangle_mesh), false)),
m_iterations(5), m_tolerance(1e-4),
need_preprocess_factorization(true),
need_preprocess_region_of_solution(true),
last_preprocess_successful(false),
weight_calculator(Weight_calculator()),
vertex_point_map(get(vertex_point, triangle_mesh))
{
init();
}
//vertex_point_map and hedge_index_map set by default
Surface_mesh_deformation(Triangle_mesh& triangle_mesh,
Vertex_index_map vertex_index_map)
: m_triangle_mesh(triangle_mesh),
vertex_index_map(vertex_index_map),
hedge_index_map(CGAL::get_initialized_halfedge_index_map(triangle_mesh)),
ros_id_map(std::vector<std::size_t>(num_vertices(triangle_mesh), (std::numeric_limits<std::size_t>::max)() )),
is_roi_map(std::vector<bool>(num_vertices(triangle_mesh), false)),
is_ctrl_map(std::vector<bool>(num_vertices(triangle_mesh), false)),
m_iterations(5), m_tolerance(1e-4),
need_preprocess_factorization(true),
need_preprocess_region_of_solution(true),
last_preprocess_successful(false),
weight_calculator(Weight_calculator()),
vertex_point_map(get(vertex_point, triangle_mesh))
{
init();
}
//vertex_point_map, hedge_index_map and vertex_index_map set by default
Surface_mesh_deformation(Triangle_mesh& triangle_mesh)
: m_triangle_mesh(triangle_mesh),
vertex_index_map(CGAL::get_initialized_vertex_index_map(triangle_mesh)),
hedge_index_map(CGAL::get_initialized_halfedge_index_map(triangle_mesh)),
ros_id_map(std::vector<std::size_t>(num_vertices(triangle_mesh), (std::numeric_limits<std::size_t>::max)() )),
is_roi_map(std::vector<bool>(num_vertices(triangle_mesh), false)),
is_ctrl_map(std::vector<bool>(num_vertices(triangle_mesh), false)),
m_iterations(5), m_tolerance(1e-4),
need_preprocess_factorization(true),
need_preprocess_region_of_solution(true),
last_preprocess_successful(false),
weight_calculator(Weight_calculator()),
vertex_point_map(get(vertex_point, triangle_mesh))
{
init();
}
// Constructor with all the parameters provided
Surface_mesh_deformation(Triangle_mesh& triangle_mesh,
Vertex_index_map vertex_index_map,
Hedge_index_map hedge_index_map,
Vertex_point_map vertex_point_map,
Weight_calculator weight_calculator = Weight_calculator())
Weight_calculator weight_calculator)
: m_triangle_mesh(triangle_mesh),
vertex_index_map(vertex_index_map),
hedge_index_map(hedge_index_map),
@ -431,13 +370,47 @@ public:
need_preprocess_factorization(true),
need_preprocess_region_of_solution(true),
last_preprocess_successful(false),
weight_calculator(weight_calculator),
vertex_point_map(vertex_point_map)
vertex_point_map(vertex_point_map),
weight_calculator(weight_calculator)
{
init();
}
Surface_mesh_deformation(Triangle_mesh& triangle_mesh,
Vertex_index_map vertex_index_map,
Hedge_index_map hedge_index_map,
Vertex_point_map vertex_point_map)
: Surface_mesh_deformation(triangle_mesh,
vertex_index_map,
hedge_index_map,
vertex_point_map,
Weight_calculator(triangle_mesh, internal::SC_on_the_fly_pmap<Vertex_point_map>(vertex_point_map)))
{ }
Surface_mesh_deformation(Triangle_mesh& triangle_mesh,
Vertex_index_map vertex_index_map,
Hedge_index_map hedge_index_map)
: Surface_mesh_deformation(triangle_mesh,
vertex_index_map,
hedge_index_map,
get(vertex_point, triangle_mesh))
{ }
Surface_mesh_deformation(Triangle_mesh& triangle_mesh,
Vertex_index_map vertex_index_map)
: Surface_mesh_deformation(triangle_mesh,
vertex_index_map,
CGAL::get_initialized_halfedge_index_map(triangle_mesh))
{ }
Surface_mesh_deformation(Triangle_mesh& triangle_mesh)
: Surface_mesh_deformation(triangle_mesh,
CGAL::get_initialized_vertex_index_map(triangle_mesh))
{ }
/// \endcond
#if DOXYGEN_RUNNING
#if DOXYGEN_RUNNING
/// \name Construction
/// @{
/**
@ -457,21 +430,17 @@ public:
Vertex_index_map vertex_index_map = unspecified_internal_vertex_index_map,
Hedge_index_map hedge_index_map = unspecified_internal_halfedge_index_map,
Vertex_point_map vertex_point_map = get(boost::vertex_point, triangle_mesh),
Weight_calculator weight_calculator = Weight_calculator());
Weight_calculator weight_calculator = Weight_calculator(triangle_mesh, vertex_point_map));
/// @}
#endif
private:
void init() {
typedef internal::SC_on_the_fly_pmap<Vertex_point_map> Wrapper;
// compute halfedge weights
halfedge_iterator eb, ee;
hedge_weight.reserve(2*num_edges(m_triangle_mesh));
for(std::tie(eb, ee) = halfedges(m_triangle_mesh); eb != ee; ++eb)
{
hedge_weight.push_back(
this->weight_calculator(*eb, m_triangle_mesh, Wrapper(vertex_point_map)));
}
void init()
{
hedge_weight.reserve(num_halfedges(m_triangle_mesh));
for(halfedge_descriptor he : halfedges(m_triangle_mesh))
hedge_weight.push_back(this->weight_calculator(he));
arap_visitor.init(m_triangle_mesh, vertex_point_map);
}
@ -854,7 +823,6 @@ public:
*/
void overwrite_initial_geometry()
{
typedef internal::SC_on_the_fly_pmap<Vertex_point_map> Wrapper;
if(roi.empty()) { return; } // no ROI to overwrite
region_of_solution(); // the roi should be preprocessed since we are using original_position vec
@ -875,13 +843,13 @@ public:
std::size_t id_e = id(he);
if(is_weight_computed[id_e]) { continue; }
hedge_weight[id_e] = weight_calculator(he, m_triangle_mesh, Wrapper(vertex_point_map));
hedge_weight[id_e] = weight_calculator(he);
is_weight_computed[id_e] = true;
halfedge_descriptor e_opp = opposite(he, m_triangle_mesh);
std::size_t id_e_opp = id(e_opp);
hedge_weight[id_e_opp] = weight_calculator(e_opp, m_triangle_mesh, Wrapper(vertex_point_map));
hedge_weight[id_e_opp] = weight_calculator(e_opp);
is_weight_computed[id_e_opp] = true;
}
}

View File

@ -458,6 +458,7 @@ private:
const Faces_vector& faces,
Cot_map ctmap) const
{
// Since we loop faces, we are implicitely defining the weight of border halfedges as 0...
for(face_descriptor fd : faces) {
halfedge_descriptor hd = halfedge(fd, mesh), hdb = hd;

View File

@ -187,7 +187,7 @@ protected:
++next_vertex_v_l;
const Point_3& position_v_l = get(ppmap, *next_vertex_v_l);
return CGAL::Weights::authalic_weight(position_v_k, position_v_j, position_v_l, position_v_i) / NT(2);
return CGAL::Weights::authalic_weight(position_v_l, position_v_j, position_v_k, position_v_i) / NT(2);
}
};

View File

@ -183,7 +183,7 @@ protected:
++next_vertex_v_l;
const Point_3& position_v_l = get(ppmap, *next_vertex_v_l);
return CGAL::Weights::cotangent_weight(position_v_k, position_v_j, position_v_l, position_v_i) / NT(2);
return CGAL::Weights::cotangent_weight(position_v_l, position_v_j, position_v_k, position_v_i) / NT(2);
}
};

View File

@ -454,7 +454,7 @@ private:
double weight;
};
NT determinant(Point_2& v0, Point_2& v1) const
NT determinant(const Point_2& v0, const Point_2& v1) const
{
return (v0.x() * v1.y() - v1.x() * v0.y());
}
@ -465,8 +465,8 @@ private:
const NT det0 = determinant(uv1, uv2);
const NT det1 = determinant(uv2, uv0);
const NT det2 = determinant(uv0, uv1);
const NT det3 = CGAL::determinant(Vector_2<Kernel>(uv1.x()-uv0.x(), uv1.y()-uv0.y()),
Vector_2<Kernel>(uv2.x()-uv0.x(), uv2.y()-uv0.y()));
NT det3 = CGAL::determinant(Vector_2<Kernel>(uv1.x()-uv0.x(), uv1.y()-uv0.y()),
Vector_2<Kernel>(uv2.x()-uv0.x(), uv2.y()-uv0.y()));
CGAL_assertion(det3 > NT(0));
if(det3 <= NT(0))
det3 = NT(1);
@ -503,7 +503,7 @@ private:
{
Neighbor_list NL;
NL.vertex = *v_j;
NL.vector = Vector_3(get(ppmap, v), tmesh.point(*v_j));
NL.vector = Vector_3(get(ppmap, v), get(ppmap, *v_j));
NL.length = sqrt(NL.vector.squared_length());
neighbor_list.push_back(NL);
++neighborsCounter;
@ -617,9 +617,6 @@ private:
const PPM_ref position_v_i = get(ppmap, main_vertex_v_i);
const PPM_ref position_v_j = get(ppmap, *neighbor_vertex_v_j);
const Vector_3 edge = position_v_i - position_v_j;
const NT squared_length = edge * edge;
vertex_around_target_circulator previous_vertex_v_k = neighbor_vertex_v_j;
--previous_vertex_v_k;
const PPM_ref position_v_k = get(ppmap, *previous_vertex_v_k);
@ -628,14 +625,10 @@ private:
++next_vertex_v_l;
const PPM_ref position_v_l = get(ppmap, *next_vertex_v_l);
NT weight = NT(0);
CGAL_assertion(squared_length > NT(0)); // two points are identical!
if(squared_length != NT(0)) {
// This version was commented out to be an alternative weight
// in the original code by authors.
// weight = CGAL::Weights::authalic_weight(position_v_k, position_v_j, position_v_l, position_v_i) / NT(2);
weight = CGAL::Weights::cotangent_weight(position_v_k, position_v_j, position_v_l, position_v_i) / NT(2);
}
// This version was commented out to be an alternative weight in the original code by authors.
// NT weight = CGAL::Weights::authalic_weight(position_v_l, position_v_j, position_v_k, position_v_i) / NT(2);
NT weight = CGAL::Weights::cotangent_weight(position_v_l, position_v_j, position_v_k, position_v_i) / NT(2);
return weight;
}
@ -699,7 +692,7 @@ private:
VertexIndexMap& vimap) const
{
auto vpm = get_const_property_map(CGAL::vertex_point, tmesh);
const CGAL::Weights::Edge_tangent_weight<Triangle_mesh, decltype(vpm)> compute_mvc(tmesh, vpm);
const CGAL::Weights::Edge_tangent_weight<Triangle_mesh, decltype(vpm), Kernel> weight_calc(tmesh, vpm, Kernel());
const int i = get(vimap, v);
@ -709,7 +702,7 @@ private:
for(halfedge_descriptor h : CGAL::halfedges_around_target(v, tmesh))
{
NT w_ij = NT(-1) * compute_mvc(h);
NT w_ij = NT(-1) * weight_calc(h);
// w_ii = - sum of w_ijs
w_ii -= w_ij;

View File

@ -206,7 +206,7 @@ protected:
++next_vertex_v_l;
const Point_3& position_v_l = get(ppmap, *next_vertex_v_l);
return CGAL::Weights::tangent_weight(position_v_k, position_v_j, position_v_l, position_v_i) / NT(2);
return CGAL::Weights::tangent_weight(position_v_l, position_v_j, position_v_k, position_v_i) / NT(2);
}
};

View File

@ -782,9 +782,13 @@ private:
const int i = get(vimap, vi);
const int j = get(vimap, vj);
if (i > j) continue;
const CGAL::Weights::Cotangent_weight<SeamMesh> cotangent_weight;
const NT w_ij = NT(2) * cotangent_weight(hd, mesh, pmap);
if (i > j)
continue;
const CGAL::Weights::Cotangent_weight<SeamMesh, PPM> cotangent_weight(mesh, pmap);
// x2 because Cotangent_weight returns 0.5 * (cot alpha + cot beta)...
const NT w_ij = NT(2) * cotangent_weight(hd);
// ij
M.set_coef(2*i, 2*j, w_ij, true /* new coef */);

View File

@ -235,7 +235,7 @@ public:
typedef typename boost::graph_traits<mTriangleMesh>::edge_iterator edge_iterator;
// Get weight from the weight interface.
typedef CGAL::Weights::Cotangent_weight<mTriangleMesh> Weight_calculator;
typedef CGAL::Weights::Cotangent_weight<mTriangleMesh, mVertexPointMap, Traits> Weight_calculator;
typedef internal::Curve_skeleton<mTriangleMesh,
VertexIndexMap,
@ -383,17 +383,19 @@ public:
Mean_curvature_flow_skeletonization(const TriangleMesh& tmesh,
VertexPointMap vertex_point_map,
const Traits& traits = Traits())
: m_traits(traits), m_weight_calculator(true /* use_clamped_version */)
:
m_tmesh(),
m_tmesh_point_pmap(get(CGAL::vertex_point, m_tmesh)),
m_traits(traits),
m_weight_calculator(m_tmesh, m_tmesh_point_pmap, m_traits, true /* use_clamped_version */)
{
init(tmesh, vertex_point_map);
}
Mean_curvature_flow_skeletonization(const TriangleMesh& tmesh,
const Traits& traits = Traits())
: m_traits(traits), m_weight_calculator(true /* use_clamped_version */)
{
init(tmesh, get(vertex_point, tmesh));
}
: Mean_curvature_flow_skeletonization(tmesh, get(vertex_point, tmesh), traits)
{ }
#endif
/// @} Constructor
@ -871,8 +873,8 @@ private:
typedef std::pair<Input_vertex_descriptor, vertex_descriptor> Vertex_pair;
std::vector<Vertex_pair> v2v;
copy_face_graph(tmesh, m_tmesh,
CGAL::parameters::vertex_to_vertex_output_iterator(
std::back_inserter(v2v)).vertex_point_map(vpm));
CGAL::parameters::vertex_to_vertex_output_iterator(std::back_inserter(v2v))
.vertex_point_map(vpm));
// copy input vertices to keep correspondence
for(const Vertex_pair& vp : v2v)
@ -919,11 +921,9 @@ private:
void compute_edge_weight()
{
m_edge_weight.clear();
m_edge_weight.reserve(2 * num_edges(m_tmesh));
m_edge_weight.reserve(num_halfedges(m_tmesh));
for(halfedge_descriptor hd : halfedges(m_tmesh))
{
m_edge_weight.push_back(m_weight_calculator(hd, m_tmesh, m_tmesh_point_pmap));
}
m_edge_weight.push_back(m_weight_calculator(hd));
}
/// Assemble the left hand side.

View File

@ -101,7 +101,7 @@ a model of `AnalyticWeightTraits_3` for 3D points
\endverbatim
This weight is computed as
\f$w = \frac{d_2^a A_1 - d^a B + d_1^a A_2}{A_1 A_2}\f$
\f$w = \frac{d_2^a A_0 - d^a B + d_0^a A_2}{A_0 A_2}\f$
with notations shown in the figure below and \f$a\f$ any real number
being the power parameter.
@ -142,7 +142,7 @@ a model of `AnalyticWeightTraits_3` for 3D points
\endverbatim
This weight is computed as
\f$w = \frac{C}{A_1 A_2}\f$
\f$w = \frac{C}{A_0 A_2}\f$
with notations shown in the figure below.
Here, `q` is a query point and the points `p0`, `p1`, and `p2` are its neighbors.
@ -207,10 +207,10 @@ a model of `AnalyticWeightTraits_3` for 3D points
\endverbatim
This weight is computed as
\f$w = \pm 2 \sqrt{\frac{2 (d_1 d_2 - D)}{(d d_1 + D_1)(d d_2 + D_2)}}\f$
\f$w = \pm 2 \sqrt{\frac{2 (d_0 d_2 - D)}{(d d_0 + D_0)(d d_2 + D_2)}}\f$
with notations shown in the figure below and dot products
\f$D_1 = (p_0 - q) \cdot (p_1 - q)\f$,
\f$D_0 = (p_0 - q) \cdot (p_1 - q)\f$,
\f$D_2 = (p_1 - q) \cdot (p_2 - q)\f$, and
\f$D = (p_0 - q) \cdot (p_2 - q)\f$.
@ -247,11 +247,11 @@ a model of `AnalyticWeightTraits_3` for 3D points
This weight is computed as
\f$w = 2 \frac{t_1 + t_2}{d}\f$, where
\f$t_1 = \frac{2 A_1}{d d_1 + D_1}\f$ and
\f$t_1 = \frac{2 A_0}{d d_0 + D_0}\f$ and
\f$t_2 = \frac{2 A_2}{d d_2 + D_2}\f$
with notations shown in the figure below and dot products
\f$D_1 = (p_0 - q) \cdot (p_1 - q)\f$ and
\f$D_0 = (p_0 - q) \cdot (p_1 - q)\f$ and
\f$D_2 = (p_1 - q) \cdot (p_2 - q)\f$.
Here, `q` is a query point and the points `p0`, `p1`, and `p2` are its neighbors.
@ -283,7 +283,7 @@ a model of `AnalyticWeightTraits_3` for 3D points
\endverbatim
This weight is computed as
\f$w = \frac{d_2^2 A_1 - d^2 B + d_1^2 A_2}{A_1 A_2}\f$
\f$w = \frac{d_2^2 A_0 - d^2 B + d_0^2 A_2}{A_0 A_2}\f$
with notations shown in the figure below.
Here, `q` is a query point and the points `p0`, `p1`, and `p2` are its neighbors.

View File

@ -1,235 +1,439 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="501.3px" height="161.7px" viewBox="0 0 501.3 161.7" style="enable-background:new 0 0 501.3 161.7;" xml:space="preserve"
>
<style type="text/css">
<svg
version="1.1"
id="Layer_1"
x="0px"
y="0px"
width="501.3px"
height="161.7px"
viewBox="0 0 501.3 161.7"
style="enable-background:new 0 0 501.3 161.7;"
xml:space="preserve"
sodipodi:docname="discrete_harmonic.svg"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs227" /><sodipodi:namedview
id="namedview225"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="5.4059447"
inkscape:cx="63.171198"
inkscape:cy="80.1895"
inkscape:window-width="1881"
inkscape:window-height="1192"
inkscape:window-x="26"
inkscape:window-y="23"
inkscape:window-maximized="0"
inkscape:current-layer="Layer_1" />
<style
type="text/css"
id="style2">
.st0{fill:none;stroke:#000000;stroke-miterlimit:10;}
.st1{display:none;}
.st2{display:inline;}
</style>
<circle cx="4.4" cy="87.7" r="2.5"/>
<circle cx="96.9" cy="140.6" r="2.5"/>
<circle cx="210" cy="83.4" r="2.5"/>
<circle cx="90.9" cy="21.1" r="2.5"/>
<polyline class="st0" points="90.9,21.1 4.4,87.7 96.9,140.6 90.9,21.1 "/>
<polyline class="st0" points="96.9,140.6 210,83.3 90.9,21.1 "/>
<g class="st1">
<g class="st2">
<path class="st0" d="M68.3,119.4c5.9-8.7,15.6-14.6,26.8-15.2"/>
<g>
<polygon points="71.6,119.1 68.8,118.7 67.1,116.4 66.2,123 "/>
<circle
cx="4.4"
cy="87.7"
r="2.5"
id="circle4" />
<circle
cx="96.9"
cy="140.6"
r="2.5"
id="circle6" />
<circle
cx="210"
cy="83.4"
r="2.5"
id="circle8" />
<circle
cx="90.9"
cy="21.1"
r="2.5"
id="circle10" />
<polyline
class="st0"
points="90.9,21.1 4.4,87.7 96.9,140.6 90.9,21.1 "
id="polyline12" />
<polyline
class="st0"
points="96.9,140.6 210,83.3 90.9,21.1 "
id="polyline14" />
<g
class="st1"
id="g24">
<g
class="st2"
id="g22">
<path
class="st0"
d="M68.3,119.4c5.9-8.7,15.6-14.6,26.8-15.2"
id="path16" />
<g
id="g20">
<polygon
points="71.6,119.1 68.8,118.7 67.1,116.4 66.2,123 "
id="polygon18" />
</g>
</g>
</g>
<g class="st1">
<g class="st2">
<path class="st0" d="M99.2,108c10,2.3,18.8,9.2,23.3,19.2"/>
<g>
<polygon points="100.7,110.9 100.1,108.2 101.6,105.8 95.1,107.3 "/>
<g
class="st1"
id="g34">
<g
class="st2"
id="g32">
<path
class="st0"
d="M99.2,108c10,2.3,18.8,9.2,23.3,19.2"
id="path26" />
<g
id="g30">
<polygon
points="100.7,110.9 100.1,108.2 101.6,105.8 95.1,107.3 "
id="polygon28" />
</g>
</g>
</g>
<g class="st1">
<g class="st2">
<path class="st0" d="M173.1,96.8c-3.6-9.9-2.6-21.3,3.5-30.6"/>
<g>
<polygon points="174.8,94 172.8,96 170,96 174.8,100.6 "/>
<g
class="st1"
id="g44">
<g
class="st2"
id="g42">
<path
class="st0"
d="M173.1,96.8c-3.6-9.9-2.6-21.3,3.5-30.6"
id="path36" />
<g
id="g40">
<polygon
points="174.8,94 172.8,96 170,96 174.8,100.6 "
id="polygon38" />
</g>
</g>
</g>
<g class="st1">
<g class="st2">
<path class="st0" d="M30.4,73.1c5.6,8.9,7.1,20.2,3.1,30.7"/>
<g>
<polygon points="29.3,76.2 30.8,73.8 33.6,73.2 27.9,69.7 "/>
<g
class="st1"
id="g54">
<g
class="st2"
id="g52">
<path
class="st0"
d="M30.4,73.1c5.6,8.9,7.1,20.2,3.1,30.7"
id="path46" />
<g
id="g50">
<polygon
points="29.3,76.2 30.8,73.8 33.6,73.2 27.9,69.7 "
id="polygon48" />
</g>
</g>
</g>
<g class="st1">
<g class="st2">
<path class="st0" d="M117,39.5c-4.5,9.5-13.3,16.8-24.2,19.1"/>
<g>
<polygon points="113.8,40.3 116.6,40.2 118.6,42.2 118.6,35.6 "/>
<g
class="st1"
id="g64">
<g
class="st2"
id="g62">
<path
class="st0"
d="M117,39.5c-4.5,9.5-13.3,16.8-24.2,19.1"
id="path56" />
<g
id="g60">
<polygon
points="113.8,40.3 116.6,40.2 118.6,42.2 118.6,35.6 "
id="polygon58" />
</g>
</g>
</g>
<g class="st1">
<g class="st2">
<path class="st0" d="M88.2,55c-10,0.9-20.2-2.8-27.3-10.7"/>
<g>
<polygon points="85.9,52.6 87.3,55 86.6,57.8 92.3,54.3 "/>
<g
class="st1"
id="g74">
<g
class="st2"
id="g72">
<path
class="st0"
d="M88.2,55c-10,0.9-20.2-2.8-27.3-10.7"
id="path66" />
<g
id="g70">
<polygon
points="85.9,52.6 87.3,55 86.6,57.8 92.3,54.3 "
id="polygon68" />
</g>
</g>
</g>
<g>
<g class="st1">
<path class="st2" d="M84.9,121.7v1.5c-0.4-2-1.1-3.9-1.8-5.7c-0.5-1.5-0.8-2-1.4-2c-0.2,0-0.4,0-0.6,0.1l-0.2-0.5
c0.4-0.4,0.8-0.7,1.4-0.7c0.7,0,1.3,0.3,2,2.4c0.4,1.1,0.9,2.8,1.4,5.4l0.1,0.1l0.1,3.9l-1.4,0.3l-0.2-0.2L84.9,121.7z
M85.4,122.3c1.2-2.1,1.7-3.5,2.5-5.5l-0.2,1.4c-0.2-1.7-0.3-2.3-0.3-2.7c0-0.8,0.4-1.2,0.9-1.2c0.3,0,0.5,0.1,0.6,0.2
c0.1,0.2,0.1,0.4,0.1,0.8c0,1.9-1.8,5.3-3.3,7.8L85.4,122.3z"/>
<g
id="g80">
<g
class="st1"
id="g78">
<path
class="st2"
d="M84.9,121.7v1.5c-0.4-2-1.1-3.9-1.8-5.7c-0.5-1.5-0.8-2-1.4-2c-0.2,0-0.4,0-0.6,0.1l-0.2-0.5 c0.4-0.4,0.8-0.7,1.4-0.7c0.7,0,1.3,0.3,2,2.4c0.4,1.1,0.9,2.8,1.4,5.4l0.1,0.1l0.1,3.9l-1.4,0.3l-0.2-0.2L84.9,121.7z M85.4,122.3c1.2-2.1,1.7-3.5,2.5-5.5l-0.2,1.4c-0.2-1.7-0.3-2.3-0.3-2.7c0-0.8,0.4-1.2,0.9-1.2c0.3,0,0.5,0.1,0.6,0.2 c0.1,0.2,0.1,0.4,0.1,0.8c0,1.9-1.8,5.3-3.3,7.8L85.4,122.3z"
id="path76" />
</g>
</g>
<g>
<g class="st1">
<path class="st2" d="M101,128v-8.8c0-2.6,1.6-4.3,3.8-4.3c1.8,0,3.2,1.1,3.2,3c0,1.4-1,3.3-3.6,3.5l-0.1-0.2c3-0.1,4.7,1,4.7,3.4
c0,2.5-1.9,3.8-3.7,3.8c-1.3,0-2.6-0.6-3.3-2.2l0.2-0.2c0.8,0.7,1.7,1.2,2.8,1.2c1.7,0,2.6-1.1,2.6-2.7c0-1.5-0.8-2.9-3.5-2.8
l-0.2-1c1.7-0.3,2.7-1.3,2.7-2.9c0-1.4-0.7-2.3-1.9-2.3c-1.3,0-2.2,1.2-2.2,3.2v7.7l-0.1,0.3l0.3,4.5l-1.4,0.3l-0.2-0.2L101,128z"
/>
<g
id="g86">
<g
class="st1"
id="g84">
<path
class="st2"
d="M101,128v-8.8c0-2.6,1.6-4.3,3.8-4.3c1.8,0,3.2,1.1,3.2,3c0,1.4-1,3.3-3.6,3.5l-0.1-0.2c3-0.1,4.7,1,4.7,3.4 c0,2.5-1.9,3.8-3.7,3.8c-1.3,0-2.6-0.6-3.3-2.2l0.2-0.2c0.8,0.7,1.7,1.2,2.8,1.2c1.7,0,2.6-1.1,2.6-2.7c0-1.5-0.8-2.9-3.5-2.8 l-0.2-1c1.7-0.3,2.7-1.3,2.7-2.9c0-1.4-0.7-2.3-1.9-2.3c-1.3,0-2.2,1.2-2.2,3.2v7.7l-0.1,0.3l0.3,4.5l-1.4,0.3l-0.2-0.2L101,128z"
id="path82" />
</g>
</g>
<g>
<g class="st1">
<path class="st2" d="M21.8,90v-8.8c0-2.6,1.6-4.3,3.8-4.3c1.8,0,3.2,1.1,3.2,3c0,1.4-1,3.3-3.6,3.5l-0.1-0.2c3-0.1,4.7,1,4.7,3.4
c0,2.5-1.9,3.8-3.7,3.8c-1.3,0-2.6-0.6-3.3-2.2L23,88c0.8,0.7,1.7,1.2,2.8,1.2c1.7,0,2.6-1.1,2.6-2.7c0-1.5-0.8-2.9-3.5-2.8
l-0.2-1c1.7-0.3,2.7-1.3,2.7-2.9c0-1.4-0.7-2.3-1.9-2.3c-1.3,0-2.2,1.2-2.2,3.2v7.7l-0.1,0.3l0.3,4.5l-1.4,0.3l-0.2-0.2L21.8,90z"
/>
<g
id="g92">
<g
class="st1"
id="g90">
<path
class="st2"
d="M21.8,90v-8.8c0-2.6,1.6-4.3,3.8-4.3c1.8,0,3.2,1.1,3.2,3c0,1.4-1,3.3-3.6,3.5l-0.1-0.2c3-0.1,4.7,1,4.7,3.4 c0,2.5-1.9,3.8-3.7,3.8c-1.3,0-2.6-0.6-3.3-2.2L23,88c0.8,0.7,1.7,1.2,2.8,1.2c1.7,0,2.6-1.1,2.6-2.7c0-1.5-0.8-2.9-3.5-2.8 l-0.2-1c1.7-0.3,2.7-1.3,2.7-2.9c0-1.4-0.7-2.3-1.9-2.3c-1.3,0-2.2,1.2-2.2,3.2v7.7l-0.1,0.3l0.3,4.5l-1.4,0.3l-0.2-0.2L21.8,90z"
id="path88" />
</g>
</g>
<g>
<g class="st1">
<path class="st2" d="M182.6,84.9v1.5c-0.4-2-1.1-3.9-1.8-5.7c-0.5-1.5-0.8-2-1.4-2c-0.2,0-0.4,0-0.6,0.1l-0.2-0.5
c0.4-0.4,0.8-0.7,1.4-0.7c0.7,0,1.3,0.3,2,2.4c0.4,1.1,0.9,2.8,1.4,5.4l0.1,0.1l0.1,3.9l-1.4,0.3l-0.2-0.2L182.6,84.9z
M183.1,85.5c1.2-2.1,1.7-3.5,2.5-5.5l-0.2,1.4c-0.2-1.7-0.3-2.3-0.3-2.7c0-0.8,0.4-1.2,0.9-1.2c0.3,0,0.5,0.1,0.6,0.2
c0.1,0.2,0.1,0.4,0.1,0.8c0,1.9-1.8,5.3-3.3,7.8L183.1,85.5z"/>
<g
id="g98">
<g
class="st1"
id="g96">
<path
class="st2"
d="M182.6,84.9v1.5c-0.4-2-1.1-3.9-1.8-5.7c-0.5-1.5-0.8-2-1.4-2c-0.2,0-0.4,0-0.6,0.1l-0.2-0.5 c0.4-0.4,0.8-0.7,1.4-0.7c0.7,0,1.3,0.3,2,2.4c0.4,1.1,0.9,2.8,1.4,5.4l0.1,0.1l0.1,3.9l-1.4,0.3l-0.2-0.2L182.6,84.9z M183.1,85.5c1.2-2.1,1.7-3.5,2.5-5.5l-0.2,1.4c-0.2-1.7-0.3-2.3-0.3-2.7c0-0.8,0.4-1.2,0.9-1.2c0.3,0,0.5,0.1,0.6,0.2 c0.1,0.2,0.1,0.4,0.1,0.8c0,1.9-1.8,5.3-3.3,7.8L183.1,85.5z"
id="path94" />
</g>
</g>
<circle cx="284.1" cy="88.3" r="2.5"/>
<circle cx="376.6" cy="141.2" r="2.5"/>
<circle cx="489.6" cy="84" r="2.5"/>
<circle cx="370.5" cy="21.6" r="2.5"/>
<line class="st0" x1="370.5" y1="21.6" x2="284.1" y2="88.3"/>
<line class="st0" x1="489.6" y1="83.9" x2="370.5" y2="21.6"/>
<polygon class="st0" points="376.6,141.2 284.1,88.3 489.6,83.9 "/>
<g>
<g>
<path d="M367.8,54.4v-0.7h2.5v1h-0.2L367.8,54.4z M370.3,64.8v1h-2.5V65l2.3-0.3h0.2V64.8z M369.5,59.3c0-1.9,0-3.7-0.1-5.6h1.7
c-0.1,1.8-0.1,3.7-0.1,5.6v0.6c0,2.1,0,4,0.1,5.9h-1.7c0.1-1.8,0.1-3.7,0.1-5.6V59.3z M370.3,59.1h1.8c2.2,0,3.2-0.7,3.2-2.4
s-1-2.2-2.8-2.2h-2.2v-0.8h2.7c2.5,0,3.9,1.2,3.9,3c0,1.4-1,2.6-3.4,2.9v-0.2c2.8,0.2,4,1.5,4,3c0,1.6-1.4,3.3-4.8,3.3h-2.4v-0.8
h2c2.3,0,3.5-0.9,3.5-2.5c0-1.7-1.1-2.5-3.6-2.5h-1.9L370.3,59.1L370.3,59.1z"/>
<circle
cx="284.1"
cy="88.3"
r="2.5"
id="circle100" />
<circle
cx="376.6"
cy="141.2"
r="2.5"
id="circle102" />
<circle
cx="489.6"
cy="84"
r="2.5"
id="circle104" />
<circle
cx="370.5"
cy="21.6"
r="2.5"
id="circle106" />
<line
class="st0"
x1="370.5"
y1="21.6"
x2="284.1"
y2="88.3"
id="line108" />
<line
class="st0"
x1="489.6"
y1="83.9"
x2="370.5"
y2="21.6"
id="line110" />
<polygon
class="st0"
points="376.6,141.2 284.1,88.3 489.6,83.9 "
id="polygon112" />
<g
id="g118">
<g
id="g116">
<path
d="M367.8,54.4v-0.7h2.5v1h-0.2L367.8,54.4z M370.3,64.8v1h-2.5V65l2.3-0.3h0.2V64.8z M369.5,59.3c0-1.9,0-3.7-0.1-5.6h1.7 c-0.1,1.8-0.1,3.7-0.1,5.6v0.6c0,2.1,0,4,0.1,5.9h-1.7c0.1-1.8,0.1-3.7,0.1-5.6V59.3z M370.3,59.1h1.8c2.2,0,3.2-0.7,3.2-2.4 s-1-2.2-2.8-2.2h-2.2v-0.8h2.7c2.5,0,3.9,1.2,3.9,3c0,1.4-1,2.6-3.4,2.9v-0.2c2.8,0.2,4,1.5,4,3c0,1.6-1.4,3.3-4.8,3.3h-2.4v-0.8 h2c2.3,0,3.5-0.9,3.5-2.5c0-1.7-1.1-2.5-3.6-2.5h-1.9L370.3,59.1L370.3,59.1z"
id="path114" />
</g>
</g>
<g>
<g class="st1">
<path class="st2" d="M380.7,113.1v1l-1.1,0.3l0.5-2.7h1.1l-0.1,2.6c-1.1,0.6-2.4,0.9-3.6,0.9c-3.6,0-6.1-2.6-6.1-6.3
c0-3.6,2.6-6.3,6.1-6.3c1,0,2.3,0.2,3.5,0.9l0.1,2.6H380l-0.5-2.7l1.1,0.3v1.1c-1-1-2-1.3-2.9-1.3c-2.4,0-4.4,1.9-4.4,5.4
s1.8,5.4,4.3,5.4C378.5,114.2,379.7,113.9,380.7,113.1z"/>
<g
id="g124">
<g
class="st1"
id="g122">
<path
class="st2"
d="M380.7,113.1v1l-1.1,0.3l0.5-2.7h1.1l-0.1,2.6c-1.1,0.6-2.4,0.9-3.6,0.9c-3.6,0-6.1-2.6-6.1-6.3 c0-3.6,2.6-6.3,6.1-6.3c1,0,2.3,0.2,3.5,0.9l0.1,2.6H380l-0.5-2.7l1.1,0.3v1.1c-1-1-2-1.3-2.9-1.3c-2.4,0-4.4,1.9-4.4,5.4 s1.8,5.4,4.3,5.4C378.5,114.2,379.7,113.9,380.7,113.1z"
id="path120" />
</g>
</g>
<g>
<g>
<path d="M88.4,8c0.9,0,1.5-0.4,2.4-1.5L91,7c-0.8,1.4-1.7,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4C84.4,1.9,86,0,88.2,0c1,0,2,0.4,2.9,1.8
l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3C87.1,1,86,2.1,86,4.6C85.9,6.9,86.9,8,88.4,8z M91.5,12.1l2,0.4v0.6h-4.6v-0.6l2.2-0.4H91.5z
M92,13.1h-1.5c0-1.3,0.1-2.6,0.1-3.8V7.5l-0.1-0.3V1.3L91.7,0L92,0.2l-0.1,2.4v6.7C92,10.5,92,11.8,92,13.1z"/>
<g
id="g130">
<g
id="g128">
<path
d="M88.4,8c0.9,0,1.5-0.4,2.4-1.5L91,7c-0.8,1.4-1.7,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4C84.4,1.9,86,0,88.2,0c1,0,2,0.4,2.9,1.8 l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3C87.1,1,86,2.1,86,4.6C85.9,6.9,86.9,8,88.4,8z M91.5,12.1l2,0.4v0.6h-4.6v-0.6l2.2-0.4H91.5z M92,13.1h-1.5c0-1.3,0.1-2.6,0.1-3.8V7.5l-0.1-0.3V1.3L91.7,0L92,0.2l-0.1,2.4v6.7C92,10.5,92,11.8,92,13.1z"
id="path126" />
</g>
</g>
<g>
<g>
<path d="M367.7,8c0.9,0,1.5-0.4,2.4-1.5l0.2,0.5c-0.8,1.4-1.7,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6
c1,0,2,0.4,2.9,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.6C365.2,6.9,366.3,8,367.7,8z M370.8,12.1l2,0.4v0.6h-4.6
v-0.6l2.2-0.4H370.8z M371.4,13.1h-1.5c0-1.3,0.1-2.6,0.1-3.8V7.5l-0.1-0.3V1.3l1.2-1.3l0.3,0.2l-0.1,2.4v6.7
C371.3,10.5,371.3,11.8,371.4,13.1z"/>
<g
id="g136">
<g
id="g134">
<path
d="M367.7,8c0.9,0,1.5-0.4,2.4-1.5l0.2,0.5c-0.8,1.4-1.7,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6 c1,0,2,0.4,2.9,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.6C365.2,6.9,366.3,8,367.7,8z M370.8,12.1l2,0.4v0.6h-4.6 v-0.6l2.2-0.4H370.8z M371.4,13.1h-1.5c0-1.3,0.1-2.6,0.1-3.8V7.5l-0.1-0.3V1.3l1.2-1.3l0.3,0.2l-0.1,2.4v6.7 C371.3,10.5,371.3,11.8,371.4,13.1z"
id="path132" />
</g>
</g>
<g>
<g>
<path d="M34.2,50c0.9,0,1.5-0.4,2.4-1.5l0.2,0.5c-0.7,1.2-1.5,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6
c1,0,1.9,0.4,2.8,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.5C31.7,49,32.7,50,34.2,50z M39.2,50.2v0.6L36.4,51
l-0.2-1.8v-5.3l0.1-0.2v-4.5L34.8,39v-0.6l2.8-0.8l0.3,0.1l-0.1,2.6v10.6L37.5,50L39.2,50.2z"/>
<path d="M45.1,50.3v0.5h-4.5v-0.5l1.7-0.3h1.1L45.1,50.3z M43.4,43.5l0.2,0.1v7.1h-1.2v-6.1l-1.5,0.2v-0.6L43.4,43.5z"/>
<g
id="g144">
<g
id="g142">
<path
d="M34.2,50c0.9,0,1.5-0.4,2.4-1.5l0.2,0.5c-0.7,1.2-1.5,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6 c1,0,1.9,0.4,2.8,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.5C31.7,49,32.7,50,34.2,50z M39.2,50.2v0.6L36.4,51 l-0.2-1.8v-5.3l0.1-0.2v-4.5L34.8,39v-0.6l2.8-0.8l0.3,0.1l-0.1,2.6v10.6L37.5,50L39.2,50.2z"
id="path138" />
</g>
</g>
<g>
<g>
<path d="M104.3,81.7c0.9,0,1.5-0.4,2.4-1.5l0.2,0.5c-0.7,1.2-1.5,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6
c1,0,1.9,0.4,2.8,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.5C101.8,80.6,102.8,81.7,104.3,81.7z M109.3,81.8v0.6
l-2.8,0.2l-0.2-1.8v-5.3l0.1-0.2v-4.6l-1.5-0.2v-0.6l2.8-0.8l0.3,0.1l-0.1,2.6v10.6l-0.3-0.9L109.3,81.8z"/>
<g
id="g150"
transform="translate(-1.9943323,0.1596911)">
<g
id="g148">
<path
d="m 104.3,81.7 c 0.9,0 1.5,-0.4 2.4,-1.5 l 0.2,0.5 c -0.7,1.2 -1.5,2 -2.9,2 -2.2,0 -3.7,-1.8 -3.7,-4.4 0,-2.7 1.6,-4.6 3.8,-4.6 1,0 1.9,0.4 2.8,1.8 l -0.2,0.5 c -1,-1 -1.8,-1.3 -2.4,-1.3 -1.4,0 -2.5,1.1 -2.5,3.5 0,2.4 1,3.5 2.5,3.5 z m 5,0.1 v 0.6 l -2.8,0.2 -0.2,-1.8 v -5.3 l 0.1,-0.2 v -4.6 l -1.5,-0.2 v -0.6 l 2.8,-0.8 0.3,0.1 -0.1,2.6 v 10.6 l -0.3,-0.9 z"
id="path146" />
</g>
</g>
<g>
<g>
<path d="M160.1,49c0.9,0,1.5-0.4,2.4-1.5l0.2,0.5c-0.7,1.2-1.5,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6
c1,0,1.9,0.4,2.8,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.5C157.5,48,158.6,49,160.1,49z M165.1,49.2v0.6
l-2.8,0.2l-0.2-1.8v-5.3l0.1-0.2v-4.5l-1.5-0.2v-0.6l2.8-0.8l0.3,0.1l-0.1,2.6v10.6l-0.3-0.9L165.1,49.2z"/>
<path d="M166.3,48.9l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8c0-0.9-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1
c-0.2,0.7-0.4,0.9-0.7,0.9s-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6l0.3-0.7h4
v1.2h-4.8L166.3,48.9L166.3,48.9z"/>
<g
id="g158">
<g
id="g156">
<path
d="M160.1,49c0.9,0,1.5-0.4,2.4-1.5l0.2,0.5c-0.7,1.2-1.5,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6 c1,0,1.9,0.4,2.8,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.5C157.5,48,158.6,49,160.1,49z M165.1,49.2v0.6 l-2.8,0.2l-0.2-1.8v-5.3l0.1-0.2v-4.5l-1.5-0.2v-0.6l2.8-0.8l0.3,0.1l-0.1,2.6v10.6l-0.3-0.9L165.1,49.2z"
id="path152" />
<path
d="M166.3,48.9l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8c0-0.9-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1 c-0.2,0.7-0.4,0.9-0.7,0.9s-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6l0.3-0.7h4 v1.2h-4.8L166.3,48.9L166.3,48.9z"
id="path154" />
</g>
</g>
<g>
<g>
<path d="M55.3,87.6l1.8-0.3h0.4l2,0.3v0.7h-4.1L55.3,87.6L55.3,87.6z M60.7,76.2h1.1l4.1,12.1h-1.7l-3.6-10.8H61l-3.7,10.8h-0.8
L60.7,76.2z M58.6,83.6h4.9l0.3,0.8h-5.4L58.6,83.6z M62.2,87.6l2.3-0.3h0.4l2.2,0.3v0.7h-4.9C62.2,88.3,62.2,87.6,62.2,87.6z"/>
<path d="M72.8,87.8v0.5h-4.5v-0.5l1.7-0.3h1.1L72.8,87.8z M71.1,81.1l0.2,0.1v7.1h-1.2v-6.1l-1.4,0.1v-0.6L71.1,81.1z"/>
<g
id="g166">
<g
id="g164">
<path
d="M55.3,87.6l1.8-0.3h0.4l2,0.3v0.7h-4.1L55.3,87.6L55.3,87.6z M60.7,76.2h1.1l4.1,12.1h-1.7l-3.6-10.8H61l-3.7,10.8h-0.8 L60.7,76.2z M58.6,83.6h4.9l0.3,0.8h-5.4L58.6,83.6z M62.2,87.6l2.3-0.3h0.4l2.2,0.3v0.7h-4.9C62.2,88.3,62.2,87.6,62.2,87.6z"
id="path160" />
</g>
</g>
<g>
<g>
<path d="M126.5,85.8l1.8-0.3h0.4l2,0.3v0.7h-4.1L126.5,85.8L126.5,85.8z M131.8,74.4h1.1l4.1,12.2h-1.7l-3.6-10.8h0.4l-3.7,10.8
h-0.8L131.8,74.4z M129.7,81.8h4.9l0.3,0.8h-5.4L129.7,81.8z M133.3,85.8l2.3-0.3h0.4l2.2,0.3v0.7h-4.9V85.8z"/>
<path d="M139.3,85.7l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8s-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1
c-0.2,0.7-0.4,0.9-0.7,0.9s-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6l0.3-0.7h4
v1.2h-4.8L139.3,85.7L139.3,85.7z"/>
<g
id="g174">
<g
id="g172">
<path
d="M126.5,85.8l1.8-0.3h0.4l2,0.3v0.7h-4.1L126.5,85.8L126.5,85.8z M131.8,74.4h1.1l4.1,12.2h-1.7l-3.6-10.8h0.4l-3.7,10.8 h-0.8L131.8,74.4z M129.7,81.8h4.9l0.3,0.8h-5.4L129.7,81.8z M133.3,85.8l2.3-0.3h0.4l2.2,0.3v0.7h-4.9V85.8z"
id="path168" />
<path
d="M139.3,85.7l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8s-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1 c-0.2,0.7-0.4,0.9-0.7,0.9s-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6l0.3-0.7h4 v1.2h-4.8L139.3,85.7L139.3,85.7z"
id="path170" />
</g>
</g>
<g>
<g>
<path d="M4.6,111.1v0.6H0v-0.6l2-0.4h0.4L4.6,111.1z M1.5,102.8c0-1,0-1.5-0.1-2.3L0,100.3v-0.6l2.4-1.1l0.3,0.2l0.1,1.5h0.1v5.6
L2.8,106v1.8c0,1.3,0,2.6,0.1,3.8H1.5c0-1.3,0.1-2.5,0.1-3.8L1.5,102.8L1.5,102.8z M5.2,99.6c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3
c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7
c1.4,0,2.5-1.1,2.5-3.4C7.5,100.9,6.5,99.6,5.2,99.6z"/>
<path d="M15.7,107.7c0,2.6-1.2,3.7-2.5,3.7c-1.3,0-2.5-1.1-2.5-3.7s1.2-3.7,2.5-3.7C14.5,103.9,15.7,105,15.7,107.7z M13.2,110.8
c0.6,0,1.2-0.6,1.2-3.1c0-2.6-0.6-3.1-1.2-3.1c-0.6,0-1.2,0.5-1.2,3.1C12,110.2,12.6,110.8,13.2,110.8z"/>
<g
id="g182">
<g
id="g180">
<path
d="M4.6,111.1v0.6H0v-0.6l2-0.4h0.4L4.6,111.1z M1.5,102.8c0-1,0-1.5-0.1-2.3L0,100.3v-0.6l2.4-1.1l0.3,0.2l0.1,1.5h0.1v5.6 L2.8,106v1.8c0,1.3,0,2.6,0.1,3.8H1.5c0-1.3,0.1-2.5,0.1-3.8L1.5,102.8L1.5,102.8z M5.2,99.6c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3 c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7 c1.4,0,2.5-1.1,2.5-3.4C7.5,100.9,6.5,99.6,5.2,99.6z"
id="path176" />
<path
d="M15.7,107.7c0,2.6-1.2,3.7-2.5,3.7c-1.3,0-2.5-1.1-2.5-3.7s1.2-3.7,2.5-3.7C14.5,103.9,15.7,105,15.7,107.7z M13.2,110.8 c0.6,0,1.2-0.6,1.2-3.1c0-2.6-0.6-3.1-1.2-3.1c-0.6,0-1.2,0.5-1.2,3.1C12,110.2,12.6,110.8,13.2,110.8z"
id="path178" />
</g>
</g>
<g>
<g>
<path d="M284.8,111.1v0.6h-4.6v-0.6l2-0.4h0.4L284.8,111.1z M281.7,102.8c0-1,0-1.5-0.1-2.3l-1.4-0.2v-0.6l2.4-1.1l0.3,0.2
l0.1,1.5h0.1v5.6L283,106v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8V102.8z M285.4,99.6c-0.7,0-1.6,0.3-2.9,1.6
l-0.2-0.3c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7
c1.4,0,2.5-1.1,2.5-3.4C287.7,100.9,286.7,99.6,285.4,99.6z"/>
<path d="M295.9,107.7c0,2.6-1.2,3.7-2.5,3.7s-2.5-1.1-2.5-3.7s1.2-3.7,2.5-3.7C294.7,103.9,295.9,105,295.9,107.7z M293.4,110.8
c0.6,0,1.2-0.6,1.2-3.1c0-2.6-0.6-3.1-1.2-3.1s-1.2,0.5-1.2,3.1C292.2,110.2,292.8,110.8,293.4,110.8z"/>
<g
id="g190">
<g
id="g188">
<path
d="M284.8,111.1v0.6h-4.6v-0.6l2-0.4h0.4L284.8,111.1z M281.7,102.8c0-1,0-1.5-0.1-2.3l-1.4-0.2v-0.6l2.4-1.1l0.3,0.2 l0.1,1.5h0.1v5.6L283,106v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8V102.8z M285.4,99.6c-0.7,0-1.6,0.3-2.9,1.6 l-0.2-0.3c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7 c1.4,0,2.5-1.1,2.5-3.4C287.7,100.9,286.7,99.6,285.4,99.6z"
id="path184" />
<path
d="M295.9,107.7c0,2.6-1.2,3.7-2.5,3.7s-2.5-1.1-2.5-3.7s1.2-3.7,2.5-3.7C294.7,103.9,295.9,105,295.9,107.7z M293.4,110.8 c0.6,0,1.2-0.6,1.2-3.1c0-2.6-0.6-3.1-1.2-3.1s-1.2,0.5-1.2,3.1C292.2,110.2,292.8,110.8,293.4,110.8z"
id="path186" />
</g>
</g>
<g>
<g>
<path d="M97.6,161.1v0.6H93v-0.6l2-0.4h0.4L97.6,161.1z M94.5,152.8c0-1,0-1.5-0.1-2.3l-1.4-0.2v-0.6l2.4-1.1l0.3,0.2l0.1,1.5h0.1
v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8V152.8z M98.2,149.6c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3
c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7
c1.4,0,2.5-1.1,2.5-3.4C100.5,150.9,99.5,149.6,98.2,149.6z"/>
<path d="M108.5,160.7v0.5H104v-0.5l1.7-0.3h1.1L108.5,160.7z M106.8,154l0.2,0.1v7.1h-1.2v-6.1l-1.4,0.1v-0.6L106.8,154z"/>
<g
id="g198">
<g
id="g196">
<path
d="M97.6,161.1v0.6H93v-0.6l2-0.4h0.4L97.6,161.1z M94.5,152.8c0-1,0-1.5-0.1-2.3l-1.4-0.2v-0.6l2.4-1.1l0.3,0.2l0.1,1.5h0.1 v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8V152.8z M98.2,149.6c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3 c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7 c1.4,0,2.5-1.1,2.5-3.4C100.5,150.9,99.5,149.6,98.2,149.6z"
id="path192" />
<path
d="M108.5,160.7v0.5H104v-0.5l1.7-0.3h1.1L108.5,160.7z M106.8,154l0.2,0.1v7.1h-1.2v-6.1l-1.4,0.1v-0.6L106.8,154z"
id="path194" />
</g>
</g>
<g>
<g>
<path d="M377.6,161.1v0.6H373v-0.6l2-0.4h0.4L377.6,161.1z M374.5,152.8c0-1,0-1.5-0.1-2.3l-1.4-0.2v-0.6l2.4-1.1l0.3,0.2l0.1,1.5
h0.1v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8V152.8z M378.2,149.6c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3
c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7
c1.4,0,2.5-1.1,2.5-3.4C380.5,150.9,379.5,149.6,378.2,149.6z"/>
<path d="M388.5,160.7v0.5H384v-0.5l1.7-0.3h1.1L388.5,160.7z M386.8,154l0.2,0.1v7.1h-1.2v-6.1l-1.4,0.1v-0.6L386.8,154z"/>
<g
id="g206">
<g
id="g204">
<path
d="M377.6,161.1v0.6H373v-0.6l2-0.4h0.4L377.6,161.1z M374.5,152.8c0-1,0-1.5-0.1-2.3l-1.4-0.2v-0.6l2.4-1.1l0.3,0.2l0.1,1.5 h0.1v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8V152.8z M378.2,149.6c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3 c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7 c1.4,0,2.5-1.1,2.5-3.4C380.5,150.9,379.5,149.6,378.2,149.6z"
id="path200" />
<path
d="M388.5,160.7v0.5H384v-0.5l1.7-0.3h1.1L388.5,160.7z M386.8,154l0.2,0.1v7.1h-1.2v-6.1l-1.4,0.1v-0.6L386.8,154z"
id="path202" />
</g>
</g>
<g>
<g>
<path d="M210.7,105.5v0.6h-4.6v-0.6l2-0.4h0.4L210.7,105.5z M207.6,97.1c0-1,0-1.5-0.1-2.3l-1.4-0.2V94l2.4-1.1l0.3,0.2l0.1,1.5
h0.1v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8L207.6,97.1L207.6,97.1z M211.3,94c-0.7,0-1.6,0.3-2.9,1.6
l-0.2-0.3c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7
c1.4,0,2.5-1.1,2.5-3.4C213.6,95.3,212.6,94,211.3,94z"/>
<path d="M216.9,104.7l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8s-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1
c-0.2,0.7-0.4,0.9-0.7,0.9s-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6l0.3-0.7h4
v1.2H217L216.9,104.7L216.9,104.7z"/>
<g
id="g214">
<g
id="g212">
<path
d="M210.7,105.5v0.6h-4.6v-0.6l2-0.4h0.4L210.7,105.5z M207.6,97.1c0-1,0-1.5-0.1-2.3l-1.4-0.2V94l2.4-1.1l0.3,0.2l0.1,1.5 h0.1v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8L207.6,97.1L207.6,97.1z M211.3,94c-0.7,0-1.6,0.3-2.9,1.6 l-0.2-0.3c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7 c1.4,0,2.5-1.1,2.5-3.4C213.6,95.3,212.6,94,211.3,94z"
id="path208" />
<path
d="M216.9,104.7l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8s-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1 c-0.2,0.7-0.4,0.9-0.7,0.9s-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6l0.3-0.7h4 v1.2H217L216.9,104.7L216.9,104.7z"
id="path210" />
</g>
</g>
<g>
<g>
<path d="M490.2,105.5v0.6h-4.6v-0.6l2-0.4h0.4L490.2,105.5z M487.1,97.1c0-1,0-1.5-0.1-2.3l-1.4-0.2V94l2.4-1.1l0.3,0.2l0.1,1.5
h0.1v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8H487c0-1.3,0.1-2.5,0.1-3.8V97.1z M490.8,94c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3
c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7
c1.4,0,2.5-1.1,2.5-3.4C493.1,95.3,492.1,94,490.8,94z"/>
<path d="M496.4,104.7l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8s-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1
c-0.2,0.7-0.4,0.9-0.7,0.9c-0.3,0-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6
l0.3-0.7h4v1.2h-4.9V104.7z"/>
<g
id="g222">
<g
id="g220">
<path
d="M490.2,105.5v0.6h-4.6v-0.6l2-0.4h0.4L490.2,105.5z M487.1,97.1c0-1,0-1.5-0.1-2.3l-1.4-0.2V94l2.4-1.1l0.3,0.2l0.1,1.5 h0.1v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8H487c0-1.3,0.1-2.5,0.1-3.8V97.1z M490.8,94c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3 c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7 c1.4,0,2.5-1.1,2.5-3.4C493.1,95.3,492.1,94,490.8,94z"
id="path216" />
<path
d="M496.4,104.7l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8s-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1 c-0.2,0.7-0.4,0.9-0.7,0.9c-0.3,0-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6 l0.3-0.7h4v1.2h-4.9V104.7z"
id="path218" />
</g>
</g>
</svg>
<path
d="m 44.898817,47.364552 c 0,2.6 -1.2,3.7 -2.5,3.7 -1.3,0 -2.5,-1.1 -2.5,-3.7 0,-2.6 1.2,-3.7 2.5,-3.7 1.3,-0.1 2.5,1 2.5,3.7 z m -2.5,3.1 c 0.6,0 1.2,-0.6 1.2,-3.1 0,-2.6 -0.6,-3.1 -1.2,-3.1 -0.6,0 -1.2,0.5 -1.2,3.1 0,2.5 0.6,3.1 1.2,3.1 z"
id="path178-6" /><path
d="m 72.819403,84.595293 c 0,2.6 -1.2,3.7 -2.5,3.7 -1.3,0 -2.5,-1.1 -2.5,-3.7 0,-2.6 1.2,-3.7 2.5,-3.7 1.3,-0.1 2.5,1 2.5,3.7 z m -2.5,3.1 c 0.6,0 1.2,-0.6 1.2,-3.1 0,-2.6 -0.6,-3.1 -1.2,-3.1 -0.6,0 -1.2,0.5 -1.2,3.1 0,2.5 0.6,3.1 1.2,3.1 z"
id="path178-6-5" /><text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none"
x="142.27249"
y="19.292274"
id="text704"><tspan
sodipodi:role="line"
id="tspan702"></tspan></text></svg>

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,79 +1,163 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="222.1px" height="160.6px" viewBox="0 0 222.1 160.6" style="enable-background:new 0 0 222.1 160.6;" xml:space="preserve"
>
<style type="text/css">
<svg
version="1.1"
id="Layer_1"
x="0px"
y="0px"
width="222.1px"
height="160.6px"
viewBox="0 0 222.1 160.6"
style="enable-background:new 0 0 222.1 160.6;"
xml:space="preserve"
sodipodi:docname="mean_value.svg"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs73" /><sodipodi:namedview
id="namedview71"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="6.1008553"
inkscape:cx="92.282143"
inkscape:cy="61.30288"
inkscape:window-width="2560"
inkscape:window-height="1371"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<style
type="text/css"
id="style2">
.st0{fill:none;stroke:#000000;stroke-miterlimit:10;}
</style>
<circle cx="3.7" cy="86.8" r="2.5"/>
<circle cx="96.2" cy="139.7" r="2.5"/>
<circle cx="209.3" cy="82.5" r="2.5"/>
<circle cx="90.2" cy="20.2" r="2.5"/>
<line class="st0" x1="90.2" y1="20.2" x2="3.7" y2="86.8"/>
<line class="st0" x1="96.2" y1="139.7" x2="90.2" y2="20.2"/>
<line class="st0" x1="209.3" y1="82.4" x2="90.2" y2="20.2"/>
<g>
<g>
<path d="M86.6,8c0.9,0,1.5-0.4,2.4-1.5L89.2,7c-0.8,1.4-1.7,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6
c1,0,2,0.4,2.9,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.6C84.1,6.9,85.2,8,86.6,8z M89.7,12.1l2,0.4v0.6h-4.6
v-0.6l2.2-0.4H89.7z M90.3,13.1h-1.5c0-1.3,0.1-2.6,0.1-3.8V7.5l-0.1-0.3V1.3L90,0l0.3,0.2l-0.1,2.4v6.7
C90.2,10.5,90.2,11.8,90.3,13.1z"/>
<circle
cx="3.7"
cy="86.8"
r="2.5"
id="circle4" />
<circle
cx="96.2"
cy="139.7"
r="2.5"
id="circle6" />
<circle
cx="209.3"
cy="82.5"
r="2.5"
id="circle8" />
<circle
cx="90.2"
cy="20.2"
r="2.5"
id="circle10" />
<line
class="st0"
x1="90.2"
y1="20.2"
x2="3.7"
y2="86.8"
id="line12" />
<line
class="st0"
x1="96.2"
y1="139.7"
x2="90.2"
y2="20.2"
id="line14" />
<line
class="st0"
x1="209.3"
y1="82.4"
x2="90.2"
y2="20.2"
id="line16" />
<g
id="g22">
<g
id="g20">
<path
d="M86.6,8c0.9,0,1.5-0.4,2.4-1.5L89.2,7c-0.8,1.4-1.7,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6 c1,0,2,0.4,2.9,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.6C84.1,6.9,85.2,8,86.6,8z M89.7,12.1l2,0.4v0.6h-4.6 v-0.6l2.2-0.4H89.7z M90.3,13.1h-1.5c0-1.3,0.1-2.6,0.1-3.8V7.5l-0.1-0.3V1.3L90,0l0.3,0.2l-0.1,2.4v6.7 C90.2,10.5,90.2,11.8,90.3,13.1z"
id="path18" />
</g>
</g>
<g>
<g>
<path d="M33.9,48.3c0.9,0,1.5-0.4,2.4-1.5l0.2,0.5c-0.7,1.2-1.5,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6
c1,0,1.9,0.4,2.8,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.5C31.4,47.2,32.5,48.3,33.9,48.3z M38.9,48.4V49
l-2.7,0.2L36,47.4v-5.3l0.1-0.2v-4.5l-1.5-0.2v-0.6l2.8-0.8l0.3,0.1l-0.1,2.6v10.6l-0.3-0.9L38.9,48.4z"/>
<path d="M44.8,48.5V49h-4.5v-0.5l1.7-0.3h1.2L44.8,48.5z M43.1,41.8l0.2,0.1V49h-1.2v-6.1L40.7,43v-0.6L43.1,41.8z"/>
<g
id="g30">
<g
id="g28">
<path
d="M33.9,48.3c0.9,0,1.5-0.4,2.4-1.5l0.2,0.5c-0.7,1.2-1.5,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6 c1,0,1.9,0.4,2.8,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.5C31.4,47.2,32.5,48.3,33.9,48.3z M38.9,48.4V49 l-2.7,0.2L36,47.4v-5.3l0.1-0.2v-4.5l-1.5-0.2v-0.6l2.8-0.8l0.3,0.1l-0.1,2.6v10.6l-0.3-0.9L38.9,48.4z"
id="path24" />
</g>
</g>
<g>
<g>
<path d="M103.6,88.5c0.9,0,1.5-0.4,2.4-1.5l0.2,0.5c-0.7,1.2-1.5,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6
c1,0,1.9,0.4,2.8,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.5C101.1,87.4,102.1,88.5,103.6,88.5z M108.6,88.7v0.6
l-2.8,0.2l-0.2-1.8v-5.3l0.1-0.2v-4.5l-1.5-0.2v-0.6l2.8-0.8l0.3,0.1l-0.1,2.6v10.6l-0.3-0.9L108.6,88.7z"/>
<g
id="g36">
<g
id="g34">
<path
d="M103.6,88.5c0.9,0,1.5-0.4,2.4-1.5l0.2,0.5c-0.7,1.2-1.5,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6 c1,0,1.9,0.4,2.8,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.5C101.1,87.4,102.1,88.5,103.6,88.5z M108.6,88.7v0.6 l-2.8,0.2l-0.2-1.8v-5.3l0.1-0.2v-4.5l-1.5-0.2v-0.6l2.8-0.8l0.3,0.1l-0.1,2.6v10.6l-0.3-0.9L108.6,88.7z"
id="path32" />
</g>
</g>
<g>
<g>
<path d="M155.6,47.3c0.9,0,1.5-0.4,2.4-1.5l0.2,0.5c-0.7,1.2-1.5,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6
c1,0,1.9,0.4,2.8,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.3,0-2.5,1.1-2.5,3.5C153.1,46.2,154.2,47.3,155.6,47.3z M160.7,47.4V48
l-2.8,0.2l-0.2-1.8v-5.3l0.1-0.2v-4.5l-1.5-0.2v-0.6l2.8-0.8l0.3,0.1l-0.1,2.6v10.6l-0.3-0.9L160.7,47.4z"/>
<path d="M161.9,47.2l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8c0-0.9-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1
c-0.2,0.7-0.4,0.9-0.7,0.9s-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6l0.3-0.7h4
v1.2H162L161.9,47.2L161.9,47.2z"/>
<g
id="g44">
<g
id="g42">
<path
d="M155.6,47.3c0.9,0,1.5-0.4,2.4-1.5l0.2,0.5c-0.7,1.2-1.5,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6 c1,0,1.9,0.4,2.8,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.3,0-2.5,1.1-2.5,3.5C153.1,46.2,154.2,47.3,155.6,47.3z M160.7,47.4V48 l-2.8,0.2l-0.2-1.8v-5.3l0.1-0.2v-4.5l-1.5-0.2v-0.6l2.8-0.8l0.3,0.1l-0.1,2.6v10.6l-0.3-0.9L160.7,47.4z"
id="path38" />
<path
d="M161.9,47.2l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8c0-0.9-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1 c-0.2,0.7-0.4,0.9-0.7,0.9s-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6l0.3-0.7h4 v1.2H162L161.9,47.2L161.9,47.2z"
id="path40" />
</g>
</g>
<g>
<g>
<path d="M4.6,108v0.6H0V108l2-0.4h0.4L4.6,108z M1.5,99.7c0-1,0-1.5-0.1-2.3L0,97.2v-0.6l2.4-1.1l0.3,0.2l0.1,1.5h0.1v5.6l0,0.2
v1.8c0,1.3,0,2.6,0.1,3.8H1.5c0-1.3,0.1-2.5,0.1-3.8L1.5,99.7L1.5,99.7z M5.2,96.5c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3
c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7
c1.4,0,2.5-1.1,2.5-3.4C7.5,97.8,6.5,96.5,5.2,96.5z"/>
<path d="M15.7,104.6c0,2.6-1.2,3.7-2.5,3.7c-1.3,0-2.5-1.1-2.5-3.7s1.2-3.7,2.5-3.7C14.5,100.8,15.7,101.9,15.7,104.6z
M13.2,107.7c0.6,0,1.2-0.6,1.2-3.1c0-2.6-0.6-3.1-1.2-3.1c-0.6,0-1.2,0.5-1.2,3.1C12,107.1,12.6,107.7,13.2,107.7z"/>
<g
id="g52">
<g
id="g50">
<path
d="M4.6,108v0.6H0V108l2-0.4h0.4L4.6,108z M1.5,99.7c0-1,0-1.5-0.1-2.3L0,97.2v-0.6l2.4-1.1l0.3,0.2l0.1,1.5h0.1v5.6l0,0.2 v1.8c0,1.3,0,2.6,0.1,3.8H1.5c0-1.3,0.1-2.5,0.1-3.8L1.5,99.7L1.5,99.7z M5.2,96.5c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3 c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7 c1.4,0,2.5-1.1,2.5-3.4C7.5,97.8,6.5,96.5,5.2,96.5z"
id="path46" />
<path
d="M15.7,104.6c0,2.6-1.2,3.7-2.5,3.7c-1.3,0-2.5-1.1-2.5-3.7s1.2-3.7,2.5-3.7C14.5,100.8,15.7,101.9,15.7,104.6z M13.2,107.7c0.6,0,1.2-0.6,1.2-3.1c0-2.6-0.6-3.1-1.2-3.1c-0.6,0-1.2,0.5-1.2,3.1C12,107.1,12.6,107.7,13.2,107.7z"
id="path48" />
</g>
</g>
<g>
<g>
<path d="M97.9,160v0.6h-4.6V160l2-0.4h0.4L97.9,160z M94.8,151.7c0-1,0-1.5-0.1-2.3l-1.4-0.2v-0.6l2.4-1.1l0.3,0.2l0.1,1.5h0.1
v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8V151.7z M98.5,148.6c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3
c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7
c1.4,0,2.5-1.1,2.5-3.4C100.8,149.9,99.8,148.6,98.5,148.6z"/>
<path d="M108.8,159.6v0.5h-4.5v-0.5l1.7-0.3h1.1L108.8,159.6z M107.1,152.9l0.2,0.1v7.1h-1.2V154l-1.4,0.1v-0.6L107.1,152.9z"/>
<g
id="g60">
<g
id="g58">
<path
d="M97.9,160v0.6h-4.6V160l2-0.4h0.4L97.9,160z M94.8,151.7c0-1,0-1.5-0.1-2.3l-1.4-0.2v-0.6l2.4-1.1l0.3,0.2l0.1,1.5h0.1 v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8V151.7z M98.5,148.6c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3 c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7 c1.4,0,2.5-1.1,2.5-3.4C100.8,149.9,99.8,148.6,98.5,148.6z"
id="path54" />
<path
d="M108.8,159.6v0.5h-4.5v-0.5l1.7-0.3h1.1L108.8,159.6z M107.1,152.9l0.2,0.1v7.1h-1.2V154l-1.4,0.1v-0.6L107.1,152.9z"
id="path56" />
</g>
</g>
<g>
<g>
<path d="M211,103.4v0.6h-4.6v-0.6l2-0.4h0.4L211,103.4z M207.9,95c0-1,0-1.5-0.1-2.3l-1.4-0.2V92l2.4-1.1l0.3,0.2l0.1,1.5h0.1v5.6
l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8L207.9,95L207.9,95z M211.6,91.9c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3
c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7
c1.4,0,2.5-1.1,2.5-3.4C213.9,93.2,212.9,91.9,211.6,91.9z"/>
<path d="M217.2,102.6l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8s-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1
c-0.2,0.7-0.4,0.9-0.7,0.9s-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6l0.3-0.7h4
v1.2h-4.8L217.2,102.6L217.2,102.6z"/>
<g
id="g68">
<g
id="g66">
<path
d="M211,103.4v0.6h-4.6v-0.6l2-0.4h0.4L211,103.4z M207.9,95c0-1,0-1.5-0.1-2.3l-1.4-0.2V92l2.4-1.1l0.3,0.2l0.1,1.5h0.1v5.6 l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8L207.9,95L207.9,95z M211.6,91.9c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3 c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7 c1.4,0,2.5-1.1,2.5-3.4C213.9,93.2,212.9,91.9,211.6,91.9z"
id="path62" />
<path
d="M217.2,102.6l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8s-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1 c-0.2,0.7-0.4,0.9-0.7,0.9s-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6l0.3-0.7h4 v1.2h-4.8L217.2,102.6L217.2,102.6z"
id="path64" />
</g>
</g>
</svg>
<path
d="m 45.019848,45.642636 c 0,2.6 -1.2,3.7 -2.5,3.7 -1.3,0 -2.5,-1.1 -2.5,-3.7 0,-2.6 1.2,-3.7 2.5,-3.7 1.3,-0.1 2.5,1 2.5,3.7 z m -2.5,3.1 c 0.6,0 1.2,-0.6 1.2,-3.1 0,-2.6 -0.6,-3.1 -1.2,-3.1 -0.6,0 -1.2,0.5 -1.2,3.1 0,2.5 0.6,3.1 1.2,3.1 z"
id="path48-3" /></svg>

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -1,93 +1,174 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="222.1px" height="161.4px" viewBox="0 0 222.1 161.4" style="enable-background:new 0 0 222.1 161.4;" xml:space="preserve"
>
<style type="text/css">
<svg
version="1.1"
id="Layer_1"
x="0px"
y="0px"
width="222.1px"
height="161.4px"
viewBox="0 0 222.1 161.4"
style="enable-background:new 0 0 222.1 161.4;"
xml:space="preserve"
sodipodi:docname="tangent.svg"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs87" /><sodipodi:namedview
id="namedview85"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="4.3139562"
inkscape:cx="207.00257"
inkscape:cy="57.951447"
inkscape:window-width="2560"
inkscape:window-height="1371"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<style
type="text/css"
id="style2">
.st0{fill:none;stroke:#000000;stroke-miterlimit:10;}
</style>
<circle cx="3.7" cy="86.7" r="2.5"/>
<circle cx="96.2" cy="139.6" r="2.5"/>
<circle cx="209.3" cy="82.4" r="2.5"/>
<circle cx="90.2" cy="20.1" r="2.5"/>
<polyline class="st0" points="90.2,20.1 3.7,86.7 96.2,139.6 90.2,20.1 "/>
<polyline class="st0" points="96.2,139.6 209.3,82.3 90.2,20.1 "/>
<g>
<g>
<path d="M87,8c0.9,0,1.5-0.4,2.4-1.5L89.6,7c-0.8,1.4-1.7,2-2.9,2C84.5,9,83,7.2,83,4.6C83,1.9,84.6,0,86.8,0c1,0,2,0.4,2.9,1.8
l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.6C84.5,6.9,85.5,8,87,8z M90.1,12.1l2,0.4v0.6h-4.6v-0.6l2.2-0.4H90.1z
M90.6,13.1h-1.5c0-1.3,0.1-2.6,0.1-3.8V7.5l-0.1-0.3V1.3L90.3,0l0.3,0.2l-0.1,2.4v6.7C90.6,10.5,90.6,11.8,90.6,13.1z"/>
<circle
cx="3.7"
cy="86.7"
r="2.5"
id="circle4" />
<circle
cx="96.2"
cy="139.6"
r="2.5"
id="circle6" />
<circle
cx="209.3"
cy="82.4"
r="2.5"
id="circle8" />
<circle
cx="90.2"
cy="20.1"
r="2.5"
id="circle10" />
<polyline
class="st0"
points="90.2,20.1 3.7,86.7 96.2,139.6 90.2,20.1 "
id="polyline12" />
<polyline
class="st0"
points="96.2,139.6 209.3,82.3 90.2,20.1 "
id="polyline14" />
<g
id="g20">
<g
id="g18">
<path
d="M87,8c0.9,0,1.5-0.4,2.4-1.5L89.6,7c-0.8,1.4-1.7,2-2.9,2C84.5,9,83,7.2,83,4.6C83,1.9,84.6,0,86.8,0c1,0,2,0.4,2.9,1.8 l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.6C84.5,6.9,85.5,8,87,8z M90.1,12.1l2,0.4v0.6h-4.6v-0.6l2.2-0.4H90.1z M90.6,13.1h-1.5c0-1.3,0.1-2.6,0.1-3.8V7.5l-0.1-0.3V1.3L90.3,0l0.3,0.2l-0.1,2.4v6.7C90.6,10.5,90.6,11.8,90.6,13.1z"
id="path16" />
</g>
</g>
<g>
<g>
<path d="M34.3,48.4c0.9,0,1.5-0.4,2.4-1.5l0.2,0.5c-0.7,1.2-1.5,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6
c1,0,1.9,0.4,2.8,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.5C31.8,47.3,32.9,48.4,34.3,48.4z M39.3,48.5v0.6
l-2.8,0.2l-0.2-1.8v-5.3l0.1-0.2v-4.5l-1.5-0.2v-0.6l2.8-0.8L38,36l-0.1,2.6v10.6l-0.3-0.9L39.3,48.5z"/>
<path d="M45.2,48.6v0.5h-4.5v-0.5l1.7-0.3h1.1L45.2,48.6z M43.5,41.9l0.2,0.1v7.1h-1.2V43l-1.4,0.1v-0.6L43.5,41.9z"/>
<g
id="g28">
<g
id="g26">
<path
d="M34.3,48.4c0.9,0,1.5-0.4,2.4-1.5l0.2,0.5c-0.7,1.2-1.5,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6 c1,0,1.9,0.4,2.8,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.5C31.8,47.3,32.9,48.4,34.3,48.4z M39.3,48.5v0.6 l-2.8,0.2l-0.2-1.8v-5.3l0.1-0.2v-4.5l-1.5-0.2v-0.6l2.8-0.8L38,36l-0.1,2.6v10.6l-0.3-0.9L39.3,48.5z"
id="path22" />
</g>
</g>
<g>
<g>
<path d="M102.1,75.4c0.9,0,1.5-0.4,2.4-1.5l0.2,0.5c-0.7,1.2-1.5,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6
c1,0,1.9,0.4,2.8,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.5C99.6,74.3,100.6,75.4,102.1,75.4z M107.1,75.5v0.6
l-2.8,0.2l-0.2-1.8v-5.3l0.1-0.2v-4.5l-1.5-0.2v-0.6l2.8-0.8l0.3,0.1l-0.1,2.6v10.6l-0.3-0.9L107.1,75.5z"/>
<g
id="g34">
<g
id="g32">
<path
d="M102.1,75.4c0.9,0,1.5-0.4,2.4-1.5l0.2,0.5c-0.7,1.2-1.5,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6 c1,0,1.9,0.4,2.8,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.5C99.6,74.3,100.6,75.4,102.1,75.4z M107.1,75.5v0.6 l-2.8,0.2l-0.2-1.8v-5.3l0.1-0.2v-4.5l-1.5-0.2v-0.6l2.8-0.8l0.3,0.1l-0.1,2.6v10.6l-0.3-0.9L107.1,75.5z"
id="path30" />
</g>
</g>
<g>
<g>
<path d="M158,48.4c0.9,0,1.5-0.4,2.4-1.5l0.2,0.5c-0.7,1.2-1.5,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6
c1,0,1.9,0.4,2.8,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.5C155.4,47.3,156.5,48.4,158,48.4z M163,48.5v0.6
l-2.8,0.2l-0.2-1.8v-5.3l0.1-0.2v-4.5l-1.5-0.2v-0.6l2.8-0.8l0.3,0.1l-0.1,2.6v10.6l-0.3-0.9L163,48.5z"/>
<path d="M164.2,48.3l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8c0-0.9-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1
c-0.2,0.7-0.4,0.9-0.7,0.9s-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6l0.3-0.7h4
v1.2h-4.9V48.3z"/>
<g
id="g42">
<g
id="g40">
<path
d="M158,48.4c0.9,0,1.5-0.4,2.4-1.5l0.2,0.5c-0.7,1.2-1.5,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6 c1,0,1.9,0.4,2.8,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.5C155.4,47.3,156.5,48.4,158,48.4z M163,48.5v0.6 l-2.8,0.2l-0.2-1.8v-5.3l0.1-0.2v-4.5l-1.5-0.2v-0.6l2.8-0.8l0.3,0.1l-0.1,2.6v10.6l-0.3-0.9L163,48.5z"
id="path36" />
<path
d="M164.2,48.3l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8c0-0.9-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1 c-0.2,0.7-0.4,0.9-0.7,0.9s-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6l0.3-0.7h4 v1.2h-4.9V48.3z"
id="path38" />
</g>
</g>
<g>
<g>
<path d="M50,83.6l1.8-0.3h0.4l2,0.3v0.7h-4.1v-0.7H50z M55.4,72.2h1.1l4.1,12.2h-1.7l-3.6-10.8h0.4L52,84.4h-0.8L55.4,72.2z
M53.3,79.6h4.9l0.3,0.8h-5.4L53.3,79.6z M56.8,83.6l2.3-0.3h0.4l2.2,0.3v0.7h-4.9V83.6z"/>
<path d="M67.5,83.8v0.5H63v-0.5l1.7-0.3h1.1L67.5,83.8z M65.8,77.1l0.2,0.1v7.1h-1.2v-6.1l-1.4,0.1v-0.6L65.8,77.1z"/>
<g
id="g50">
<g
id="g48">
<path
d="M50,83.6l1.8-0.3h0.4l2,0.3v0.7h-4.1v-0.7H50z M55.4,72.2h1.1l4.1,12.2h-1.7l-3.6-10.8h0.4L52,84.4h-0.8L55.4,72.2z M53.3,79.6h4.9l0.3,0.8h-5.4L53.3,79.6z M56.8,83.6l2.3-0.3h0.4l2.2,0.3v0.7h-4.9V83.6z"
id="path44" />
<path
d="m 67.653352,80.654808 c 0,2.6 -1.2,3.7 -2.5,3.7 -1.3,0 -2.5,-1.1 -2.5,-3.7 0,-2.6 1.2,-3.7 2.5,-3.7 1.3,-0.1 2.5,1 2.5,3.7 z m -2.5,3.1 c 0.6,0 1.2,-0.6 1.2,-3.1 0,-2.6 -0.6,-3.1 -1.2,-3.1 -0.6,0 -1.2,0.5 -1.2,3.1 0,2.5 0.6,3.1 1.2,3.1 z"
id="path62-6" /></g>
</g>
<g
id="g58">
<g
id="g56">
<path
d="M123,83.4l1.8-0.3h0.4l2,0.3v0.7h-4.1v-0.7H123z M128.4,72h1.1l4.1,12.2h-1.7l-3.6-10.8h0.4L125,84.2h-0.8L128.4,72z M126.3,79.4h4.9l0.3,0.8h-5.4L126.3,79.4z M129.8,83.4l2.3-0.3h0.4l2.2,0.3v0.7h-4.9V83.4z"
id="path52" />
<path
d="M135.9,83.3l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8s-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1 c-0.2,0.7-0.4,0.9-0.7,0.9s-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6l0.3-0.7h4 v1.2H136L135.9,83.3L135.9,83.3z"
id="path54" />
</g>
</g>
<g>
<g>
<path d="M123,83.4l1.8-0.3h0.4l2,0.3v0.7h-4.1v-0.7H123z M128.4,72h1.1l4.1,12.2h-1.7l-3.6-10.8h0.4L125,84.2h-0.8L128.4,72z
M126.3,79.4h4.9l0.3,0.8h-5.4L126.3,79.4z M129.8,83.4l2.3-0.3h0.4l2.2,0.3v0.7h-4.9V83.4z"/>
<path d="M135.9,83.3l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8s-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1
c-0.2,0.7-0.4,0.9-0.7,0.9s-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6l0.3-0.7h4
v1.2H136L135.9,83.3L135.9,83.3z"/>
<g
id="g66">
<g
id="g64">
<path
d="M4.6,108.2v0.6H0v-0.6l2-0.4h0.4L4.6,108.2z M1.5,99.9c0-1,0-1.5-0.1-2.3L0,97.4v-0.6l2.4-1.1l0.3,0.2l0.1,1.5h0.1v5.6 l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8H1.5c0-1.3,0.1-2.5,0.1-3.8L1.5,99.9L1.5,99.9z M5.2,96.7c-0.7,0-1.6,0.3-2.9,1.6L2.2,98 c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7 c1.4,0,2.5-1.1,2.5-3.4C7.5,98,6.5,96.7,5.2,96.7z"
id="path60" />
<path
d="M15.7,104.8c0,2.6-1.2,3.7-2.5,3.7c-1.3,0-2.5-1.1-2.5-3.7s1.2-3.7,2.5-3.7C14.5,101,15.7,102.1,15.7,104.8z M13.2,107.9 c0.6,0,1.2-0.6,1.2-3.1c0-2.6-0.6-3.1-1.2-3.1c-0.6,0-1.2,0.5-1.2,3.1C12,107.3,12.6,107.9,13.2,107.9z"
id="path62" />
</g>
</g>
<g>
<g>
<path d="M4.6,108.2v0.6H0v-0.6l2-0.4h0.4L4.6,108.2z M1.5,99.9c0-1,0-1.5-0.1-2.3L0,97.4v-0.6l2.4-1.1l0.3,0.2l0.1,1.5h0.1v5.6
l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8H1.5c0-1.3,0.1-2.5,0.1-3.8L1.5,99.9L1.5,99.9z M5.2,96.7c-0.7,0-1.6,0.3-2.9,1.6L2.2,98
c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7
c1.4,0,2.5-1.1,2.5-3.4C7.5,98,6.5,96.7,5.2,96.7z"/>
<path d="M15.7,104.8c0,2.6-1.2,3.7-2.5,3.7c-1.3,0-2.5-1.1-2.5-3.7s1.2-3.7,2.5-3.7C14.5,101,15.7,102.1,15.7,104.8z M13.2,107.9
c0.6,0,1.2-0.6,1.2-3.1c0-2.6-0.6-3.1-1.2-3.1c-0.6,0-1.2,0.5-1.2,3.1C12,107.3,12.6,107.9,13.2,107.9z"/>
<g
id="g74">
<g
id="g72">
<path
d="M96.9,160.8v0.6h-4.6v-0.6l2-0.4h0.4L96.9,160.8z M93.8,152.5c0-1,0-1.5-0.1-2.3l-1.4-0.2v-0.6l2.4-1.1l0.3,0.2l0.1,1.5 h0.1v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8V152.5z M97.5,149.3c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3 c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7 c1.4,0,2.5-1.1,2.5-3.4C99.8,150.6,98.8,149.3,97.5,149.3z"
id="path68" />
<path
d="M107.8,160.4v0.5h-4.5v-0.5l1.7-0.3h1.1L107.8,160.4z M106.1,153.6l0.2,0.1v7.1h-1.2v-6.1l-1.4,0.1v-0.6L106.1,153.6z"
id="path70" />
</g>
</g>
<g>
<g>
<path d="M96.9,160.8v0.6h-4.6v-0.6l2-0.4h0.4L96.9,160.8z M93.8,152.5c0-1,0-1.5-0.1-2.3l-1.4-0.2v-0.6l2.4-1.1l0.3,0.2l0.1,1.5
h0.1v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8V152.5z M97.5,149.3c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3
c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7
c1.4,0,2.5-1.1,2.5-3.4C99.8,150.6,98.8,149.3,97.5,149.3z"/>
<path d="M107.8,160.4v0.5h-4.5v-0.5l1.7-0.3h1.1L107.8,160.4z M106.1,153.6l0.2,0.1v7.1h-1.2v-6.1l-1.4,0.1v-0.6L106.1,153.6z"/>
<g
id="g82">
<g
id="g80">
<path
d="M211,103.9v0.6h-4.6v-0.6l2-0.4h0.4L211,103.9z M207.9,95.6c0-1,0-1.5-0.1-2.3l-1.4-0.2v-0.6l2.4-1.1l0.3,0.2l0.1,1.5h0.1 v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8L207.9,95.6L207.9,95.6z M211.6,92.4c-0.7,0-1.6,0.3-2.9,1.6 l-0.2-0.3c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7 c1.4,0,2.5-1.1,2.5-3.4C213.9,93.7,212.9,92.4,211.6,92.4z"
id="path76" />
<path
d="M217.2,103.1l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8s-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1 c-0.2,0.7-0.4,0.9-0.7,0.9s-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6l0.3-0.7h4 v1.2h-4.8L217.2,103.1L217.2,103.1z"
id="path78" />
</g>
</g>
<g>
<g>
<path d="M211,103.9v0.6h-4.6v-0.6l2-0.4h0.4L211,103.9z M207.9,95.6c0-1,0-1.5-0.1-2.3l-1.4-0.2v-0.6l2.4-1.1l0.3,0.2l0.1,1.5h0.1
v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8L207.9,95.6L207.9,95.6z M211.6,92.4c-0.7,0-1.6,0.3-2.9,1.6
l-0.2-0.3c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7
c1.4,0,2.5-1.1,2.5-3.4C213.9,93.7,212.9,92.4,211.6,92.4z"/>
<path d="M217.2,103.1l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8s-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1
c-0.2,0.7-0.4,0.9-0.7,0.9s-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6l0.3-0.7h4
v1.2h-4.8L217.2,103.1L217.2,103.1z"/>
</g>
</g>
</svg>
<path
d="m 45.360441,45.707672 c 0,2.6 -1.2,3.7 -2.5,3.7 -1.3,0 -2.5,-1.1 -2.5,-3.7 0,-2.6 1.2,-3.7 2.5,-3.7 1.3,-0.1 2.5,1 2.5,3.7 z m -2.5,3.1 c 0.6,0 1.2,-0.6 1.2,-3.1 0,-2.6 -0.6,-3.1 -1.2,-3.1 -0.6,0 -1.2,0.5 -1.2,3.1 0,2.5 0.6,3.1 1.2,3.1 z"
id="path62-5" /></svg>

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@ -1,235 +1,433 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="501.3px" height="161.7px" viewBox="0 0 501.3 161.7" style="enable-background:new 0 0 501.3 161.7;" xml:space="preserve"
>
<style type="text/css">
<svg
version="1.1"
id="Layer_1"
x="0px"
y="0px"
width="501.3px"
height="161.7px"
viewBox="0 0 501.3 161.7"
style="enable-background:new 0 0 501.3 161.7;"
xml:space="preserve"
sodipodi:docname="three_point_family.svg"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs227" /><sodipodi:namedview
id="namedview225"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="2.7029723"
inkscape:cx="186.09143"
inkscape:cy="-27.377269"
inkscape:window-width="2560"
inkscape:window-height="1371"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<style
type="text/css"
id="style2">
.st0{fill:none;stroke:#000000;stroke-miterlimit:10;}
.st1{display:none;}
.st2{display:inline;}
</style>
<circle cx="4.4" cy="87.7" r="2.5"/>
<circle cx="96.9" cy="140.6" r="2.5"/>
<circle cx="210" cy="83.4" r="2.5"/>
<circle cx="90.9" cy="21.1" r="2.5"/>
<polyline class="st0" points="90.9,21.1 4.4,87.7 96.9,140.6 90.9,21.1 "/>
<polyline class="st0" points="96.9,140.6 210,83.3 90.9,21.1 "/>
<g class="st1">
<g class="st2">
<path class="st0" d="M68.3,119.4c5.9-8.7,15.6-14.6,26.8-15.2"/>
<g>
<polygon points="71.6,119.1 68.8,118.7 67.1,116.4 66.2,123 "/>
<circle
cx="4.4"
cy="87.7"
r="2.5"
id="circle4" />
<circle
cx="96.9"
cy="140.6"
r="2.5"
id="circle6" />
<circle
cx="210"
cy="83.4"
r="2.5"
id="circle8" />
<circle
cx="90.9"
cy="21.1"
r="2.5"
id="circle10" />
<polyline
class="st0"
points="90.9,21.1 4.4,87.7 96.9,140.6 90.9,21.1 "
id="polyline12" />
<polyline
class="st0"
points="96.9,140.6 210,83.3 90.9,21.1 "
id="polyline14" />
<g
class="st1"
id="g24">
<g
class="st2"
id="g22">
<path
class="st0"
d="M68.3,119.4c5.9-8.7,15.6-14.6,26.8-15.2"
id="path16" />
<g
id="g20">
<polygon
points="71.6,119.1 68.8,118.7 67.1,116.4 66.2,123 "
id="polygon18" />
</g>
</g>
</g>
<g class="st1">
<g class="st2">
<path class="st0" d="M99.2,108c10,2.3,18.8,9.2,23.3,19.2"/>
<g>
<polygon points="100.7,110.9 100.1,108.2 101.6,105.8 95.1,107.3 "/>
<g
class="st1"
id="g34">
<g
class="st2"
id="g32">
<path
class="st0"
d="M99.2,108c10,2.3,18.8,9.2,23.3,19.2"
id="path26" />
<g
id="g30">
<polygon
points="100.7,110.9 100.1,108.2 101.6,105.8 95.1,107.3 "
id="polygon28" />
</g>
</g>
</g>
<g class="st1">
<g class="st2">
<path class="st0" d="M173.1,96.8c-3.6-9.9-2.6-21.3,3.5-30.6"/>
<g>
<polygon points="174.8,94 172.8,96 170,96 174.8,100.6 "/>
<g
class="st1"
id="g44">
<g
class="st2"
id="g42">
<path
class="st0"
d="M173.1,96.8c-3.6-9.9-2.6-21.3,3.5-30.6"
id="path36" />
<g
id="g40">
<polygon
points="174.8,94 172.8,96 170,96 174.8,100.6 "
id="polygon38" />
</g>
</g>
</g>
<g class="st1">
<g class="st2">
<path class="st0" d="M30.4,73.1c5.6,8.9,7.1,20.2,3.1,30.7"/>
<g>
<polygon points="29.3,76.2 30.8,73.8 33.6,73.2 27.9,69.7 "/>
<g
class="st1"
id="g54">
<g
class="st2"
id="g52">
<path
class="st0"
d="M30.4,73.1c5.6,8.9,7.1,20.2,3.1,30.7"
id="path46" />
<g
id="g50">
<polygon
points="29.3,76.2 30.8,73.8 33.6,73.2 27.9,69.7 "
id="polygon48" />
</g>
</g>
</g>
<g class="st1">
<g class="st2">
<path class="st0" d="M117,39.5c-4.5,9.5-13.3,16.8-24.2,19.1"/>
<g>
<polygon points="113.8,40.3 116.6,40.2 118.6,42.2 118.6,35.6 "/>
<g
class="st1"
id="g64">
<g
class="st2"
id="g62">
<path
class="st0"
d="M117,39.5c-4.5,9.5-13.3,16.8-24.2,19.1"
id="path56" />
<g
id="g60">
<polygon
points="113.8,40.3 116.6,40.2 118.6,42.2 118.6,35.6 "
id="polygon58" />
</g>
</g>
</g>
<g class="st1">
<g class="st2">
<path class="st0" d="M88.2,55c-10,0.9-20.2-2.8-27.3-10.7"/>
<g>
<polygon points="85.9,52.6 87.3,55 86.6,57.8 92.3,54.3 "/>
<g
class="st1"
id="g74">
<g
class="st2"
id="g72">
<path
class="st0"
d="M88.2,55c-10,0.9-20.2-2.8-27.3-10.7"
id="path66" />
<g
id="g70">
<polygon
points="85.9,52.6 87.3,55 86.6,57.8 92.3,54.3 "
id="polygon68" />
</g>
</g>
</g>
<g>
<g class="st1">
<path class="st2" d="M84.9,121.7v1.5c-0.4-2-1.1-3.9-1.8-5.7c-0.5-1.5-0.8-2-1.4-2c-0.2,0-0.4,0-0.6,0.1l-0.2-0.5
c0.4-0.4,0.8-0.7,1.4-0.7c0.7,0,1.3,0.3,2,2.4c0.4,1.1,0.9,2.8,1.4,5.4l0.1,0.1l0.1,3.9l-1.4,0.3l-0.2-0.2L84.9,121.7z
M85.4,122.3c1.2-2.1,1.7-3.5,2.5-5.5l-0.2,1.4c-0.2-1.7-0.3-2.3-0.3-2.7c0-0.8,0.4-1.2,0.9-1.2c0.3,0,0.5,0.1,0.6,0.2
c0.1,0.2,0.1,0.4,0.1,0.8c0,1.9-1.8,5.3-3.3,7.8L85.4,122.3z"/>
<g
id="g80">
<g
class="st1"
id="g78">
<path
class="st2"
d="M84.9,121.7v1.5c-0.4-2-1.1-3.9-1.8-5.7c-0.5-1.5-0.8-2-1.4-2c-0.2,0-0.4,0-0.6,0.1l-0.2-0.5 c0.4-0.4,0.8-0.7,1.4-0.7c0.7,0,1.3,0.3,2,2.4c0.4,1.1,0.9,2.8,1.4,5.4l0.1,0.1l0.1,3.9l-1.4,0.3l-0.2-0.2L84.9,121.7z M85.4,122.3c1.2-2.1,1.7-3.5,2.5-5.5l-0.2,1.4c-0.2-1.7-0.3-2.3-0.3-2.7c0-0.8,0.4-1.2,0.9-1.2c0.3,0,0.5,0.1,0.6,0.2 c0.1,0.2,0.1,0.4,0.1,0.8c0,1.9-1.8,5.3-3.3,7.8L85.4,122.3z"
id="path76" />
</g>
</g>
<g>
<g class="st1">
<path class="st2" d="M101,128v-8.8c0-2.6,1.6-4.3,3.8-4.3c1.8,0,3.2,1.1,3.2,3c0,1.4-1,3.3-3.6,3.5l-0.1-0.2c3-0.1,4.7,1,4.7,3.4
c0,2.5-1.9,3.8-3.7,3.8c-1.3,0-2.6-0.6-3.3-2.2l0.2-0.2c0.8,0.7,1.7,1.2,2.8,1.2c1.7,0,2.6-1.1,2.6-2.7c0-1.5-0.8-2.9-3.5-2.8
l-0.2-1c1.7-0.3,2.7-1.3,2.7-2.9c0-1.4-0.7-2.3-1.9-2.3c-1.3,0-2.2,1.2-2.2,3.2v7.7l-0.1,0.3l0.3,4.5l-1.4,0.3l-0.2-0.2L101,128z"
/>
<g
id="g86">
<g
class="st1"
id="g84">
<path
class="st2"
d="M101,128v-8.8c0-2.6,1.6-4.3,3.8-4.3c1.8,0,3.2,1.1,3.2,3c0,1.4-1,3.3-3.6,3.5l-0.1-0.2c3-0.1,4.7,1,4.7,3.4 c0,2.5-1.9,3.8-3.7,3.8c-1.3,0-2.6-0.6-3.3-2.2l0.2-0.2c0.8,0.7,1.7,1.2,2.8,1.2c1.7,0,2.6-1.1,2.6-2.7c0-1.5-0.8-2.9-3.5-2.8 l-0.2-1c1.7-0.3,2.7-1.3,2.7-2.9c0-1.4-0.7-2.3-1.9-2.3c-1.3,0-2.2,1.2-2.2,3.2v7.7l-0.1,0.3l0.3,4.5l-1.4,0.3l-0.2-0.2L101,128z"
id="path82" />
</g>
</g>
<g>
<g class="st1">
<path class="st2" d="M21.8,90v-8.8c0-2.6,1.6-4.3,3.8-4.3c1.8,0,3.2,1.1,3.2,3c0,1.4-1,3.3-3.6,3.5l-0.1-0.2c3-0.1,4.7,1,4.7,3.4
c0,2.5-1.9,3.8-3.7,3.8c-1.3,0-2.6-0.6-3.3-2.2L23,88c0.8,0.7,1.7,1.2,2.8,1.2c1.7,0,2.6-1.1,2.6-2.7c0-1.5-0.8-2.9-3.5-2.8
l-0.2-1c1.7-0.3,2.7-1.3,2.7-2.9c0-1.4-0.7-2.3-1.9-2.3c-1.3,0-2.2,1.2-2.2,3.2v7.7l-0.1,0.3l0.3,4.5l-1.4,0.3l-0.2-0.2L21.8,90z"
/>
<g
id="g92">
<g
class="st1"
id="g90">
<path
class="st2"
d="M21.8,90v-8.8c0-2.6,1.6-4.3,3.8-4.3c1.8,0,3.2,1.1,3.2,3c0,1.4-1,3.3-3.6,3.5l-0.1-0.2c3-0.1,4.7,1,4.7,3.4 c0,2.5-1.9,3.8-3.7,3.8c-1.3,0-2.6-0.6-3.3-2.2L23,88c0.8,0.7,1.7,1.2,2.8,1.2c1.7,0,2.6-1.1,2.6-2.7c0-1.5-0.8-2.9-3.5-2.8 l-0.2-1c1.7-0.3,2.7-1.3,2.7-2.9c0-1.4-0.7-2.3-1.9-2.3c-1.3,0-2.2,1.2-2.2,3.2v7.7l-0.1,0.3l0.3,4.5l-1.4,0.3l-0.2-0.2L21.8,90z"
id="path88" />
</g>
</g>
<g>
<g class="st1">
<path class="st2" d="M182.6,84.9v1.5c-0.4-2-1.1-3.9-1.8-5.7c-0.5-1.5-0.8-2-1.4-2c-0.2,0-0.4,0-0.6,0.1l-0.2-0.5
c0.4-0.4,0.8-0.7,1.4-0.7c0.7,0,1.3,0.3,2,2.4c0.4,1.1,0.9,2.8,1.4,5.4l0.1,0.1l0.1,3.9l-1.4,0.3l-0.2-0.2L182.6,84.9z
M183.1,85.5c1.2-2.1,1.7-3.5,2.5-5.5l-0.2,1.4c-0.2-1.7-0.3-2.3-0.3-2.7c0-0.8,0.4-1.2,0.9-1.2c0.3,0,0.5,0.1,0.6,0.2
c0.1,0.2,0.1,0.4,0.1,0.8c0,1.9-1.8,5.3-3.3,7.8L183.1,85.5z"/>
<g
id="g98">
<g
class="st1"
id="g96">
<path
class="st2"
d="M182.6,84.9v1.5c-0.4-2-1.1-3.9-1.8-5.7c-0.5-1.5-0.8-2-1.4-2c-0.2,0-0.4,0-0.6,0.1l-0.2-0.5 c0.4-0.4,0.8-0.7,1.4-0.7c0.7,0,1.3,0.3,2,2.4c0.4,1.1,0.9,2.8,1.4,5.4l0.1,0.1l0.1,3.9l-1.4,0.3l-0.2-0.2L182.6,84.9z M183.1,85.5c1.2-2.1,1.7-3.5,2.5-5.5l-0.2,1.4c-0.2-1.7-0.3-2.3-0.3-2.7c0-0.8,0.4-1.2,0.9-1.2c0.3,0,0.5,0.1,0.6,0.2 c0.1,0.2,0.1,0.4,0.1,0.8c0,1.9-1.8,5.3-3.3,7.8L183.1,85.5z"
id="path94" />
</g>
</g>
<circle cx="284.1" cy="88.3" r="2.5"/>
<circle cx="376.6" cy="141.2" r="2.5"/>
<circle cx="489.6" cy="84" r="2.5"/>
<circle cx="370.5" cy="21.6" r="2.5"/>
<line class="st0" x1="370.5" y1="21.6" x2="284.1" y2="88.3"/>
<line class="st0" x1="489.6" y1="83.9" x2="370.5" y2="21.6"/>
<polygon class="st0" points="376.6,141.2 284.1,88.3 489.6,83.9 "/>
<g>
<g>
<path d="M367.8,54.4v-0.7h2.5v1h-0.2L367.8,54.4z M370.3,64.8v1h-2.5V65l2.3-0.3h0.2V64.8z M369.5,59.3c0-1.9,0-3.7-0.1-5.6h1.7
c-0.1,1.8-0.1,3.7-0.1,5.6v0.6c0,2.1,0,4,0.1,5.9h-1.7c0.1-1.8,0.1-3.7,0.1-5.6V59.3z M370.3,59.1h1.8c2.2,0,3.2-0.7,3.2-2.4
s-1-2.2-2.8-2.2h-2.2v-0.8h2.7c2.5,0,3.9,1.2,3.9,3c0,1.4-1,2.6-3.4,2.9v-0.2c2.8,0.2,4,1.5,4,3c0,1.6-1.4,3.3-4.8,3.3h-2.4v-0.8
h2c2.3,0,3.5-0.9,3.5-2.5c0-1.7-1.1-2.5-3.6-2.5h-1.9L370.3,59.1L370.3,59.1z"/>
<circle
cx="284.1"
cy="88.3"
r="2.5"
id="circle100" />
<circle
cx="376.6"
cy="141.2"
r="2.5"
id="circle102" />
<circle
cx="489.6"
cy="84"
r="2.5"
id="circle104" />
<circle
cx="370.5"
cy="21.6"
r="2.5"
id="circle106" />
<line
class="st0"
x1="370.5"
y1="21.6"
x2="284.1"
y2="88.3"
id="line108" />
<line
class="st0"
x1="489.6"
y1="83.9"
x2="370.5"
y2="21.6"
id="line110" />
<polygon
class="st0"
points="376.6,141.2 284.1,88.3 489.6,83.9 "
id="polygon112" />
<g
id="g118">
<g
id="g116">
<path
d="M367.8,54.4v-0.7h2.5v1h-0.2L367.8,54.4z M370.3,64.8v1h-2.5V65l2.3-0.3h0.2V64.8z M369.5,59.3c0-1.9,0-3.7-0.1-5.6h1.7 c-0.1,1.8-0.1,3.7-0.1,5.6v0.6c0,2.1,0,4,0.1,5.9h-1.7c0.1-1.8,0.1-3.7,0.1-5.6V59.3z M370.3,59.1h1.8c2.2,0,3.2-0.7,3.2-2.4 s-1-2.2-2.8-2.2h-2.2v-0.8h2.7c2.5,0,3.9,1.2,3.9,3c0,1.4-1,2.6-3.4,2.9v-0.2c2.8,0.2,4,1.5,4,3c0,1.6-1.4,3.3-4.8,3.3h-2.4v-0.8 h2c2.3,0,3.5-0.9,3.5-2.5c0-1.7-1.1-2.5-3.6-2.5h-1.9L370.3,59.1L370.3,59.1z"
id="path114" />
</g>
</g>
<g>
<g class="st1">
<path class="st2" d="M380.7,113.1v1l-1.1,0.3l0.5-2.7h1.1l-0.1,2.6c-1.1,0.6-2.4,0.9-3.6,0.9c-3.6,0-6.1-2.6-6.1-6.3
c0-3.6,2.6-6.3,6.1-6.3c1,0,2.3,0.2,3.5,0.9l0.1,2.6H380l-0.5-2.7l1.1,0.3v1.1c-1-1-2-1.3-2.9-1.3c-2.4,0-4.4,1.9-4.4,5.4
s1.8,5.4,4.3,5.4C378.5,114.2,379.7,113.9,380.7,113.1z"/>
<g
id="g124">
<g
class="st1"
id="g122">
<path
class="st2"
d="M380.7,113.1v1l-1.1,0.3l0.5-2.7h1.1l-0.1,2.6c-1.1,0.6-2.4,0.9-3.6,0.9c-3.6,0-6.1-2.6-6.1-6.3 c0-3.6,2.6-6.3,6.1-6.3c1,0,2.3,0.2,3.5,0.9l0.1,2.6H380l-0.5-2.7l1.1,0.3v1.1c-1-1-2-1.3-2.9-1.3c-2.4,0-4.4,1.9-4.4,5.4 s1.8,5.4,4.3,5.4C378.5,114.2,379.7,113.9,380.7,113.1z"
id="path120" />
</g>
</g>
<g>
<g>
<path d="M88.4,8c0.9,0,1.5-0.4,2.4-1.5L91,7c-0.8,1.4-1.7,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4C84.4,1.9,86,0,88.2,0c1,0,2,0.4,2.9,1.8
l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3C87.1,1,86,2.1,86,4.6C85.9,6.9,86.9,8,88.4,8z M91.5,12.1l2,0.4v0.6h-4.6v-0.6l2.2-0.4H91.5z
M92,13.1h-1.5c0-1.3,0.1-2.6,0.1-3.8V7.5l-0.1-0.3V1.3L91.7,0L92,0.2l-0.1,2.4v6.7C92,10.5,92,11.8,92,13.1z"/>
<g
id="g130">
<g
id="g128">
<path
d="M88.4,8c0.9,0,1.5-0.4,2.4-1.5L91,7c-0.8,1.4-1.7,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4C84.4,1.9,86,0,88.2,0c1,0,2,0.4,2.9,1.8 l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3C87.1,1,86,2.1,86,4.6C85.9,6.9,86.9,8,88.4,8z M91.5,12.1l2,0.4v0.6h-4.6v-0.6l2.2-0.4H91.5z M92,13.1h-1.5c0-1.3,0.1-2.6,0.1-3.8V7.5l-0.1-0.3V1.3L91.7,0L92,0.2l-0.1,2.4v6.7C92,10.5,92,11.8,92,13.1z"
id="path126" />
</g>
</g>
<g>
<g>
<path d="M367.7,8c0.9,0,1.5-0.4,2.4-1.5l0.2,0.5c-0.8,1.4-1.7,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6
c1,0,2,0.4,2.9,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.6C365.2,6.9,366.3,8,367.7,8z M370.8,12.1l2,0.4v0.6h-4.6
v-0.6l2.2-0.4H370.8z M371.4,13.1h-1.5c0-1.3,0.1-2.6,0.1-3.8V7.5l-0.1-0.3V1.3l1.2-1.3l0.3,0.2l-0.1,2.4v6.7
C371.3,10.5,371.3,11.8,371.4,13.1z"/>
<g
id="g136">
<g
id="g134">
<path
d="M367.7,8c0.9,0,1.5-0.4,2.4-1.5l0.2,0.5c-0.8,1.4-1.7,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6 c1,0,2,0.4,2.9,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.6C365.2,6.9,366.3,8,367.7,8z M370.8,12.1l2,0.4v0.6h-4.6 v-0.6l2.2-0.4H370.8z M371.4,13.1h-1.5c0-1.3,0.1-2.6,0.1-3.8V7.5l-0.1-0.3V1.3l1.2-1.3l0.3,0.2l-0.1,2.4v6.7 C371.3,10.5,371.3,11.8,371.4,13.1z"
id="path132" />
</g>
</g>
<g>
<g>
<path d="M34.2,50c0.9,0,1.5-0.4,2.4-1.5l0.2,0.5c-0.7,1.2-1.5,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6
c1,0,1.9,0.4,2.8,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.5C31.7,49,32.7,50,34.2,50z M39.2,50.2v0.6L36.4,51
l-0.2-1.8v-5.3l0.1-0.2v-4.5L34.8,39v-0.6l2.8-0.8l0.3,0.1l-0.1,2.6v10.6L37.5,50L39.2,50.2z"/>
<path d="M45.1,50.3v0.5h-4.5v-0.5l1.7-0.3h1.1L45.1,50.3z M43.4,43.5l0.2,0.1v7.1h-1.2v-6.1l-1.5,0.2v-0.6L43.4,43.5z"/>
<g
id="g144">
<g
id="g142">
<path
d="M34.2,50c0.9,0,1.5-0.4,2.4-1.5l0.2,0.5c-0.7,1.2-1.5,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6 c1,0,1.9,0.4,2.8,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.5C31.7,49,32.7,50,34.2,50z M39.2,50.2v0.6L36.4,51 l-0.2-1.8v-5.3l0.1-0.2v-4.5L34.8,39v-0.6l2.8-0.8l0.3,0.1l-0.1,2.6v10.6L37.5,50L39.2,50.2z"
id="path138" />
</g>
</g>
<g>
<g>
<path d="M104.3,81.7c0.9,0,1.5-0.4,2.4-1.5l0.2,0.5c-0.7,1.2-1.5,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6
c1,0,1.9,0.4,2.8,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.5C101.8,80.6,102.8,81.7,104.3,81.7z M109.3,81.8v0.6
l-2.8,0.2l-0.2-1.8v-5.3l0.1-0.2v-4.6l-1.5-0.2v-0.6l2.8-0.8l0.3,0.1l-0.1,2.6v10.6l-0.3-0.9L109.3,81.8z"/>
<g
id="g150">
<g
id="g148">
<path
d="M104.3,81.7c0.9,0,1.5-0.4,2.4-1.5l0.2,0.5c-0.7,1.2-1.5,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6 c1,0,1.9,0.4,2.8,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.5C101.8,80.6,102.8,81.7,104.3,81.7z M109.3,81.8v0.6 l-2.8,0.2l-0.2-1.8v-5.3l0.1-0.2v-4.6l-1.5-0.2v-0.6l2.8-0.8l0.3,0.1l-0.1,2.6v10.6l-0.3-0.9L109.3,81.8z"
id="path146" />
</g>
</g>
<g>
<g>
<path d="M160.1,49c0.9,0,1.5-0.4,2.4-1.5l0.2,0.5c-0.7,1.2-1.5,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6
c1,0,1.9,0.4,2.8,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.5C157.5,48,158.6,49,160.1,49z M165.1,49.2v0.6
l-2.8,0.2l-0.2-1.8v-5.3l0.1-0.2v-4.5l-1.5-0.2v-0.6l2.8-0.8l0.3,0.1l-0.1,2.6v10.6l-0.3-0.9L165.1,49.2z"/>
<path d="M166.3,48.9l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8c0-0.9-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1
c-0.2,0.7-0.4,0.9-0.7,0.9s-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6l0.3-0.7h4
v1.2h-4.8L166.3,48.9L166.3,48.9z"/>
<g
id="g158">
<g
id="g156">
<path
d="M160.1,49c0.9,0,1.5-0.4,2.4-1.5l0.2,0.5c-0.7,1.2-1.5,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6 c1,0,1.9,0.4,2.8,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.5C157.5,48,158.6,49,160.1,49z M165.1,49.2v0.6 l-2.8,0.2l-0.2-1.8v-5.3l0.1-0.2v-4.5l-1.5-0.2v-0.6l2.8-0.8l0.3,0.1l-0.1,2.6v10.6l-0.3-0.9L165.1,49.2z"
id="path152" />
<path
d="M166.3,48.9l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8c0-0.9-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1 c-0.2,0.7-0.4,0.9-0.7,0.9s-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6l0.3-0.7h4 v1.2h-4.8L166.3,48.9L166.3,48.9z"
id="path154" />
</g>
</g>
<g>
<g>
<path d="M55.3,87.6l1.8-0.3h0.4l2,0.3v0.7h-4.1L55.3,87.6L55.3,87.6z M60.7,76.2h1.1l4.1,12.1h-1.7l-3.6-10.8H61l-3.7,10.8h-0.8
L60.7,76.2z M58.6,83.6h4.9l0.3,0.8h-5.4L58.6,83.6z M62.2,87.6l2.3-0.3h0.4l2.2,0.3v0.7h-4.9C62.2,88.3,62.2,87.6,62.2,87.6z"/>
<path d="M72.8,87.8v0.5h-4.5v-0.5l1.7-0.3h1.1L72.8,87.8z M71.1,81.1l0.2,0.1v7.1h-1.2v-6.1l-1.4,0.1v-0.6L71.1,81.1z"/>
<g
id="g166"
transform="translate(-1.5665625,1.0361857)">
<g
id="g164">
<path
d="m 55.3,87.6 1.8,-0.3 h 0.4 l 2,0.3 v 0.7 h -4.1 l -0.1,-0.7 z m 5.4,-11.4 h 1.1 l 4.1,12.1 H 64.2 L 60.6,77.5 H 61 l -3.7,10.8 h -0.8 z m -2.1,7.4 h 4.9 l 0.3,0.8 h -5.4 z m 3.6,4 2.3,-0.3 h 0.4 l 2.2,0.3 v 0.7 h -4.9 c 0,0 0,-0.7 0,-0.7 z"
id="path160" />
</g>
</g>
<g>
<g>
<path d="M126.5,85.8l1.8-0.3h0.4l2,0.3v0.7h-4.1L126.5,85.8L126.5,85.8z M131.8,74.4h1.1l4.1,12.2h-1.7l-3.6-10.8h0.4l-3.7,10.8
h-0.8L131.8,74.4z M129.7,81.8h4.9l0.3,0.8h-5.4L129.7,81.8z M133.3,85.8l2.3-0.3h0.4l2.2,0.3v0.7h-4.9V85.8z"/>
<path d="M139.3,85.7l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8s-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1
c-0.2,0.7-0.4,0.9-0.7,0.9s-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6l0.3-0.7h4
v1.2h-4.8L139.3,85.7L139.3,85.7z"/>
<g
id="g174"
transform="translate(4.0074518,2.1561911)">
<g
id="g172">
<path
d="m 126.5,85.8 1.8,-0.3 h 0.4 l 2,0.3 v 0.7 h -4.1 l -0.1,-0.7 z m 5.3,-11.4 h 1.1 l 4.1,12.2 h -1.7 l -3.6,-10.8 h 0.4 l -3.7,10.8 h -0.8 z m -2.1,7.4 h 4.9 l 0.3,0.8 h -5.4 z m 3.6,4 2.3,-0.3 h 0.4 l 2.2,0.3 v 0.7 h -4.9 z"
id="path168" />
<path
d="m 139.3,85.7 1.7,-1.6 c 1.1,-1 1.6,-1.9 1.6,-2.8 0,-0.9 -0.5,-1.4 -1.4,-1.4 -0.3,0 -0.7,0.1 -1.1,0.2 l 0.7,-0.5 -0.3,1 c -0.2,0.7 -0.4,0.9 -0.7,0.9 -0.3,0 -0.5,-0.2 -0.6,-0.4 0.1,-1.2 1.2,-1.8 2.5,-1.8 1.6,0 2.2,0.9 2.2,1.9 0,1 -0.7,1.8 -2.3,3.3 l -1.7,1.6 0.3,-0.7 h 4 v 1.2 h -4.8 l -0.1,-0.9 z"
id="path170" />
</g>
</g>
<g>
<g>
<path d="M4.6,111.1v0.6H0v-0.6l2-0.4h0.4L4.6,111.1z M1.5,102.8c0-1,0-1.5-0.1-2.3L0,100.3v-0.6l2.4-1.1l0.3,0.2l0.1,1.5h0.1v5.6
L2.8,106v1.8c0,1.3,0,2.6,0.1,3.8H1.5c0-1.3,0.1-2.5,0.1-3.8L1.5,102.8L1.5,102.8z M5.2,99.6c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3
c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7
c1.4,0,2.5-1.1,2.5-3.4C7.5,100.9,6.5,99.6,5.2,99.6z"/>
<path d="M15.7,107.7c0,2.6-1.2,3.7-2.5,3.7c-1.3,0-2.5-1.1-2.5-3.7s1.2-3.7,2.5-3.7C14.5,103.9,15.7,105,15.7,107.7z M13.2,110.8
c0.6,0,1.2-0.6,1.2-3.1c0-2.6-0.6-3.1-1.2-3.1c-0.6,0-1.2,0.5-1.2,3.1C12,110.2,12.6,110.8,13.2,110.8z"/>
<g
id="g182">
<g
id="g180">
<path
d="M4.6,111.1v0.6H0v-0.6l2-0.4h0.4L4.6,111.1z M1.5,102.8c0-1,0-1.5-0.1-2.3L0,100.3v-0.6l2.4-1.1l0.3,0.2l0.1,1.5h0.1v5.6 L2.8,106v1.8c0,1.3,0,2.6,0.1,3.8H1.5c0-1.3,0.1-2.5,0.1-3.8L1.5,102.8L1.5,102.8z M5.2,99.6c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3 c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7 c1.4,0,2.5-1.1,2.5-3.4C7.5,100.9,6.5,99.6,5.2,99.6z"
id="path176" />
<path
d="M15.7,107.7c0,2.6-1.2,3.7-2.5,3.7c-1.3,0-2.5-1.1-2.5-3.7s1.2-3.7,2.5-3.7C14.5,103.9,15.7,105,15.7,107.7z M13.2,110.8 c0.6,0,1.2-0.6,1.2-3.1c0-2.6-0.6-3.1-1.2-3.1c-0.6,0-1.2,0.5-1.2,3.1C12,110.2,12.6,110.8,13.2,110.8z"
id="path178" />
</g>
</g>
<g>
<g>
<path d="M284.8,111.1v0.6h-4.6v-0.6l2-0.4h0.4L284.8,111.1z M281.7,102.8c0-1,0-1.5-0.1-2.3l-1.4-0.2v-0.6l2.4-1.1l0.3,0.2
l0.1,1.5h0.1v5.6L283,106v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8V102.8z M285.4,99.6c-0.7,0-1.6,0.3-2.9,1.6
l-0.2-0.3c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7
c1.4,0,2.5-1.1,2.5-3.4C287.7,100.9,286.7,99.6,285.4,99.6z"/>
<path d="M295.9,107.7c0,2.6-1.2,3.7-2.5,3.7s-2.5-1.1-2.5-3.7s1.2-3.7,2.5-3.7C294.7,103.9,295.9,105,295.9,107.7z M293.4,110.8
c0.6,0,1.2-0.6,1.2-3.1c0-2.6-0.6-3.1-1.2-3.1s-1.2,0.5-1.2,3.1C292.2,110.2,292.8,110.8,293.4,110.8z"/>
<g
id="g190">
<g
id="g188">
<path
d="M284.8,111.1v0.6h-4.6v-0.6l2-0.4h0.4L284.8,111.1z M281.7,102.8c0-1,0-1.5-0.1-2.3l-1.4-0.2v-0.6l2.4-1.1l0.3,0.2 l0.1,1.5h0.1v5.6L283,106v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8V102.8z M285.4,99.6c-0.7,0-1.6,0.3-2.9,1.6 l-0.2-0.3c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7 c1.4,0,2.5-1.1,2.5-3.4C287.7,100.9,286.7,99.6,285.4,99.6z"
id="path184" />
<path
d="M295.9,107.7c0,2.6-1.2,3.7-2.5,3.7s-2.5-1.1-2.5-3.7s1.2-3.7,2.5-3.7C294.7,103.9,295.9,105,295.9,107.7z M293.4,110.8 c0.6,0,1.2-0.6,1.2-3.1c0-2.6-0.6-3.1-1.2-3.1s-1.2,0.5-1.2,3.1C292.2,110.2,292.8,110.8,293.4,110.8z"
id="path186" />
</g>
</g>
<g>
<g>
<path d="M97.6,161.1v0.6H93v-0.6l2-0.4h0.4L97.6,161.1z M94.5,152.8c0-1,0-1.5-0.1-2.3l-1.4-0.2v-0.6l2.4-1.1l0.3,0.2l0.1,1.5h0.1
v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8V152.8z M98.2,149.6c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3
c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7
c1.4,0,2.5-1.1,2.5-3.4C100.5,150.9,99.5,149.6,98.2,149.6z"/>
<path d="M108.5,160.7v0.5H104v-0.5l1.7-0.3h1.1L108.5,160.7z M106.8,154l0.2,0.1v7.1h-1.2v-6.1l-1.4,0.1v-0.6L106.8,154z"/>
<g
id="g198">
<g
id="g196">
<path
d="M97.6,161.1v0.6H93v-0.6l2-0.4h0.4L97.6,161.1z M94.5,152.8c0-1,0-1.5-0.1-2.3l-1.4-0.2v-0.6l2.4-1.1l0.3,0.2l0.1,1.5h0.1 v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8V152.8z M98.2,149.6c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3 c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7 c1.4,0,2.5-1.1,2.5-3.4C100.5,150.9,99.5,149.6,98.2,149.6z"
id="path192" />
<path
d="M108.5,160.7v0.5H104v-0.5l1.7-0.3h1.1L108.5,160.7z M106.8,154l0.2,0.1v7.1h-1.2v-6.1l-1.4,0.1v-0.6L106.8,154z"
id="path194" />
</g>
</g>
<g>
<g>
<path d="M377.6,161.1v0.6H373v-0.6l2-0.4h0.4L377.6,161.1z M374.5,152.8c0-1,0-1.5-0.1-2.3l-1.4-0.2v-0.6l2.4-1.1l0.3,0.2l0.1,1.5
h0.1v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8V152.8z M378.2,149.6c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3
c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7
c1.4,0,2.5-1.1,2.5-3.4C380.5,150.9,379.5,149.6,378.2,149.6z"/>
<path d="M388.5,160.7v0.5H384v-0.5l1.7-0.3h1.1L388.5,160.7z M386.8,154l0.2,0.1v7.1h-1.2v-6.1l-1.4,0.1v-0.6L386.8,154z"/>
<g
id="g206">
<g
id="g204">
<path
d="M377.6,161.1v0.6H373v-0.6l2-0.4h0.4L377.6,161.1z M374.5,152.8c0-1,0-1.5-0.1-2.3l-1.4-0.2v-0.6l2.4-1.1l0.3,0.2l0.1,1.5 h0.1v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8V152.8z M378.2,149.6c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3 c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7 c1.4,0,2.5-1.1,2.5-3.4C380.5,150.9,379.5,149.6,378.2,149.6z"
id="path200" />
<path
d="M388.5,160.7v0.5H384v-0.5l1.7-0.3h1.1L388.5,160.7z M386.8,154l0.2,0.1v7.1h-1.2v-6.1l-1.4,0.1v-0.6L386.8,154z"
id="path202" />
</g>
</g>
<g>
<g>
<path d="M210.7,105.5v0.6h-4.6v-0.6l2-0.4h0.4L210.7,105.5z M207.6,97.1c0-1,0-1.5-0.1-2.3l-1.4-0.2V94l2.4-1.1l0.3,0.2l0.1,1.5
h0.1v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8L207.6,97.1L207.6,97.1z M211.3,94c-0.7,0-1.6,0.3-2.9,1.6
l-0.2-0.3c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7
c1.4,0,2.5-1.1,2.5-3.4C213.6,95.3,212.6,94,211.3,94z"/>
<path d="M216.9,104.7l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8s-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1
c-0.2,0.7-0.4,0.9-0.7,0.9s-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6l0.3-0.7h4
v1.2H217L216.9,104.7L216.9,104.7z"/>
<g
id="g214">
<g
id="g212">
<path
d="M210.7,105.5v0.6h-4.6v-0.6l2-0.4h0.4L210.7,105.5z M207.6,97.1c0-1,0-1.5-0.1-2.3l-1.4-0.2V94l2.4-1.1l0.3,0.2l0.1,1.5 h0.1v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8L207.6,97.1L207.6,97.1z M211.3,94c-0.7,0-1.6,0.3-2.9,1.6 l-0.2-0.3c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7 c1.4,0,2.5-1.1,2.5-3.4C213.6,95.3,212.6,94,211.3,94z"
id="path208" />
<path
d="M216.9,104.7l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8s-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1 c-0.2,0.7-0.4,0.9-0.7,0.9s-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6l0.3-0.7h4 v1.2H217L216.9,104.7L216.9,104.7z"
id="path210" />
</g>
</g>
<g>
<g>
<path d="M490.2,105.5v0.6h-4.6v-0.6l2-0.4h0.4L490.2,105.5z M487.1,97.1c0-1,0-1.5-0.1-2.3l-1.4-0.2V94l2.4-1.1l0.3,0.2l0.1,1.5
h0.1v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8H487c0-1.3,0.1-2.5,0.1-3.8V97.1z M490.8,94c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3
c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7
c1.4,0,2.5-1.1,2.5-3.4C493.1,95.3,492.1,94,490.8,94z"/>
<path d="M496.4,104.7l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8s-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1
c-0.2,0.7-0.4,0.9-0.7,0.9c-0.3,0-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6
l0.3-0.7h4v1.2h-4.9V104.7z"/>
<g
id="g222">
<g
id="g220">
<path
d="M490.2,105.5v0.6h-4.6v-0.6l2-0.4h0.4L490.2,105.5z M487.1,97.1c0-1,0-1.5-0.1-2.3l-1.4-0.2V94l2.4-1.1l0.3,0.2l0.1,1.5 h0.1v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8H487c0-1.3,0.1-2.5,0.1-3.8V97.1z M490.8,94c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3 c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7 c1.4,0,2.5-1.1,2.5-3.4C493.1,95.3,492.1,94,490.8,94z"
id="path216" />
<path
d="M496.4,104.7l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8s-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1 c-0.2,0.7-0.4,0.9-0.7,0.9c-0.3,0-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6 l0.3-0.7h4v1.2h-4.9V104.7z"
id="path218" />
</g>
</g>
</svg>
<path
d="m 45.711075,47.390632 c 0,2.6 -1.2,3.7 -2.5,3.7 -1.3,0 -2.5,-1.1 -2.5,-3.7 0,-2.6 1.2,-3.7 2.5,-3.7 1.3,-0.1 2.5,1 2.5,3.7 z m -2.5,3.1 c 0.6,0 1.2,-0.6 1.2,-3.1 0,-2.6 -0.6,-3.1 -1.2,-3.1 -0.6,0 -1.2,0.5 -1.2,3.1 0,2.5 0.6,3.1 1.2,3.1 z"
id="path178-3" /><path
d="m 71.585549,85.750185 c 0,2.6 -1.2,3.7 -2.5,3.7 -1.3,0 -2.5,-1.1 -2.5,-3.7 0,-2.6 1.2,-3.7 2.5,-3.7 1.3,-0.1 2.5,1 2.5,3.7 z m -2.5,3.1 c 0.6,0 1.2,-0.6 1.2,-3.1 0,-2.6 -0.6,-3.1 -1.2,-3.1 -0.6,0 -1.2,0.5 -1.2,3.1 0,2.5 0.6,3.1 1.2,3.1 z"
id="path178-6" /></svg>

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,244 +1,439 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="501.2px" height="161px" viewBox="0 0 501.2 161" style="enable-background:new 0 0 501.2 161;" xml:space="preserve">
<style type="text/css">
<svg
version="1.1"
id="Layer_1"
x="0px"
y="0px"
width="501.2px"
height="161px"
viewBox="0 0 501.2 161"
style="enable-background:new 0 0 501.2 161;"
xml:space="preserve"
sodipodi:docname="wachspress.svg"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs227" /><sodipodi:namedview
id="namedview225"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="5.407023"
inkscape:cx="135.0096"
inkscape:cy="111.15174"
inkscape:window-width="2560"
inkscape:window-height="1371"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<style
type="text/css"
id="style2">
.st0{fill:none;stroke:#000000;stroke-miterlimit:10;}
.st1{display:none;}
.st2{display:inline;}
</style>
<circle cx="3.7" cy="86.7" r="2.5"/>
<circle cx="96.2" cy="139.6" r="2.5"/>
<circle cx="209.3" cy="82.4" r="2.5"/>
<circle cx="90.2" cy="20.1" r="2.5"/>
<polyline class="st0" points="90.2,20.1 3.7,86.7 96.2,139.6 90.2,20.1 "/>
<polyline class="st0" points="96.2,139.6 209.3,82.3 90.2,20.1 "/>
<g class="st1">
<g class="st2">
<path class="st0" d="M67.6,118.4c5.9-8.7,15.6-14.6,26.8-15.2"/>
<g>
<polygon points="70.9,118.1 68.1,117.7 66.4,115.4 65.5,122 "/>
<circle
cx="3.7"
cy="86.7"
r="2.5"
id="circle4" />
<circle
cx="96.2"
cy="139.6"
r="2.5"
id="circle6" />
<circle
cx="209.3"
cy="82.4"
r="2.5"
id="circle8" />
<circle
cx="90.2"
cy="20.1"
r="2.5"
id="circle10" />
<polyline
class="st0"
points="90.2,20.1 3.7,86.7 96.2,139.6 90.2,20.1 "
id="polyline12" />
<polyline
class="st0"
points="96.2,139.6 209.3,82.3 90.2,20.1 "
id="polyline14" />
<g
class="st1"
id="g24">
<g
class="st2"
id="g22">
<path
class="st0"
d="M67.6,118.4c5.9-8.7,15.6-14.6,26.8-15.2"
id="path16" />
<g
id="g20">
<polygon
points="70.9,118.1 68.1,117.7 66.4,115.4 65.5,122 "
id="polygon18" />
</g>
</g>
</g>
<g class="st1">
<g class="st2">
<path class="st0" d="M98.5,107c10,2.3,18.8,9.2,23.3,19.2"/>
<g>
<polygon points="100,109.9 99.4,107.2 100.9,104.8 94.4,106.3 "/>
<g
class="st1"
id="g34">
<g
class="st2"
id="g32">
<path
class="st0"
d="M98.5,107c10,2.3,18.8,9.2,23.3,19.2"
id="path26" />
<g
id="g30">
<polygon
points="100,109.9 99.4,107.2 100.9,104.8 94.4,106.3 "
id="polygon28" />
</g>
</g>
</g>
<g class="st1">
<g class="st2">
<path class="st0" d="M172.4,95.8c-3.6-9.9-2.6-21.3,3.5-30.6"/>
<g>
<polygon points="174.1,93 172.1,95 169.3,95 174.1,99.6 "/>
<g
class="st1"
id="g44">
<g
class="st2"
id="g42">
<path
class="st0"
d="M172.4,95.8c-3.6-9.9-2.6-21.3,3.5-30.6"
id="path36" />
<g
id="g40">
<polygon
points="174.1,93 172.1,95 169.3,95 174.1,99.6 "
id="polygon38" />
</g>
</g>
</g>
<g class="st1">
<g class="st2">
<path class="st0" d="M29.7,72.1c5.6,8.9,7.1,20.2,3.1,30.7"/>
<g>
<polygon points="28.6,75.2 30.1,72.8 32.9,72.2 27.2,68.7 "/>
<g
class="st1"
id="g54">
<g
class="st2"
id="g52">
<path
class="st0"
d="M29.7,72.1c5.6,8.9,7.1,20.2,3.1,30.7"
id="path46" />
<g
id="g50">
<polygon
points="28.6,75.2 30.1,72.8 32.9,72.2 27.2,68.7 "
id="polygon48" />
</g>
</g>
</g>
<g class="st1">
<g class="st2">
<path class="st0" d="M116.3,38.5C111.8,48,103,55.3,92.1,57.6"/>
<g>
<polygon points="113.1,39.3 115.9,39.2 117.9,41.2 117.9,34.6 "/>
<g
class="st1"
id="g64">
<g
class="st2"
id="g62">
<path
class="st0"
d="M116.3,38.5C111.8,48,103,55.3,92.1,57.6"
id="path56" />
<g
id="g60">
<polygon
points="113.1,39.3 115.9,39.2 117.9,41.2 117.9,34.6 "
id="polygon58" />
</g>
</g>
</g>
<g class="st1">
<g class="st2">
<path class="st0" d="M87.5,54c-10,0.9-20.2-2.8-27.3-10.7"/>
<g>
<polygon points="85.2,51.6 86.6,54 85.9,56.8 91.6,53.3 "/>
<g
class="st1"
id="g74">
<g
class="st2"
id="g72">
<path
class="st0"
d="M87.5,54c-10,0.9-20.2-2.8-27.3-10.7"
id="path66" />
<g
id="g70">
<polygon
points="85.2,51.6 86.6,54 85.9,56.8 91.6,53.3 "
id="polygon68" />
</g>
</g>
</g>
<g>
<g class="st1">
<path class="st2" d="M84.2,120.7v1.5c-0.4-2-1.1-3.9-1.8-5.7c-0.5-1.5-0.8-2-1.4-2c-0.2,0-0.4,0-0.6,0.1l-0.2-0.5
c0.4-0.4,0.8-0.7,1.4-0.7c0.7,0,1.3,0.3,2,2.4c0.4,1.1,0.9,2.8,1.4,5.4l0.1,0.1l0.1,3.9l-1.4,0.3l-0.2-0.2L84.2,120.7z
M84.7,121.3c1.2-2.1,1.7-3.5,2.5-5.5l-0.2,1.4c-0.2-1.7-0.3-2.3-0.3-2.7c0-0.8,0.4-1.2,0.9-1.2c0.3,0,0.5,0.1,0.6,0.2
c0.1,0.2,0.1,0.4,0.1,0.8c0,1.9-1.8,5.3-3.3,7.8L84.7,121.3z"/>
<g
id="g80">
<g
class="st1"
id="g78">
<path
class="st2"
d="M84.2,120.7v1.5c-0.4-2-1.1-3.9-1.8-5.7c-0.5-1.5-0.8-2-1.4-2c-0.2,0-0.4,0-0.6,0.1l-0.2-0.5 c0.4-0.4,0.8-0.7,1.4-0.7c0.7,0,1.3,0.3,2,2.4c0.4,1.1,0.9,2.8,1.4,5.4l0.1,0.1l0.1,3.9l-1.4,0.3l-0.2-0.2L84.2,120.7z M84.7,121.3c1.2-2.1,1.7-3.5,2.5-5.5l-0.2,1.4c-0.2-1.7-0.3-2.3-0.3-2.7c0-0.8,0.4-1.2,0.9-1.2c0.3,0,0.5,0.1,0.6,0.2 c0.1,0.2,0.1,0.4,0.1,0.8c0,1.9-1.8,5.3-3.3,7.8L84.7,121.3z"
id="path76" />
</g>
</g>
<g>
<g class="st1">
<path class="st2" d="M100.3,127v-8.8c0-2.6,1.6-4.3,3.8-4.3c1.8,0,3.2,1.1,3.2,3c0,1.4-1,3.3-3.6,3.5l-0.1-0.2
c3-0.1,4.7,1,4.7,3.4c0,2.5-1.9,3.8-3.7,3.8c-1.3,0-2.6-0.6-3.3-2.2l0.2-0.2c0.8,0.7,1.7,1.2,2.8,1.2c1.7,0,2.6-1.1,2.6-2.7
c0-1.5-0.8-2.9-3.5-2.8l-0.2-1c1.7-0.3,2.7-1.3,2.7-2.9c0-1.4-0.7-2.3-1.9-2.3c-1.3,0-2.2,1.2-2.2,3.2v7.7l-0.1,0.3l0.3,4.5
l-1.4,0.3l-0.2-0.2L100.3,127z"/>
<g
id="g86">
<g
class="st1"
id="g84">
<path
class="st2"
d="M100.3,127v-8.8c0-2.6,1.6-4.3,3.8-4.3c1.8,0,3.2,1.1,3.2,3c0,1.4-1,3.3-3.6,3.5l-0.1-0.2 c3-0.1,4.7,1,4.7,3.4c0,2.5-1.9,3.8-3.7,3.8c-1.3,0-2.6-0.6-3.3-2.2l0.2-0.2c0.8,0.7,1.7,1.2,2.8,1.2c1.7,0,2.6-1.1,2.6-2.7 c0-1.5-0.8-2.9-3.5-2.8l-0.2-1c1.7-0.3,2.7-1.3,2.7-2.9c0-1.4-0.7-2.3-1.9-2.3c-1.3,0-2.2,1.2-2.2,3.2v7.7l-0.1,0.3l0.3,4.5 l-1.4,0.3l-0.2-0.2L100.3,127z"
id="path82" />
</g>
</g>
<g>
<g class="st1">
<path class="st2" d="M83.1,78.4v-0.8c0-1,0-1.5-0.1-2.3l-1.4-0.2v-0.6l2.4-1.1l0.3,0.2l0.2,2.3v2.6c0,1.1,0,3,0.1,3.8h-1.5
C83.1,81.4,83.1,79.6,83.1,78.4z M81.8,81.6l2-0.4h0.4l2,0.4v0.6h-4.3L81.8,81.6L81.8,81.6z M83.9,76h1l-0.5,0.1
c0.6-1.7,1.7-2.7,2.7-2.7c0.5,0,1.1,0.3,1.3,0.6c0,0.8-0.3,1.3-1,1.3c-0.4,0-0.8-0.2-1.1-0.4l-0.5-0.4l0.9-0.2
c-1.1,0.6-1.8,1.5-2.2,2.9h-0.7L83.9,76L83.9,76z"/>
<g
id="g92">
<g
class="st1"
id="g90">
<path
class="st2"
d="M83.1,78.4v-0.8c0-1,0-1.5-0.1-2.3l-1.4-0.2v-0.6l2.4-1.1l0.3,0.2l0.2,2.3v2.6c0,1.1,0,3,0.1,3.8h-1.5 C83.1,81.4,83.1,79.6,83.1,78.4z M81.8,81.6l2-0.4h0.4l2,0.4v0.6h-4.3L81.8,81.6L81.8,81.6z M83.9,76h1l-0.5,0.1 c0.6-1.7,1.7-2.7,2.7-2.7c0.5,0,1.1,0.3,1.3,0.6c0,0.8-0.3,1.3-1,1.3c-0.4,0-0.8-0.2-1.1-0.4l-0.5-0.4l0.9-0.2 c-1.1,0.6-1.8,1.5-2.2,2.9h-0.7L83.9,76L83.9,76z"
id="path88" />
</g>
</g>
<g>
<g class="st1">
<path class="st2" d="M21.1,89v-8.8c0-2.6,1.6-4.3,3.8-4.3c1.8,0,3.2,1.1,3.2,3c0,1.4-1,3.3-3.6,3.5l-0.1-0.2c3-0.1,4.7,1,4.7,3.4
c0,2.5-1.9,3.8-3.7,3.8c-1.3,0-2.6-0.6-3.3-2.2l0.2-0.2c0.8,0.7,1.7,1.2,2.8,1.2c1.7,0,2.6-1.1,2.6-2.7c0-1.5-0.8-2.9-3.5-2.8
l-0.2-1c1.7-0.3,2.7-1.3,2.7-2.9c0-1.4-0.7-2.3-1.9-2.3c-1.3,0-2.2,1.2-2.2,3.2v7.7l-0.1,0.3l0.3,4.5l-1.4,0.3l-0.2-0.2L21.1,89z"
/>
<g
id="g98">
<g
class="st1"
id="g96">
<path
class="st2"
d="M21.1,89v-8.8c0-2.6,1.6-4.3,3.8-4.3c1.8,0,3.2,1.1,3.2,3c0,1.4-1,3.3-3.6,3.5l-0.1-0.2c3-0.1,4.7,1,4.7,3.4 c0,2.5-1.9,3.8-3.7,3.8c-1.3,0-2.6-0.6-3.3-2.2l0.2-0.2c0.8,0.7,1.7,1.2,2.8,1.2c1.7,0,2.6-1.1,2.6-2.7c0-1.5-0.8-2.9-3.5-2.8 l-0.2-1c1.7-0.3,2.7-1.3,2.7-2.9c0-1.4-0.7-2.3-1.9-2.3c-1.3,0-2.2,1.2-2.2,3.2v7.7l-0.1,0.3l0.3,4.5l-1.4,0.3l-0.2-0.2L21.1,89z"
id="path94" />
</g>
</g>
<g>
<g class="st1">
<path class="st2" d="M181.9,83.9v1.5c-0.4-2-1.1-3.9-1.8-5.7c-0.5-1.5-0.8-2-1.4-2c-0.2,0-0.4,0-0.6,0.1l-0.2-0.5
c0.4-0.4,0.8-0.7,1.4-0.7c0.7,0,1.3,0.3,2,2.4c0.4,1.1,0.9,2.8,1.4,5.4l0.1,0.1l0.1,3.9l-1.4,0.3l-0.2-0.2L181.9,83.9z
M182.4,84.5c1.2-2.1,1.7-3.5,2.5-5.5l-0.2,1.4c-0.2-1.7-0.3-2.3-0.3-2.7c0-0.8,0.4-1.2,0.9-1.2c0.3,0,0.5,0.1,0.6,0.2
c0.1,0.2,0.1,0.4,0.1,0.8c0,1.9-1.8,5.3-3.3,7.8L182.4,84.5z"/>
<g
id="g104">
<g
class="st1"
id="g102">
<path
class="st2"
d="M181.9,83.9v1.5c-0.4-2-1.1-3.9-1.8-5.7c-0.5-1.5-0.8-2-1.4-2c-0.2,0-0.4,0-0.6,0.1l-0.2-0.5 c0.4-0.4,0.8-0.7,1.4-0.7c0.7,0,1.3,0.3,2,2.4c0.4,1.1,0.9,2.8,1.4,5.4l0.1,0.1l0.1,3.9l-1.4,0.3l-0.2-0.2L181.9,83.9z M182.4,84.5c1.2-2.1,1.7-3.5,2.5-5.5l-0.2,1.4c-0.2-1.7-0.3-2.3-0.3-2.7c0-0.8,0.4-1.2,0.9-1.2c0.3,0,0.5,0.1,0.6,0.2 c0.1,0.2,0.1,0.4,0.1,0.8c0,1.9-1.8,5.3-3.3,7.8L182.4,84.5z"
id="path100" />
</g>
</g>
<g>
<g class="st1">
<path class="st2" d="M32.9,41.8V41c0-1,0-1.5-0.1-2.3l-1.4-0.2v-0.6l2.4-1.1l0.3,0.2l0.2,2.3v2.6c0,1.1,0,3,0.1,3.8h-1.5
C32.8,44.7,32.9,42.9,32.9,41.8z M31.5,44.9l2-0.4h0.4l2,0.4v0.6h-4.3v-0.6H31.5z M33.6,39.3h1l-0.5,0.1c0.6-1.7,1.7-2.7,2.7-2.7
c0.5,0,1.1,0.3,1.3,0.6c0,0.8-0.3,1.3-1,1.3c-0.4,0-0.8-0.2-1.1-0.4l-0.5-0.4l0.9-0.2c-1.1,0.6-1.8,1.5-2.2,2.9h-0.6
C33.6,40.5,33.6,39.3,33.6,39.3z"/>
<path class="st2" d="M39.7,45.5c0-0.5,0-1.8,0-2.5v-0.6c0-0.7,0-1,0-1.5l-1-0.1v-0.4l1.6-0.7l0.2,0.1l0.1,1.4V43c0,0.7,0,2,0,2.5
C40.6,45.5,39.7,45.5,39.7,45.5z M41.5,45.1v0.4h-2.8v-0.4l1.3-0.2h0.2L41.5,45.1z M44.3,41.9V43c0,0.8,0,2,0,2.5h-1
c0-0.5,0-1.8,0-2.5v-1c0-1.1-0.3-1.5-1-1.5c-0.6,0-1.1,0.2-1.7,0.8h-0.3v-0.5h0.6L40.4,41c0.6-0.8,1.5-1.3,2.3-1.3
C43.8,39.7,44.3,40.3,44.3,41.9z M43.9,44.9l1.2,0.2v0.4h-2.8v-0.4l1.3-0.2H43.9z M47.9,41.8V43c0,0.8,0,2,0,2.5h-1
c0-0.5,0-1.8,0-2.5v-1c0-1.1-0.3-1.5-1-1.5c-0.5,0-1,0.2-1.7,0.8h-0.3v-0.4h0.6L43.9,41c0.6-0.9,1.5-1.3,2.4-1.3
C47.3,39.7,47.9,40.3,47.9,41.8z M47.5,44.9l1.2,0.2v0.4h-2.8v-0.4l1.3-0.2H47.5z"/>
<g
id="g112">
<g
class="st1"
id="g110">
<path
class="st2"
d="M32.9,41.8V41c0-1,0-1.5-0.1-2.3l-1.4-0.2v-0.6l2.4-1.1l0.3,0.2l0.2,2.3v2.6c0,1.1,0,3,0.1,3.8h-1.5 C32.8,44.7,32.9,42.9,32.9,41.8z M31.5,44.9l2-0.4h0.4l2,0.4v0.6h-4.3v-0.6H31.5z M33.6,39.3h1l-0.5,0.1c0.6-1.7,1.7-2.7,2.7-2.7 c0.5,0,1.1,0.3,1.3,0.6c0,0.8-0.3,1.3-1,1.3c-0.4,0-0.8-0.2-1.1-0.4l-0.5-0.4l0.9-0.2c-1.1,0.6-1.8,1.5-2.2,2.9h-0.6 C33.6,40.5,33.6,39.3,33.6,39.3z"
id="path106" />
<path
class="st2"
d="M39.7,45.5c0-0.5,0-1.8,0-2.5v-0.6c0-0.7,0-1,0-1.5l-1-0.1v-0.4l1.6-0.7l0.2,0.1l0.1,1.4V43c0,0.7,0,2,0,2.5 C40.6,45.5,39.7,45.5,39.7,45.5z M41.5,45.1v0.4h-2.8v-0.4l1.3-0.2h0.2L41.5,45.1z M44.3,41.9V43c0,0.8,0,2,0,2.5h-1 c0-0.5,0-1.8,0-2.5v-1c0-1.1-0.3-1.5-1-1.5c-0.6,0-1.1,0.2-1.7,0.8h-0.3v-0.5h0.6L40.4,41c0.6-0.8,1.5-1.3,2.3-1.3 C43.8,39.7,44.3,40.3,44.3,41.9z M43.9,44.9l1.2,0.2v0.4h-2.8v-0.4l1.3-0.2H43.9z M47.9,41.8V43c0,0.8,0,2,0,2.5h-1 c0-0.5,0-1.8,0-2.5v-1c0-1.1-0.3-1.5-1-1.5c-0.5,0-1,0.2-1.7,0.8h-0.3v-0.4h0.6L43.9,41c0.6-0.9,1.5-1.3,2.4-1.3 C47.3,39.7,47.9,40.3,47.9,41.8z M47.5,44.9l1.2,0.2v0.4h-2.8v-0.4l1.3-0.2H47.5z"
id="path108" />
</g>
</g>
<g>
<g class="st1">
<path class="st2" d="M151.9,41.8V41c0-1,0-1.5-0.1-2.3l-1.4-0.2v-0.6l2.4-1.1l0.3,0.2l0.2,2.2v2.6c0,1.1,0,3,0.1,3.8h-1.5
C151.8,44.7,151.9,42.9,151.9,41.8z M150.5,44.9l2-0.4h0.4l2,0.4v0.6h-4.3L150.5,44.9L150.5,44.9z M152.6,39.3h1l-0.5,0.1
c0.6-1.7,1.7-2.7,2.7-2.7c0.5,0,1.1,0.3,1.3,0.6c0,0.8-0.3,1.3-1,1.3c-0.4,0-0.8-0.2-1.1-0.4l-0.5-0.4l0.9-0.2
c-1.1,0.6-1.8,1.5-2.2,2.9h-0.6L152.6,39.3L152.6,39.3z"/>
<path class="st2" d="M160.8,48v0.4h-3.1V48l1.4-0.2h0.2L160.8,48z M158.7,42.5c0-0.7,0-1,0-1.5l-1-0.1v-0.4l1.6-0.7l0.2,0.1l0.1,1
l0,0v3.7v0.1v1.2c0,0.8,0,1.7,0,2.5h-1c0-0.8,0-1.7,0-2.5L158.7,42.5L158.7,42.5z M161.2,40.4c-0.4,0-1,0.2-1.9,1.1l-0.1-0.2
c0.7-1.1,1.5-1.5,2.3-1.5c1.3,0,2.3,1.1,2.3,3c0,1.8-1.1,3-2.5,3c-0.7,0-1.5-0.3-2.1-1.6l0.1-0.2c0.6,0.8,1.2,1.1,1.8,1.1
c0.9,0,1.7-0.7,1.7-2.3S162.1,40.4,161.2,40.4z"/>
<g
id="g120">
<g
class="st1"
id="g118">
<path
class="st2"
d="M151.9,41.8V41c0-1,0-1.5-0.1-2.3l-1.4-0.2v-0.6l2.4-1.1l0.3,0.2l0.2,2.2v2.6c0,1.1,0,3,0.1,3.8h-1.5 C151.8,44.7,151.9,42.9,151.9,41.8z M150.5,44.9l2-0.4h0.4l2,0.4v0.6h-4.3L150.5,44.9L150.5,44.9z M152.6,39.3h1l-0.5,0.1 c0.6-1.7,1.7-2.7,2.7-2.7c0.5,0,1.1,0.3,1.3,0.6c0,0.8-0.3,1.3-1,1.3c-0.4,0-0.8-0.2-1.1-0.4l-0.5-0.4l0.9-0.2 c-1.1,0.6-1.8,1.5-2.2,2.9h-0.6L152.6,39.3L152.6,39.3z"
id="path114" />
<path
class="st2"
d="M160.8,48v0.4h-3.1V48l1.4-0.2h0.2L160.8,48z M158.7,42.5c0-0.7,0-1,0-1.5l-1-0.1v-0.4l1.6-0.7l0.2,0.1l0.1,1 l0,0v3.7v0.1v1.2c0,0.8,0,1.7,0,2.5h-1c0-0.8,0-1.7,0-2.5L158.7,42.5L158.7,42.5z M161.2,40.4c-0.4,0-1,0.2-1.9,1.1l-0.1-0.2 c0.7-1.1,1.5-1.5,2.3-1.5c1.3,0,2.3,1.1,2.3,3c0,1.8-1.1,3-2.5,3c-0.7,0-1.5-0.3-2.1-1.6l0.1-0.2c0.6,0.8,1.2,1.1,1.8,1.1 c0.9,0,1.7-0.7,1.7-2.3S162.1,40.4,161.2,40.4z"
id="path116" />
</g>
</g>
<circle cx="283.4" cy="87.3" r="2.5"/>
<circle cx="375.9" cy="140.2" r="2.5"/>
<circle cx="488.9" cy="83" r="2.5"/>
<circle cx="369.8" cy="20.6" r="2.5"/>
<line class="st0" x1="369.8" y1="20.6" x2="283.4" y2="87.3"/>
<line class="st0" x1="488.9" y1="82.9" x2="369.8" y2="20.6"/>
<polygon class="st0" points="375.9,140.2 283.4,87.3 488.9,82.9 "/>
<g>
<g class="st1">
<path class="st2" d="M367.1,53.4v-0.7h2.5v1h-0.2L367.1,53.4z M369.6,63.8v1h-2.5V64l2.3-0.3L369.6,63.8L369.6,63.8z M368.8,58.3
c0-1.9,0-3.7-0.1-5.6h1.7c-0.1,1.8-0.1,3.7-0.1,5.6v0.6c0,2.1,0,4,0.1,5.9h-1.7c0.1-1.8,0.1-3.7,0.1-5.6V58.3z M369.6,58.1h1.8
c2.2,0,3.2-0.7,3.2-2.4s-1-2.2-2.8-2.2h-2.2v-0.8h2.7c2.5,0,3.9,1.2,3.9,3c0,1.4-1,2.6-3.4,2.9v-0.2c2.8,0.2,4,1.5,4,3
c0,1.6-1.4,3.3-4.8,3.3h-2.4v-0.8h2c2.3,0,3.5-0.9,3.5-2.5c0-1.7-1.1-2.5-3.6-2.5h-1.9L369.6,58.1L369.6,58.1z"/>
<circle
cx="283.4"
cy="87.3"
r="2.5"
id="circle122" />
<circle
cx="375.9"
cy="140.2"
r="2.5"
id="circle124" />
<circle
cx="488.9"
cy="83"
r="2.5"
id="circle126" />
<circle
cx="369.8"
cy="20.6"
r="2.5"
id="circle128" />
<line
class="st0"
x1="369.8"
y1="20.6"
x2="283.4"
y2="87.3"
id="line130" />
<line
class="st0"
x1="488.9"
y1="82.9"
x2="369.8"
y2="20.6"
id="line132" />
<polygon
class="st0"
points="375.9,140.2 283.4,87.3 488.9,82.9 "
id="polygon134" />
<g
id="g140">
<g
class="st1"
id="g138">
<path
class="st2"
d="M367.1,53.4v-0.7h2.5v1h-0.2L367.1,53.4z M369.6,63.8v1h-2.5V64l2.3-0.3L369.6,63.8L369.6,63.8z M368.8,58.3 c0-1.9,0-3.7-0.1-5.6h1.7c-0.1,1.8-0.1,3.7-0.1,5.6v0.6c0,2.1,0,4,0.1,5.9h-1.7c0.1-1.8,0.1-3.7,0.1-5.6V58.3z M369.6,58.1h1.8 c2.2,0,3.2-0.7,3.2-2.4s-1-2.2-2.8-2.2h-2.2v-0.8h2.7c2.5,0,3.9,1.2,3.9,3c0,1.4-1,2.6-3.4,2.9v-0.2c2.8,0.2,4,1.5,4,3 c0,1.6-1.4,3.3-4.8,3.3h-2.4v-0.8h2c2.3,0,3.5-0.9,3.5-2.5c0-1.7-1.1-2.5-3.6-2.5h-1.9L369.6,58.1L369.6,58.1z"
id="path136" />
</g>
</g>
<g>
<g>
<path d="M380,112.1v1l-1.1,0.3l0.5-2.7h1.1l-0.1,2.6c-1.1,0.6-2.4,0.9-3.6,0.9c-3.6,0-6.1-2.6-6.1-6.3c0-3.6,2.6-6.3,6.1-6.3
c1,0,2.3,0.2,3.5,0.9l0.1,2.6h-1.1l-0.5-2.7l1.1,0.3v1.1c-1-1-2-1.3-2.9-1.3c-2.4,0-4.4,1.9-4.4,5.4s1.8,5.4,4.3,5.4
C377.8,113.2,379,112.9,380,112.1z"/>
<g
id="g146">
<g
id="g144">
<path
d="M380,112.1v1l-1.1,0.3l0.5-2.7h1.1l-0.1,2.6c-1.1,0.6-2.4,0.9-3.6,0.9c-3.6,0-6.1-2.6-6.1-6.3c0-3.6,2.6-6.3,6.1-6.3 c1,0,2.3,0.2,3.5,0.9l0.1,2.6h-1.1l-0.5-2.7l1.1,0.3v1.1c-1-1-2-1.3-2.9-1.3c-2.4,0-4.4,1.9-4.4,5.4s1.8,5.4,4.3,5.4 C377.8,113.2,379,112.9,380,112.1z"
id="path142" />
</g>
</g>
<g>
<g>
<path d="M86.5,8c0.9,0,1.5-0.4,2.4-1.5L89.1,7c-0.8,1.4-1.7,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6
c1,0,2,0.4,2.9,1.8L89,2.3c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.6C84,6.9,85,8,86.5,8z M89.6,12.1l2,0.4v0.6H87v-0.6
l2.2-0.4H89.6z M90.1,13.1h-1.5c0-1.3,0.1-2.6,0.1-3.8V7.5l-0.1-0.3V1.3L89.8,0l0.3,0.2L90,2.6v6.7C90.1,10.5,90.1,11.8,90.1,13.1
z"/>
<g
id="g152">
<g
id="g150">
<path
d="M86.5,8c0.9,0,1.5-0.4,2.4-1.5L89.1,7c-0.8,1.4-1.7,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6 c1,0,2,0.4,2.9,1.8L89,2.3c-1-1-1.8-1.3-2.4-1.3c-1.4,0-2.5,1.1-2.5,3.6C84,6.9,85,8,86.5,8z M89.6,12.1l2,0.4v0.6H87v-0.6 l2.2-0.4H89.6z M90.1,13.1h-1.5c0-1.3,0.1-2.6,0.1-3.8V7.5l-0.1-0.3V1.3L89.8,0l0.3,0.2L90,2.6v6.7C90.1,10.5,90.1,11.8,90.1,13.1 z"
id="path148" />
</g>
</g>
<g>
<g>
<path d="M366.2,8c0.9,0,1.5-0.4,2.4-1.5l0.2,0.5c-0.8,1.4-1.7,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6
c1,0,2,0.4,2.9,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.3,0-2.5,1.1-2.5,3.6C363.7,6.9,364.7,8,366.2,8z M369.3,12.1l2,0.4v0.6h-4.6
v-0.6l2.2-0.4H369.3z M369.8,13.1h-1.5c0-1.3,0.1-2.6,0.1-3.8V7.5l-0.1-0.3V1.3l1.2-1.3l0.3,0.2l-0.1,2.4v6.7
C369.8,10.5,369.8,11.8,369.8,13.1z"/>
<g
id="g158">
<g
id="g156">
<path
d="M366.2,8c0.9,0,1.5-0.4,2.4-1.5l0.2,0.5c-0.8,1.4-1.7,2-2.9,2c-2.2,0-3.7-1.8-3.7-4.4c0-2.7,1.6-4.6,3.8-4.6 c1,0,2,0.4,2.9,1.8l-0.2,0.5c-1-1-1.8-1.3-2.4-1.3c-1.3,0-2.5,1.1-2.5,3.6C363.7,6.9,364.7,8,366.2,8z M369.3,12.1l2,0.4v0.6h-4.6 v-0.6l2.2-0.4H369.3z M369.8,13.1h-1.5c0-1.3,0.1-2.6,0.1-3.8V7.5l-0.1-0.3V1.3l1.2-1.3l0.3,0.2l-0.1,2.4v6.7 C369.8,10.5,369.8,11.8,369.8,13.1z"
id="path154" />
</g>
</g>
<g>
<g>
<path d="M55.3,84.2l1.8-0.3h0.4l2,0.3v0.7h-4.1L55.3,84.2L55.3,84.2z M60.7,72.8h1.1L65.9,85h-1.7l-3.6-10.8H61L57.3,85h-0.8
L60.7,72.8z M58.6,80.2h4.9l0.3,0.8h-5.4L58.6,80.2z M62.1,84.2l2.3-0.3h0.4l2.2,0.3v0.7h-4.9C62.1,84.9,62.1,84.2,62.1,84.2z"/>
<path d="M72.8,84.4v0.5h-4.5v-0.5l1.7-0.3h1.1L72.8,84.4z M71.1,77.7l0.2,0.1v7.1h-1.2v-6.1l-1.4,0.1v-0.6L71.1,77.7z"/>
<g
id="g166">
<g
id="g164">
<path
d="M55.3,84.2l1.8-0.3h0.4l2,0.3v0.7h-4.1L55.3,84.2L55.3,84.2z M60.7,72.8h1.1L65.9,85h-1.7l-3.6-10.8H61L57.3,85h-0.8 L60.7,72.8z M58.6,80.2h4.9l0.3,0.8h-5.4L58.6,80.2z M62.1,84.2l2.3-0.3h0.4l2.2,0.3v0.7h-4.9C62.1,84.9,62.1,84.2,62.1,84.2z"
id="path160" />
</g>
</g>
<g>
<g>
<path d="M123.1,84.8l1.8-0.3h0.4l2,0.3v0.7h-4.1v-0.7H123.1z M128.5,73.4h1.1l4.1,12.2H132l-3.6-10.9h0.4l-3.7,10.8h-0.8
L128.5,73.4z M126.3,80.8h4.9l0.3,0.8h-5.4L126.3,80.8z M129.9,84.8l2.3-0.3h0.4l2.2,0.3v0.7h-4.9V84.8z"/>
<path d="M135.9,84.6l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8s-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1
c-0.2,0.7-0.4,0.9-0.7,0.9s-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6l0.3-0.7h4
v1.2H136L135.9,84.6L135.9,84.6z"/>
<g
id="g174">
<g
id="g172">
<path
d="M123.1,84.8l1.8-0.3h0.4l2,0.3v0.7h-4.1v-0.7H123.1z M128.5,73.4h1.1l4.1,12.2H132l-3.6-10.9h0.4l-3.7,10.8h-0.8 L128.5,73.4z M126.3,80.8h4.9l0.3,0.8h-5.4L126.3,80.8z M129.9,84.8l2.3-0.3h0.4l2.2,0.3v0.7h-4.9V84.8z"
id="path168" />
<path
d="M135.9,84.6l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8s-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1 c-0.2,0.7-0.4,0.9-0.7,0.9s-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6l0.3-0.7h4 v1.2H136L135.9,84.6L135.9,84.6z"
id="path170" />
</g>
</g>
<g>
<g>
<path d="M4.6,108.9v0.6H0v-0.6l2-0.4h0.4L4.6,108.9z M1.5,100.6c0-1,0-1.5-0.1-2.3L0,98.1v-0.6l2.4-1.1l0.3,0.2l0.1,1.5h0.1v5.6
l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8H1.5c0-1.3,0.1-2.5,0.1-3.8L1.5,100.6L1.5,100.6z M5.2,97.4c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3
c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7
c1.4,0,2.5-1.1,2.5-3.4C7.5,98.7,6.5,97.4,5.2,97.4z"/>
<path d="M15.7,105.5c0,2.6-1.2,3.7-2.5,3.7c-1.3,0-2.5-1.1-2.5-3.7s1.2-3.7,2.5-3.7C14.5,101.7,15.7,102.8,15.7,105.5z
M13.2,108.6c0.6,0,1.2-0.6,1.2-3.1c0-2.6-0.6-3.1-1.2-3.1c-0.6,0-1.2,0.5-1.2,3.1C12,108,12.6,108.6,13.2,108.6z"/>
<g
id="g182">
<g
id="g180">
<path
d="M4.6,108.9v0.6H0v-0.6l2-0.4h0.4L4.6,108.9z M1.5,100.6c0-1,0-1.5-0.1-2.3L0,98.1v-0.6l2.4-1.1l0.3,0.2l0.1,1.5h0.1v5.6 l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8H1.5c0-1.3,0.1-2.5,0.1-3.8L1.5,100.6L1.5,100.6z M5.2,97.4c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3 c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7 c1.4,0,2.5-1.1,2.5-3.4C7.5,98.7,6.5,97.4,5.2,97.4z"
id="path176" />
<path
d="M15.7,105.5c0,2.6-1.2,3.7-2.5,3.7c-1.3,0-2.5-1.1-2.5-3.7s1.2-3.7,2.5-3.7C14.5,101.7,15.7,102.8,15.7,105.5z M13.2,108.6c0.6,0,1.2-0.6,1.2-3.1c0-2.6-0.6-3.1-1.2-3.1c-0.6,0-1.2,0.5-1.2,3.1C12,108,12.6,108.6,13.2,108.6z"
id="path178" />
</g>
</g>
<g>
<g>
<path d="M96.9,160.4v0.6h-4.6v-0.6l2-0.4h0.4L96.9,160.4z M93.8,152.1c0-1,0-1.5-0.1-2.3l-1.4-0.2V149l2.4-1.1l0.3,0.2l0.1,1.5
h0.1v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8V152.1z M97.5,148.9c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3
c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7
c1.4,0,2.5-1.1,2.5-3.4C99.8,150.2,98.8,148.9,97.5,148.9z"/>
<path d="M107.8,160v0.5h-4.5V160l1.7-0.3h1.1L107.8,160z M106.1,153.3l0.2,0.1v7.1h-1.2v-6.1l-1.4,0.1v-0.6L106.1,153.3z"/>
<g
id="g190">
<g
id="g188">
<path
d="M96.9,160.4v0.6h-4.6v-0.6l2-0.4h0.4L96.9,160.4z M93.8,152.1c0-1,0-1.5-0.1-2.3l-1.4-0.2V149l2.4-1.1l0.3,0.2l0.1,1.5 h0.1v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8V152.1z M97.5,148.9c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3 c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7 c1.4,0,2.5-1.1,2.5-3.4C99.8,150.2,98.8,148.9,97.5,148.9z"
id="path184" />
<path
d="M107.8,160v0.5h-4.5V160l1.7-0.3h1.1L107.8,160z M106.1,153.3l0.2,0.1v7.1h-1.2v-6.1l-1.4,0.1v-0.6L106.1,153.3z"
id="path186" />
</g>
</g>
<g>
<g>
<path d="M211,104.3v0.6h-4.6v-0.6l2-0.4h0.4L211,104.3z M207.9,96c0-1,0-1.5-0.1-2.3l-1.4-0.2v-0.6l2.4-1.1l0.3,0.2l0.1,1.5h0.1
v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8L207.9,96L207.9,96z M211.6,92.8c-0.7,0-1.6,0.3-2.9,1.6
l-0.2-0.3c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7
c1.4,0,2.5-1.1,2.5-3.4C213.9,94.1,212.9,92.8,211.6,92.8z"/>
<path d="M217.2,103.5l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8s-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1
c-0.2,0.7-0.4,0.9-0.7,0.9s-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6l0.3-0.7h4
v1.2h-4.8L217.2,103.5L217.2,103.5z"/>
<g
id="g198">
<g
id="g196">
<path
d="M211,104.3v0.6h-4.6v-0.6l2-0.4h0.4L211,104.3z M207.9,96c0-1,0-1.5-0.1-2.3l-1.4-0.2v-0.6l2.4-1.1l0.3,0.2l0.1,1.5h0.1 v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8L207.9,96L207.9,96z M211.6,92.8c-0.7,0-1.6,0.3-2.9,1.6 l-0.2-0.3c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7 c1.4,0,2.5-1.1,2.5-3.4C213.9,94.1,212.9,92.8,211.6,92.8z"
id="path192" />
<path
d="M217.2,103.5l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8s-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1 c-0.2,0.7-0.4,0.9-0.7,0.9s-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6l0.3-0.7h4 v1.2h-4.8L217.2,103.5L217.2,103.5z"
id="path194" />
</g>
</g>
<g>
<g>
<path d="M284.7,108.9v0.6h-4.6v-0.6l2-0.4h0.4L284.7,108.9z M281.6,100.6c0-1,0-1.5-0.1-2.3l-1.4-0.2v-0.6l2.4-1.1l0.3,0.2
l0.1,1.5h0.1v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8V100.6z M285.3,97.4c-0.7,0-1.6,0.3-2.9,1.6
l-0.2-0.3c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7
c1.4,0,2.5-1.1,2.5-3.4C287.6,98.7,286.6,97.4,285.3,97.4z"/>
<path d="M295.8,105.5c0,2.6-1.2,3.7-2.5,3.7s-2.5-1.1-2.5-3.7s1.2-3.7,2.5-3.7C294.5,101.7,295.8,102.8,295.8,105.5z M293.3,108.6
c0.6,0,1.2-0.6,1.2-3.1c0-2.6-0.6-3.1-1.2-3.1s-1.2,0.5-1.2,3.1C292,108,292.6,108.6,293.3,108.6z"/>
<g
id="g206">
<g
id="g204">
<path
d="M284.7,108.9v0.6h-4.6v-0.6l2-0.4h0.4L284.7,108.9z M281.6,100.6c0-1,0-1.5-0.1-2.3l-1.4-0.2v-0.6l2.4-1.1l0.3,0.2 l0.1,1.5h0.1v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8V100.6z M285.3,97.4c-0.7,0-1.6,0.3-2.9,1.6 l-0.2-0.3c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7 c1.4,0,2.5-1.1,2.5-3.4C287.6,98.7,286.6,97.4,285.3,97.4z"
id="path200" />
<path
d="M295.8,105.5c0,2.6-1.2,3.7-2.5,3.7s-2.5-1.1-2.5-3.7s1.2-3.7,2.5-3.7C294.5,101.7,295.8,102.8,295.8,105.5z M293.3,108.6 c0.6,0,1.2-0.6,1.2-3.1c0-2.6-0.6-3.1-1.2-3.1s-1.2,0.5-1.2,3.1C292,108,292.6,108.6,293.3,108.6z"
id="path202" />
</g>
</g>
<g>
<g>
<path d="M377.2,160.4v0.6h-4.6v-0.6l2-0.4h0.4L377.2,160.4z M374.1,152.1c0-1,0-1.5-0.1-2.3l-1.4-0.2V149l2.4-1.1l0.3,0.2l0.1,1.5
h0.1v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8H374c0-1.3,0.1-2.5,0.1-3.8V152.1z M377.8,148.9c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3
c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7
c1.4,0,2.5-1.1,2.5-3.4C380.1,150.2,379.1,148.9,377.8,148.9z"/>
<path d="M388.1,160v0.5h-4.5V160l1.7-0.3h1.1L388.1,160z M386.4,153.3l0.2,0.1v7.1h-1.2v-6.1l-1.4,0.1v-0.6L386.4,153.3z"/>
<g
id="g214">
<g
id="g212">
<path
d="M377.2,160.4v0.6h-4.6v-0.6l2-0.4h0.4L377.2,160.4z M374.1,152.1c0-1,0-1.5-0.1-2.3l-1.4-0.2V149l2.4-1.1l0.3,0.2l0.1,1.5 h0.1v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8H374c0-1.3,0.1-2.5,0.1-3.8V152.1z M377.8,148.9c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3 c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7 c1.4,0,2.5-1.1,2.5-3.4C380.1,150.2,379.1,148.9,377.8,148.9z"
id="path208" />
<path
d="M388.1,160v0.5h-4.5V160l1.7-0.3h1.1L388.1,160z M386.4,153.3l0.2,0.1v7.1h-1.2v-6.1l-1.4,0.1v-0.6L386.4,153.3z"
id="path210" />
</g>
</g>
<g>
<g>
<path d="M490.1,104.3v0.6h-4.6v-0.6l2-0.4h0.4L490.1,104.3z M487,96c0-1,0-1.5-0.1-2.3l-1.4-0.2v-0.6l2.4-1.1l0.3,0.2l0.1,1.5h0.1
v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8V96z M490.7,92.8c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3
c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7
c1.4,0,2.5-1.1,2.5-3.4C493,94.1,492,92.8,490.7,92.8z"/>
<path d="M496.3,103.5l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8s-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1
c-0.2,0.7-0.4,0.9-0.7,0.9c-0.3,0-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6
l0.3-0.7h4v1.2h-4.8v-0.9H496.3z"/>
<g
id="g222">
<g
id="g220">
<path
d="M490.1,104.3v0.6h-4.6v-0.6l2-0.4h0.4L490.1,104.3z M487,96c0-1,0-1.5-0.1-2.3l-1.4-0.2v-0.6l2.4-1.1l0.3,0.2l0.1,1.5h0.1 v5.6l-0.1,0.1v1.8c0,1.3,0,2.6,0.1,3.8h-1.5c0-1.3,0.1-2.5,0.1-3.8V96z M490.7,92.8c-0.7,0-1.6,0.3-2.9,1.6l-0.2-0.3 c1-1.6,2.2-2.3,3.4-2.3c1.9,0,3.4,1.7,3.4,4.5s-1.6,4.5-3.7,4.5c-1.1,0-2.2-0.5-3.2-2.4l0.2-0.3c0.9,1.2,1.8,1.7,2.7,1.7 c1.4,0,2.5-1.1,2.5-3.4C493,94.1,492,92.8,490.7,92.8z"
id="path216" />
<path
d="M496.3,103.5l1.7-1.6c1.1-1,1.6-1.9,1.6-2.8s-0.5-1.4-1.4-1.4c-0.3,0-0.7,0.1-1.1,0.2l0.7-0.5l-0.3,1 c-0.2,0.7-0.4,0.9-0.7,0.9c-0.3,0-0.5-0.2-0.6-0.4c0.1-1.2,1.2-1.8,2.5-1.8c1.6,0,2.2,0.9,2.2,1.9s-0.7,1.8-2.3,3.3l-1.7,1.6 l0.3-0.7h4v1.2h-4.8v-0.9H496.3z"
id="path218" />
</g>
</g>
</svg>
<path
d="m 73.601453,81.294142 c 0,2.6 -1.2,3.7 -2.5,3.7 -1.3,0 -2.5,-1.1 -2.5,-3.7 0,-2.6 1.2,-3.7 2.5,-3.7 1.3,-0.1 2.5,1 2.5,3.7 z m -2.5,3.1 c 0.6,0 1.2,-0.6 1.2,-3.1 0,-2.6 -0.6,-3.1 -1.2,-3.1 -0.6,0 -1.2,0.5 -1.2,3.1 0,2.5 0.6,3.1 1.2,3.1 z"
id="path178-6" /></svg>

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -1,5 +1,4 @@
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Weights/authalic_weights.h>
#include <CGAL/Weights/three_point_family_weights.h>
// Typedefs.

View File

@ -20,18 +20,18 @@ using HD = boost::graph_traits<Mesh>::halfedge_descriptor;
template<typename PointMap>
FT get_w_ij(const Mesh& mesh, const HD he, const PointMap pmap) {
const auto v0 = target(he, mesh);
const auto v1 = source(he, mesh);
const VD v0 = target(he, mesh);
const VD v1 = source(he, mesh);
const auto& q = get(pmap, v0); // query
const auto& p1 = get(pmap, v1); // neighbor j
if (is_border_edge(he, mesh)) {
const auto he_cw = opposite(next(he, mesh), mesh);
auto v2 = source(he_cw, mesh);
const HD he_cw = opposite(next(he, mesh), mesh);
VD v2 = source(he_cw, mesh);
if (is_border_edge(he_cw, mesh)) {
const auto he_ccw = prev(opposite(he, mesh), mesh);
const HD he_ccw = prev(opposite(he, mesh), mesh);
v2 = source(he_ccw, mesh);
const auto& p2 = get(pmap, v2); // neighbor jp
@ -42,10 +42,10 @@ FT get_w_ij(const Mesh& mesh, const HD he, const PointMap pmap) {
}
}
const auto he_cw = opposite(next(he, mesh), mesh);
const auto v2 = source(he_cw, mesh);
const auto he_ccw = prev(opposite(he, mesh), mesh);
const auto v3 = source(he_ccw, mesh);
const HD he_cw = opposite(next(he, mesh), mesh);
const VD v2 = source(he_cw, mesh);
const HD he_ccw = prev(opposite(he, mesh), mesh);
const VD v3 = source(he_ccw, mesh);
const auto& p0 = get(pmap, v2); // neighbor jm
const auto& p2 = get(pmap, v3); // neighbor jp
@ -56,14 +56,14 @@ template<typename PointMap>
FT get_w_i(const Mesh& mesh, const VD v_i, const PointMap pmap) {
FT A_i = 0.0;
const auto v0 = v_i;
const auto init = halfedge(v_i, mesh);
for (const auto& he : halfedges_around_target(init, mesh)) {
const VD v0 = v_i;
const HD init = halfedge(v_i, mesh);
for (const HD he : halfedges_around_target(init, mesh)) {
assert(v0 == target(he, mesh));
if (is_border(he, mesh)) { continue; }
const auto v1 = source(he, mesh);
const auto v2 = target(next(he, mesh), mesh);
const VD v1 = source(he, mesh);
const VD v2 = target(next(he, mesh), mesh);
const auto& p = get(pmap, v0);
const auto& q = get(pmap, v1);
@ -81,14 +81,14 @@ void set_laplacian_matrix(const Mesh& mesh, Matrix& L) {
// Precompute Voronoi areas.
std::map<std::size_t, FT> w_i;
for (const auto& v_i : vertices(mesh)) {
for (const VD v_i : vertices(mesh)) {
w_i[get(imap, v_i)] = get_w_i(mesh, v_i, pmap);
}
// Fill the matrix.
for (const auto& he : halfedges(mesh)) {
const auto vi = source(he, mesh);
const auto vj = target(he, mesh);
for (const HD he : halfedges(mesh)) {
const VD vi = source(he, mesh);
const VD vj = target(he, mesh);
const std::size_t i = get(imap, vi);
const std::size_t j = get(imap, vj);
@ -106,11 +106,11 @@ int main() {
// Create mesh.
Mesh mesh;
const auto v0 = mesh.add_vertex(Point_3(0, 2, 0));
const auto v1 = mesh.add_vertex(Point_3(2, 2, 0));
const auto v2 = mesh.add_vertex(Point_3(0, 0, 0));
const auto v3 = mesh.add_vertex(Point_3(2, 0, 0));
const auto v4 = mesh.add_vertex(Point_3(1, 1, 1));
const VD v0 = mesh.add_vertex(Point_3(0, 2, 0));
const VD v1 = mesh.add_vertex(Point_3(2, 2, 0));
const VD v2 = mesh.add_vertex(Point_3(0, 0, 0));
const VD v3 = mesh.add_vertex(Point_3(2, 0, 0));
const VD v4 = mesh.add_vertex(Point_3(1, 1, 1));
mesh.add_face(v0, v2, v4);
mesh.add_face(v2, v3, v4);
mesh.add_face(v3, v1, v4);

View File

@ -14,186 +14,147 @@
#ifndef CGAL_AUTHALIC_WEIGHTS_H
#define CGAL_AUTHALIC_WEIGHTS_H
// Internal includes.
#include <CGAL/Weights/internal/utils.h>
#include <CGAL/Weights/utils.h>
#include <CGAL/Point_2.h>
#include <CGAL/Point_3.h>
namespace CGAL {
namespace Weights {
/// \cond SKIP_IN_MANUAL
namespace authalic_ns {
/// \cond SKIP_IN_MANUAL
template<typename FT>
FT half_weight(const FT cot, const FT r2) {
namespace authalic_ns {
FT w = FT(0);
CGAL_precondition(r2 != FT(0));
if (r2 != FT(0)) {
const FT inv = FT(2) / r2;
w = cot * inv;
}
return w;
}
template<typename FT>
FT half_weight(const FT cot, const FT r2)
{
FT w = FT(0);
CGAL_precondition(!is_zero(r2));
if (!is_zero(r2))
w = FT(2) * cot / r2;
template<typename FT>
FT weight(const FT cot_gamma, const FT cot_beta, const FT r2) {
return w;
}
FT w = FT(0);
CGAL_precondition(r2 != FT(0));
if (r2 != FT(0)) {
const FT inv = FT(2) / r2;
w = (cot_gamma + cot_beta) * inv;
}
return w;
}
}
/// \endcond
template<typename FT>
FT weight(const FT cot_gamma, const FT cot_beta, const FT r2)
{
FT w = FT(0);
CGAL_precondition(!is_zero(r2));
if (!is_zero(r2))
w = FT(2) * (cot_gamma + cot_beta) / r2;
/*!
\ingroup PkgWeightsRefAuthalicWeights
return w;
}
\brief computes the half value of the authalic weight.
} // namespace authalic_ns
This function constructs the half of the authalic weight using the precomputed
cotangent and squared distance values. The returned value is
\f$\frac{2\textbf{cot}}{\textbf{d2}}\f$.
/// \endcond
\tparam FT
a model of `FieldNumberType`
/*!
\ingroup PkgWeightsRefAuthalicWeights
\param cot
the cotangent value
\brief computes the half value of the authalic weight.
\param d2
the squared distance value
This function computes the half of the authalic weight using the precomputed
cotangent and squared distance values. The returned value is
\f$\frac{2\textbf{cot}}{\textbf{sq_d}}\f$.
\pre d2 != 0
\tparam FT a model of `FieldNumberType`
\sa `authalic_weight()`
*/
template<typename FT>
FT half_authalic_weight(const FT cot, const FT d2) {
return authalic_ns::half_weight(cot, d2);
}
\param cot the cotangent value
\param sq_d the squared distance value
#if defined(DOXYGEN_RUNNING)
\pre sq_d != 0
/*!
\ingroup PkgWeightsRefAuthalicWeights
\sa `authalic_weight()`
*/
template<typename FT>
FT half_authalic_weight(const FT cot, const FT sq_d)
{
return authalic_ns::half_weight(cot, sq_d);
}
\brief computes the authalic weight in 2D at `q` using the points `p0`, `p1`,
and `p2`, given a traits class `traits` with geometric objects, predicates, and constructions.
*/
template<typename GeomTraits>
typename GeomTraits::FT authalic_weight(
const typename GeomTraits::Point_2& p0,
const typename GeomTraits::Point_2& p1,
const typename GeomTraits::Point_2& p2,
const typename GeomTraits::Point_2& q,
const GeomTraits& traits) { }
// 2D ==============================================================================================
/*!
\ingroup PkgWeightsRefAuthalicWeights
/*!
\ingroup PkgWeightsRefAuthalicWeights
\brief computes the authalic weight in 2D at `q` using the points `p0`, `p1`, and `p2`.
\tparam GeomTraits a model of `AnalyticWeightTraits_2`
*/
template<typename GeomTraits>
typename GeomTraits::FT authalic_weight(const typename GeomTraits::Point_2& p0,
const typename GeomTraits::Point_2& p1,
const typename GeomTraits::Point_2& p2,
const typename GeomTraits::Point_2& q,
const GeomTraits& traits)
{
using FT = typename GeomTraits::FT;
\brief computes the authalic weight in 3D at `q` using the points `p0`, `p1`,
and `p2`, given a traits class `traits` with geometric objects, predicates, and constructions.
*/
template<typename GeomTraits>
typename GeomTraits::FT authalic_weight(
const typename GeomTraits::Point_3& p0,
const typename GeomTraits::Point_3& p1,
const typename GeomTraits::Point_3& p2,
const typename GeomTraits::Point_3& q,
const GeomTraits& traits) { }
const FT cot_gamma = cotangent_2(p0, p1, q, traits);
const FT cot_beta = cotangent_2(q, p1, p2, traits);
/*!
\ingroup PkgWeightsRefAuthalicWeights
auto squared_distance_2 = traits.compute_squared_distance_2_object();
const FT sq_d = squared_distance_2(q, p1);
\brief computes the authalic weight in 2D at `q` using the points `p0`, `p1`,
and `p2` which are parameterized by a `Kernel` K.
*/
template<typename K>
typename K::FT authalic_weight(
const CGAL::Point_2<K>& p0,
const CGAL::Point_2<K>& p1,
const CGAL::Point_2<K>& p2,
const CGAL::Point_2<K>& q) { }
return authalic_ns::weight(cot_gamma, cot_beta, sq_d);
}
/*!
\ingroup PkgWeightsRefAuthalicWeights
/*!
\ingroup PkgWeightsRefAuthalicWeights
\brief computes the authalic weight in 2D at `q` using the points `p0`, `p1`, and `p2`.
\tparam Kernel a model of `Kernel`
*/
template<typename Kernel>
typename Kernel::FT authalic_weight(const CGAL::Point_2<Kernel>& p0,
const CGAL::Point_2<Kernel>& p1,
const CGAL::Point_2<Kernel>& p2,
const CGAL::Point_2<Kernel>& q)
{
const Kernel traits;
return authalic_weight(p0, p1, p2, q, traits);
}
\brief computes the authalic weight in 3D at `q` using the points `p0`, `p1`,
and `p2` which are parameterized by a `Kernel` K.
*/
template<typename K>
typename K::FT authalic_weight(
const CGAL::Point_3<K>& p0,
const CGAL::Point_3<K>& p1,
const CGAL::Point_3<K>& p2,
const CGAL::Point_3<K>& q) { }
// 3D ==============================================================================================
#endif // DOXYGEN_RUNNING
/*!
\ingroup PkgWeightsRefAuthalicWeights
\brief computes the authalic weight in 3D at `q` using the points `p0`, `p1`, and `p2`.
\tparam GeomTraits a model of `AnalyticWeightTraits_3`
*/
template<typename GeomTraits>
typename GeomTraits::FT authalic_weight(const typename GeomTraits::Point_3& p0,
const typename GeomTraits::Point_3& p1,
const typename GeomTraits::Point_3& p2,
const typename GeomTraits::Point_3& q,
const GeomTraits& traits)
{
using FT = typename GeomTraits::FT;
/// \cond SKIP_IN_MANUAL
// Overloads!
template<typename GeomTraits>
typename GeomTraits::FT authalic_weight(
const typename GeomTraits::Point_2& t,
const typename GeomTraits::Point_2& r,
const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const GeomTraits& traits) {
const FT cot_gamma = cotangent_3(p0, p1, q, traits);
const FT cot_beta = cotangent_3(q, p1, p2, traits);
using FT = typename GeomTraits::FT;
const FT cot_gamma = internal::cotangent_2(traits, t, r, q);
const FT cot_beta = internal::cotangent_2(traits, q, r, p);
auto squared_distance_3 = traits.compute_squared_distance_3_object();
const FT sq_d = squared_distance_3(q, p1);
const auto squared_distance_2 =
traits.compute_squared_distance_2_object();
const FT d2 = squared_distance_2(q, r);
return authalic_ns::weight(cot_gamma, cot_beta, d2);
}
return authalic_ns::weight(cot_gamma, cot_beta, sq_d);
}
template<typename GeomTraits>
typename GeomTraits::FT authalic_weight(
const CGAL::Point_2<GeomTraits>& t,
const CGAL::Point_2<GeomTraits>& r,
const CGAL::Point_2<GeomTraits>& p,
const CGAL::Point_2<GeomTraits>& q) {
const GeomTraits traits;
return authalic_weight(t, r, p, q, traits);
}
template<typename GeomTraits>
typename GeomTraits::FT authalic_weight(
const typename GeomTraits::Point_3& t,
const typename GeomTraits::Point_3& r,
const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const GeomTraits& traits) {
using FT = typename GeomTraits::FT;
const FT cot_gamma = internal::cotangent_3(traits, t, r, q);
const FT cot_beta = internal::cotangent_3(traits, q, r, p);
const auto squared_distance_3 =
traits.compute_squared_distance_3_object();
const FT d2 = squared_distance_3(q, r);
return authalic_ns::weight(cot_gamma, cot_beta, d2);
}
template<typename GeomTraits>
typename GeomTraits::FT authalic_weight(
const CGAL::Point_3<GeomTraits>& t,
const CGAL::Point_3<GeomTraits>& r,
const CGAL::Point_3<GeomTraits>& p,
const CGAL::Point_3<GeomTraits>& q) {
const GeomTraits traits;
return authalic_weight(t, r, p, q, traits);
}
/// \endcond
/*!
\ingroup PkgWeightsRefAuthalicWeights
\brief computes the authalic weight in 3D at `q` using the points `p0`, `p1`, and `p2`.
\tparam Kernel a model of `Kernel`
*/
template<typename Kernel>
typename Kernel::FT authalic_weight(const CGAL::Point_3<Kernel>& p0,
const CGAL::Point_3<Kernel>& p1,
const CGAL::Point_3<Kernel>& p2,
const CGAL::Point_3<Kernel>& q)
{
const Kernel traits;
return authalic_weight(p0, p1, p2, q, traits);
}
} // namespace Weights
} // namespace CGAL

View File

@ -14,131 +14,97 @@
#ifndef CGAL_BARYCENTRIC_REGION_WEIGHTS_H
#define CGAL_BARYCENTRIC_REGION_WEIGHTS_H
// Internal includes.
#include <CGAL/Weights/internal/utils.h>
#include <CGAL/Weights/utils.h>
#include <CGAL/Point_2.h>
#include <CGAL/Point_3.h>
namespace CGAL {
namespace Weights {
#if defined(DOXYGEN_RUNNING)
// 2D ==============================================================================================
/*!
\ingroup PkgWeightsRefBarycentricRegionWeights
/*!
\ingroup PkgWeightsRefBarycentricRegionWeights
\brief computes the area of the barycentric cell in 2D using the points `p`, `q`, and `r`.
\tparam GeomTraits a model of `AnalyticWeightTraits_2`
*/
template<typename GeomTraits>
typename GeomTraits::FT barycentric_area(const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const typename GeomTraits::Point_2& r,
const GeomTraits& traits)
{
using FT = typename GeomTraits::FT;
using Point_2 = typename GeomTraits::Point_2;
\brief computes the area of the barycentric cell in 2D using the points `p`, `q`
and `r`, given a traits class `traits` with geometric objects, predicates, and constructions.
*/
template<typename GeomTraits>
typename GeomTraits::FT barycentric_area(
const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const typename GeomTraits::Point_2& r,
const GeomTraits& traits) { }
auto midpoint_2 = traits.construct_midpoint_2_object();
auto centroid_2 = traits.construct_centroid_2_object();
/*!
\ingroup PkgWeightsRefBarycentricRegionWeights
const Point_2 center = centroid_2(p, q, r);
const Point_2 m1 = midpoint_2(q, r);
const Point_2 m2 = midpoint_2(q, p);
\brief computes the area of the barycentric cell in 3D using the points `p`, `q`
and `r`, given a traits class `traits` with geometric objects, predicates, and constructions.
*/
template<typename GeomTraits>
typename GeomTraits::FT barycentric_area(
const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const typename GeomTraits::Point_3& r,
const GeomTraits& traits) { }
const FT A1 = internal::positive_area_2(q, m1, center, traits);
const FT A2 = internal::positive_area_2(q, center, m2, traits);
return A1 + A2;
}
/*!
\ingroup PkgWeightsRefBarycentricRegionWeights
/*!
\ingroup PkgWeightsRefBarycentricRegionWeights
\brief computes the area of the barycentric cell in 2D using the points `p`, `q`, and `r`.
\tparam Kernel a model of `Kernel`
*/
template<typename Kernel>
typename Kernel::FT barycentric_area(const CGAL::Point_2<Kernel>& p,
const CGAL::Point_2<Kernel>& q,
const CGAL::Point_2<Kernel>& r)
{
const Kernel traits;
return barycentric_area(p, q, r, traits);
}
\brief computes the area of the barycentric cell in 2D using the points `p`, `q`
and `r` which are parameterized by a `Kernel` K.
*/
template<typename K>
typename K::FT barycentric_area(
const CGAL::Point_2<K>& p,
const CGAL::Point_2<K>& q,
const CGAL::Point_2<K>& r) { }
// 3D ==============================================================================================
/*!
\ingroup PkgWeightsRefBarycentricRegionWeights
/*!
\ingroup PkgWeightsRefBarycentricRegionWeights
\brief computes the area of the barycentric cell in 3D using the points `p`, `q`, and `r`.
\tparam GeomTraits a model of `AnalyticWeightTraits_3`
*/
template<typename GeomTraits>
typename GeomTraits::FT barycentric_area(const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const typename GeomTraits::Point_3& r,
const GeomTraits& traits)
{
using FT = typename GeomTraits::FT;
using Point_3 = typename GeomTraits::Point_3;
\brief computes the area of the barycentric cell in 3D using the points `p`, `q`
and `r` which are parameterized by a `Kernel` K.
*/
template<typename K>
typename K::FT barycentric_area(
const CGAL::Point_3<K>& p,
const CGAL::Point_3<K>& q,
const CGAL::Point_3<K>& r) { }
auto midpoint_3 = traits.construct_midpoint_3_object();
auto centroid_3 = traits.construct_centroid_3_object();
#endif // DOXYGEN_RUNNING
const Point_3 center = centroid_3(p, q, r);
const Point_3 m1 = midpoint_3(q, r);
const Point_3 m2 = midpoint_3(q, p);
/// \cond SKIP_IN_MANUAL
template<typename GeomTraits>
typename GeomTraits::FT barycentric_area(
const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const typename GeomTraits::Point_2& r,
const GeomTraits& traits) {
const FT A1 = internal::positive_area_3(q, m1, center, traits);
const FT A2 = internal::positive_area_3(q, center, m2, traits);
return A1 + A2;
}
using FT = typename GeomTraits::FT;
const auto midpoint_2 =
traits.construct_midpoint_2_object();
const auto centroid_2 =
traits.construct_centroid_2_object();
const auto center = centroid_2(p, q, r);
const auto m1 = midpoint_2(q, r);
const auto m2 = midpoint_2(q, p);
const FT A1 = internal::positive_area_2(traits, q, m1, center);
const FT A2 = internal::positive_area_2(traits, q, center, m2);
return A1 + A2;
}
template<typename GeomTraits>
typename GeomTraits::FT barycentric_area(
const CGAL::Point_2<GeomTraits>& p,
const CGAL::Point_2<GeomTraits>& q,
const CGAL::Point_2<GeomTraits>& r) {
const GeomTraits traits;
return barycentric_area(p, q, r, traits);
}
template<typename GeomTraits>
typename GeomTraits::FT barycentric_area(
const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const typename GeomTraits::Point_3& r,
const GeomTraits& traits) {
using FT = typename GeomTraits::FT;
const auto midpoint_3 =
traits.construct_midpoint_3_object();
const auto centroid_3 =
traits.construct_centroid_3_object();
const auto center = centroid_3(p, q, r);
const auto m1 = midpoint_3(q, r);
const auto m2 = midpoint_3(q, p);
const FT A1 = internal::positive_area_3(traits, q, m1, center);
const FT A2 = internal::positive_area_3(traits, q, center, m2);
return A1 + A2;
}
template<typename GeomTraits>
typename GeomTraits::FT barycentric_area(
const CGAL::Point_3<GeomTraits>& p,
const CGAL::Point_3<GeomTraits>& q,
const CGAL::Point_3<GeomTraits>& r) {
const GeomTraits traits;
return barycentric_area(p, q, r, traits);
}
/// \endcond
/*!
\ingroup PkgWeightsRefBarycentricRegionWeights
\brief computes the area of the barycentric cell in 3D using the points `p`, `q`, and `r`.
\tparam Kernel a model of `Kernel`
*/
template<typename Kernel>
typename Kernel::FT barycentric_area(const CGAL::Point_3<Kernel>& p,
const CGAL::Point_3<Kernel>& q,
const CGAL::Point_3<Kernel>& r)
{
const Kernel traits;
return barycentric_area(p, q, r, traits);
}
} // namespace Weights
} // namespace CGAL

View File

@ -14,482 +14,356 @@
#ifndef CGAL_COTANGENT_WEIGHTS_H
#define CGAL_COTANGENT_WEIGHTS_H
// Internal includes.
#include <CGAL/Weights/internal/utils.h>
#include <CGAL/Weights/utils.h>
#include <CGAL/boost/graph/helpers.h>
#include <CGAL/Kernel_traits.h>
#include <CGAL/Point_2.h>
#include <CGAL/Point_3.h>
#include <CGAL/utility.h>
namespace CGAL {
namespace Weights {
/// \cond SKIP_IN_MANUAL
namespace cotangent_ns {
/// \cond SKIP_IN_MANUAL
template<typename FT>
FT half_weight(const FT cot) {
return FT(2) * cot;
}
namespace cotangent_ns {
template<typename FT>
FT weight(const FT cot_beta, const FT cot_gamma) {
return FT(2) * (cot_beta + cot_gamma);
}
template<typename FT>
FT half_weight(const FT cot)
{
return FT(2) * cot;
}
template<typename FT>
FT weight(const FT cot_beta, const FT cot_gamma)
{
return FT(2) * (cot_beta + cot_gamma);
}
} // namespace cotangent_ns
/// \endcond
/*!
\ingroup PkgWeightsRefCotangentWeights
\brief computes the half value of the cotangent weight.
This function constructs the half of the cotangent weight using the precomputed
cotangent value. The returned value is \f$2\textbf{cot}\f$.
\tparam FT a model of `FieldNumberType`
\param cot the cotangent value
\sa `cotangent_weight()`
*/
template<typename FT>
FT half_cotangent_weight(const FT cot)
{
return cotangent_ns::half_weight(cot);
}
/*!
\ingroup PkgWeightsRefCotangentWeights
\brief computes the cotangent weight in 2D at `q` using the points `p0`, `p1`, and `p2`.
\tparam GeomTraits a model of `AnalyticWeightTraits_2`
*/
template<typename GeomTraits>
typename GeomTraits::FT cotangent_weight(const typename GeomTraits::Point_2& p0,
const typename GeomTraits::Point_2& p1,
const typename GeomTraits::Point_2& p2,
const typename GeomTraits::Point_2& q,
const GeomTraits& traits)
{
using FT = typename GeomTraits::FT;
const FT cot_beta = cotangent_2(q, p0, p1, traits);
const FT cot_gamma = cotangent_2(p1, p2, q, traits);
return cotangent_ns::weight(cot_beta, cot_gamma);
}
/*!
\ingroup PkgWeightsRefCotangentWeights
\brief computes the cotangent weight in 2D at `q` using the points `p0`, `p1`, and `p2`.
\tparam Kernel a model of `Kernel`
*/
template<typename Kernel>
typename Kernel::FT cotangent_weight(const CGAL::Point_2<Kernel>& p0,
const CGAL::Point_2<Kernel>& p1,
const CGAL::Point_2<Kernel>& p2,
const CGAL::Point_2<Kernel>& q)
{
Kernel traits;
return cotangent_weight(p0, p1, p2, q, traits);
}
// 3D ==============================================================================================
/*!
\ingroup PkgWeightsRefCotangentWeights
\brief computes the cotangent weight in 3D at `q` using the points `p0`, `p1`, and `p2`.
\tparam GeomTraits a model of `AnalyticWeightTraits_3`
*/
template<typename GeomTraits>
typename GeomTraits::FT cotangent_weight(const typename GeomTraits::Point_3& p0,
const typename GeomTraits::Point_3& p1,
const typename GeomTraits::Point_3& p2,
const typename GeomTraits::Point_3& q,
const GeomTraits& traits)
{
using FT = typename GeomTraits::FT;
const FT cot_beta = cotangent_3(q, p0, p1, traits);
const FT cot_gamma = cotangent_3(p1, p2, q, traits);
return cotangent_ns::weight(cot_beta, cot_gamma);
}
/*!
\ingroup PkgWeightsRefCotangentWeights
\brief computes the cotangent weight in 3D at `q` using the points `p0`, `p1`, and `p2`.
\tparam Kernel a model of `Kernel`
*/
template<typename Kernel>
typename Kernel::FT cotangent_weight(const CGAL::Point_3<Kernel>& p0,
const CGAL::Point_3<Kernel>& p1,
const CGAL::Point_3<Kernel>& p2,
const CGAL::Point_3<Kernel>& q)
{
Kernel traits;
return cotangent_weight(p0, p1, p2, q, traits);
}
/// \cond SKIP_IN_MANUAL
// Undocumented cotangent weight class.
// Returns: cot(beta)
//
// Returns a single cotangent weight, its operator() is defined based on the
// halfedge_descriptor, polygon mesh, and vertex to point map.
// For border edges it returns zero.
// This version is currently used in:
// Surface_mesh_deformation -> Surface_mesh_deformation.h
template<typename PolygonMesh,
typename VertexPointMap,
typename GeomTraits = typename Kernel_traits<
typename boost::property_traits<VertexPointMap>::value_type>::type>
class Single_cotangent_weight
{
using vertex_descriptor = typename boost::graph_traits<PolygonMesh>::vertex_descriptor;
using halfedge_descriptor = typename boost::graph_traits<PolygonMesh>::halfedge_descriptor;
using Point_ref = typename boost::property_traits<VertexPointMap>::reference;
using FT = typename GeomTraits::FT;
private:
const PolygonMesh& m_pmesh;
const VertexPointMap m_vpm;
const GeomTraits m_traits;
public:
Single_cotangent_weight(const PolygonMesh& pmesh,
const VertexPointMap vpm,
const GeomTraits& traits = GeomTraits())
: m_pmesh(pmesh), m_vpm(vpm), m_traits(traits)
{ }
decltype(auto) operator()(const halfedge_descriptor he) const
{
if (is_border(he, m_pmesh))
return FT{0};
const vertex_descriptor v0 = target(he, m_pmesh);
const vertex_descriptor v1 = source(he, m_pmesh);
const vertex_descriptor v2 = target(next(he, m_pmesh), m_pmesh);
const Point_ref p0 = get(m_vpm, v0);
const Point_ref p1 = get(m_vpm, v1);
const Point_ref p2 = get(m_vpm, v2);
return cotangent_3(p0, p2, p1, m_traits);
}
/// \endcond
};
/*!
\ingroup PkgWeightsRefCotangentWeights
// Undocumented cotangent weight class.
// Returns: 0.5 * (cot(beta) + cot(gamma))
//
// Its constructor takes a boolean flag to choose between default and clamped
// versions of the cotangent weights and its operator() is defined based on the
// halfedge_descriptor, polygon mesh, and vertex to point map.
// This version is currently used in:
// Polygon_mesh_processing -> curvature_flow_impl.h (no clamping, no bounding)
// Surface_mesh_deformation -> Surface_mesh_deformation.h (default version)
// Surface_mesh_parameterizer -> Orbifold_Tutte_parameterizer_3.h (default version)
// Surface_mesh_skeletonization -> Mean_curvature_flow_skeletonization.h (clamped version)
template<typename PolygonMesh,
typename VertexPointMap,
typename GeomTraits = typename Kernel_traits<
typename boost::property_traits<VertexPointMap>::value_type>::type>
class Cotangent_weight
{
using vertex_descriptor = typename boost::graph_traits<PolygonMesh>::vertex_descriptor;
using halfedge_descriptor = typename boost::graph_traits<PolygonMesh>::halfedge_descriptor;
\brief computes the half value of the cotangent weight.
using Point_ref = typename boost::property_traits<VertexPointMap>::reference;
using FT = typename GeomTraits::FT;
This function constructs the half of the cotangent weight using the precomputed
cotangent value. The returned value is
\f$2\textbf{cot}\f$.
private:
const PolygonMesh& m_pmesh;
const VertexPointMap m_vpm;
const GeomTraits m_traits;
\tparam FT
a model of `FieldNumberType`
bool m_use_clamped_version;
bool m_bound_from_below;
\param cot
the cotangent value
public:
Cotangent_weight(const PolygonMesh& pmesh,
const VertexPointMap vpm,
const GeomTraits& traits = GeomTraits(),
const bool use_clamped_version = false,
const bool bound_from_below = true)
: m_pmesh(pmesh), m_vpm(vpm), m_traits(traits),
m_use_clamped_version(use_clamped_version),
m_bound_from_below(bound_from_below)
{ }
\sa `cotangent_weight()`
*/
template<typename FT>
FT half_cotangent_weight(const FT cot) {
return cotangent_ns::half_weight(cot);
decltype(auto) operator()(const halfedge_descriptor he) const
{
if(is_border(he, m_pmesh))
return FT{0};
auto half_weight = [&] (const halfedge_descriptor he) -> FT
{
if(is_border(he, m_pmesh))
return FT{0};
const vertex_descriptor v0 = target(he, m_pmesh);
const vertex_descriptor v1 = source(he, m_pmesh);
const vertex_descriptor v2 = target(next(he, m_pmesh), m_pmesh);
const Point_ref p0 = get(m_vpm, v0);
const Point_ref p1 = get(m_vpm, v1);
const Point_ref p2 = get(m_vpm, v2);
FT weight = 0;
if (m_use_clamped_version)
weight = cotangent_3_clamped(p1, p2, p0, m_traits);
else
weight = cotangent_3(p1, p2, p0, m_traits);
if(m_bound_from_below)
weight = (CGAL::max)(FT(0), weight);
return weight / FT(2);
};
FT weight = half_weight(he) + half_weight(opposite(he, m_pmesh));
return weight;
}
};
// Undocumented cotangent weight class.
//
// Its constructor takes a polygon mesh and a vertex to point map
// and its operator() is defined based on the halfedge_descriptor only.
// This class is using a special clamped version of the cotangent weights.
// This version is currently used in:
// Polygon_mesh_processing -> fair.h
// Polyhedron demo -> Hole_filling_plugin.cpp
template<typename PolygonMesh,
typename VertexPointMap,
typename GeomTraits>
class Secure_cotangent_weight_with_voronoi_area
{
using vertex_descriptor = typename boost::graph_traits<PolygonMesh>::vertex_descriptor;
using halfedge_descriptor = typename boost::graph_traits<PolygonMesh>::halfedge_descriptor;
using Point_ref = typename boost::property_traits<VertexPointMap>::reference;
using FT = typename GeomTraits::FT;
using Vector_3 = typename GeomTraits::Vector_3;
private:
const PolygonMesh& m_pmesh;
const VertexPointMap m_vpm;
GeomTraits m_traits;
Cotangent_weight<PolygonMesh, VertexPointMap, GeomTraits> cotangent_weight_calculator;
public:
Secure_cotangent_weight_with_voronoi_area(const PolygonMesh& pmesh,
const VertexPointMap vpm,
const GeomTraits& traits = GeomTraits())
: m_pmesh(pmesh), m_vpm(vpm), m_traits(traits),
cotangent_weight_calculator(m_pmesh, m_vpm, m_traits,
true /*clamp*/, true /*bound from below*/)
{ }
FT w_i(const vertex_descriptor v_i) const
{
return FT(1) / (FT(2) * voronoi(v_i));
}
#if defined(DOXYGEN_RUNNING)
/*!
\ingroup PkgWeightsRefCotangentWeights
\brief computes the cotangent weight in 2D at `q` using the points `p0`, `p1`,
and `p2`, given a traits class `traits` with geometric objects, predicates, and constructions.
*/
template<typename GeomTraits>
typename GeomTraits::FT cotangent_weight(
const typename GeomTraits::Point_2& p0,
const typename GeomTraits::Point_2& p1,
const typename GeomTraits::Point_2& p2,
const typename GeomTraits::Point_2& q,
const GeomTraits& traits) { }
/*!
\ingroup PkgWeightsRefCotangentWeights
\brief computes the cotangent weight in 3D at `q` using the points `p0`, `p1`,
and `p2`, given a traits class `traits` with geometric objects, predicates, and constructions.
*/
template<typename GeomTraits>
typename GeomTraits::FT cotangent_weight(
const typename GeomTraits::Point_3& p0,
const typename GeomTraits::Point_3& p1,
const typename GeomTraits::Point_3& p2,
const typename GeomTraits::Point_3& q,
const GeomTraits& traits) { }
/*!
\ingroup PkgWeightsRefCotangentWeights
\brief computes the cotangent weight in 2D at `q` using the points `p0`, `p1`,
and `p2` which are parameterized by a `Kernel` K.
*/
template<typename K>
typename K::FT cotangent_weight(
const CGAL::Point_2<K>& p0,
const CGAL::Point_2<K>& p1,
const CGAL::Point_2<K>& p2,
const CGAL::Point_2<K>& q) { }
/*!
\ingroup PkgWeightsRefCotangentWeights
\brief computes the cotangent weight in 3D at `q` using the points `p0`, `p1`,
and `p2` which are parameterized by a `Kernel` K.
*/
template<typename K>
typename K::FT cotangent_weight(
const CGAL::Point_3<K>& p0,
const CGAL::Point_3<K>& p1,
const CGAL::Point_3<K>& p2,
const CGAL::Point_3<K>& q) { }
#endif // DOXYGEN_RUNNING
/// \cond SKIP_IN_MANUAL
template<typename GeomTraits>
typename GeomTraits::FT cotangent_weight(
const typename GeomTraits::Point_2& t,
const typename GeomTraits::Point_2& r,
const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const GeomTraits& traits) {
using FT = typename GeomTraits::FT;
const FT cot_beta = internal::cotangent_2(traits, q, t, r);
const FT cot_gamma = internal::cotangent_2(traits, r, p, q);
return cotangent_ns::weight(cot_beta, cot_gamma);
FT w_ij(const halfedge_descriptor he) const
{
return cotangent_weight_calculator(he);
}
template<typename GeomTraits>
typename GeomTraits::FT cotangent_weight(
const CGAL::Point_2<GeomTraits>& t,
const CGAL::Point_2<GeomTraits>& r,
const CGAL::Point_2<GeomTraits>& p,
const CGAL::Point_2<GeomTraits>& q) {
GeomTraits traits;
return cotangent_weight(t, r, p, q, traits);
}
private:
FT voronoi(const vertex_descriptor v0) const
{
auto squared_length_3 = m_traits.compute_squared_length_3_object();
auto vector_3 = m_traits.construct_vector_3_object();
template<typename GeomTraits>
typename GeomTraits::FT cotangent_weight(
const typename GeomTraits::Point_3& t,
const typename GeomTraits::Point_3& r,
const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const GeomTraits& traits) {
FT voronoi_area = FT(0);
for (const halfedge_descriptor he : halfedges_around_target(halfedge(v0, m_pmesh), m_pmesh))
{
CGAL_assertion(v0 == target(he, m_pmesh));
CGAL_assertion(CGAL::is_triangle(he, m_pmesh));
using FT = typename GeomTraits::FT;
const FT cot_beta = internal::cotangent_3(traits, q, t, r);
const FT cot_gamma = internal::cotangent_3(traits, r, p, q);
return cotangent_ns::weight(cot_beta, cot_gamma);
}
if (is_border(he, m_pmesh))
continue;
template<typename GeomTraits>
typename GeomTraits::FT cotangent_weight(
const CGAL::Point_3<GeomTraits>& t,
const CGAL::Point_3<GeomTraits>& r,
const CGAL::Point_3<GeomTraits>& p,
const CGAL::Point_3<GeomTraits>& q) {
const vertex_descriptor v1 = source(he, m_pmesh);
const vertex_descriptor v2 = target(next(he, m_pmesh), m_pmesh);
GeomTraits traits;
return cotangent_weight(t, r, p, q, traits);
}
const Point_ref p0 = get(m_vpm, v0);
const Point_ref p1 = get(m_vpm, v1);
const Point_ref p2 = get(m_vpm, v2);
// Undocumented cotangent weight class.
// Its constructor takes a polygon mesh and a vertex to point map
// and its operator() is defined based on the halfedge_descriptor only.
// This version is currently used in:
// Polygon_mesh_processing -> curvature_flow_impl.h
template<
typename PolygonMesh,
typename VertexPointMap = typename boost::property_map<PolygonMesh, CGAL::vertex_point_t>::type>
class Edge_cotangent_weight {
using GeomTraits = typename CGAL::Kernel_traits<
typename boost::property_traits<VertexPointMap>::value_type>::type;
using FT = typename GeomTraits::FT;
const PolygonMesh& m_pmesh;
const VertexPointMap m_pmap;
GeomTraits m_traits;
public:
using vertex_descriptor = typename boost::graph_traits<PolygonMesh>::vertex_descriptor;
using halfedge_descriptor = typename boost::graph_traits<PolygonMesh>::halfedge_descriptor;
Edge_cotangent_weight(const PolygonMesh& pmesh, const VertexPointMap pmap) :
m_pmesh(pmesh), m_pmap(pmap), m_traits() { }
FT operator()(const halfedge_descriptor he) const {
FT weight = FT(0);
if (is_border_edge(he, m_pmesh)) {
const auto h1 = next(he, m_pmesh);
const auto v0 = target(he, m_pmesh);
const auto v1 = source(he, m_pmesh);
const auto v2 = target(h1, m_pmesh);
const auto& p0 = get(m_pmap, v0);
const auto& p1 = get(m_pmap, v1);
const auto& p2 = get(m_pmap, v2);
weight = internal::cotangent_3(m_traits, p0, p2, p1);
} else {
const auto h1 = next(he, m_pmesh);
const auto h2 = prev(opposite(he, m_pmesh), m_pmesh);
const auto v0 = target(he, m_pmesh);
const auto v1 = source(he, m_pmesh);
const auto v2 = target(h1, m_pmesh);
const auto v3 = source(h2, m_pmesh);
const auto& p0 = get(m_pmap, v0);
const auto& p1 = get(m_pmap, v1);
const auto& p2 = get(m_pmap, v2);
const auto& p3 = get(m_pmap, v3);
weight = cotangent_weight(p2, p1, p3, p0) / FT(2);
const CGAL::Angle angle0 = CGAL::angle(p1, p0, p2);
if((angle0 == CGAL::OBTUSE) ||
(CGAL::angle(p2, p1, p0) == CGAL::OBTUSE) ||
(CGAL::angle(p0, p2, p1) == CGAL::OBTUSE))
{
const FT A = internal::positive_area_3(p0, p1, p2, m_traits);
if (angle0 == CGAL::OBTUSE)
voronoi_area += A / FT(2);
else
voronoi_area += A / FT(4);
}
return weight;
}
};
else
{
const FT cot_p1 = cotangent_3_clamped(p2, p1, p0, m_traits);
const FT cot_p2 = cotangent_3_clamped(p0, p2, p1, m_traits);
// Undocumented cotangent weight class.
// Returns a single cotangent weight, its operator() is defined based on the
// halfedge_descriptor, polygon mesh, and vertex to point map.
// For border edges it returns zero.
// This version is currently used in:
// Surface_mesh_deformation -> Surface_mesh_deformation.h
template<typename PolygonMesh>
class Single_cotangent_weight {
const Vector_3 v1 = vector_3(p0, p1);
const Vector_3 v2 = vector_3(p0, p2);
public:
using vertex_descriptor = typename boost::graph_traits<PolygonMesh>::vertex_descriptor;
using halfedge_descriptor = typename boost::graph_traits<PolygonMesh>::halfedge_descriptor;
template<class VertexPointMap>
decltype(auto) operator()(const halfedge_descriptor he,
const PolygonMesh& pmesh, const VertexPointMap pmap) const {
using GeomTraits = typename CGAL::Kernel_traits<
typename boost::property_traits<VertexPointMap>::value_type>::type;
using FT = typename GeomTraits::FT;
GeomTraits traits;
if (is_border(he, pmesh)) {
return FT(0);
const FT t1 = cot_p1 * squared_length_3(v2);
const FT t2 = cot_p2 * squared_length_3(v1);
voronoi_area += (t1 + t2) / FT(8);
}
const vertex_descriptor v0 = target(he, pmesh);
const vertex_descriptor v1 = source(he, pmesh);
const vertex_descriptor v2 = target(next(he, pmesh), pmesh);
const auto& p0 = get(pmap, v0);
const auto& p1 = get(pmap, v1);
const auto& p2 = get(pmap, v2);
return internal::cotangent_3(traits, p0, p2, p1);
}
};
// Undocumented cotangent weight class.
// Its constructor takes a boolean flag to choose between default and clamped
// versions of the cotangent weights and its operator() is defined based on the
// halfedge_descriptor, polygon mesh, and vertex to point map.
// This version is currently used in:
// Surface_mesh_deformation -> Surface_mesh_deformation.h (default version)
// Surface_mesh_parameterizer -> Orbifold_Tutte_parameterizer_3.h (default version)
// Surface_mesh_skeletonization -> Mean_curvature_flow_skeletonization.h (clamped version)
template<typename PolygonMesh>
class Cotangent_weight {
bool m_use_clamped_version;
public:
using vertex_descriptor = typename boost::graph_traits<PolygonMesh>::vertex_descriptor;
using halfedge_descriptor = typename boost::graph_traits<PolygonMesh>::halfedge_descriptor;
Cotangent_weight(const bool use_clamped_version = false) :
m_use_clamped_version(use_clamped_version) { }
template<class VertexPointMap>
decltype(auto) operator()(const halfedge_descriptor he,
const PolygonMesh& pmesh, const VertexPointMap pmap) const {
using GeomTraits = typename CGAL::Kernel_traits<
typename boost::property_traits<VertexPointMap>::value_type>::type;
using FT = typename GeomTraits::FT;
GeomTraits traits;
const auto v0 = target(he, pmesh);
const auto v1 = source(he, pmesh);
const auto& p0 = get(pmap, v0);
const auto& p1 = get(pmap, v1);
FT weight = FT(0);
if (is_border_edge(he, pmesh)) {
const auto he_cw = opposite(next(he, pmesh), pmesh);
auto v2 = source(he_cw, pmesh);
if (is_border_edge(he_cw, pmesh)) {
const auto he_ccw = prev(opposite(he, pmesh), pmesh);
v2 = source(he_ccw, pmesh);
const auto& p2 = get(pmap, v2);
if (m_use_clamped_version) {
weight = internal::cotangent_3_clamped(traits, p1, p2, p0);
} else {
weight = internal::cotangent_3(traits, p1, p2, p0);
}
weight = (CGAL::max)(FT(0), weight);
weight /= FT(2);
} else {
const auto& p2 = get(pmap, v2);
if (m_use_clamped_version) {
weight = internal::cotangent_3_clamped(traits, p0, p2, p1);
} else {
weight = internal::cotangent_3(traits, p0, p2, p1);
}
weight = (CGAL::max)(FT(0), weight);
weight /= FT(2);
}
} else {
const auto he_cw = opposite(next(he, pmesh), pmesh);
const auto v2 = source(he_cw, pmesh);
const auto he_ccw = prev(opposite(he, pmesh), pmesh);
const auto v3 = source(he_ccw, pmesh);
const auto& p2 = get(pmap, v2);
const auto& p3 = get(pmap, v3);
FT cot_beta = FT(0), cot_gamma = FT(0);
if (m_use_clamped_version) {
cot_beta = internal::cotangent_3_clamped(traits, p0, p2, p1);
} else {
cot_beta = internal::cotangent_3(traits, p0, p2, p1);
}
if (m_use_clamped_version) {
cot_gamma = internal::cotangent_3_clamped(traits, p1, p3, p0);
} else {
cot_gamma = internal::cotangent_3(traits, p1, p3, p0);
}
cot_beta = (CGAL::max)(FT(0), cot_beta); cot_beta /= FT(2);
cot_gamma = (CGAL::max)(FT(0), cot_gamma); cot_gamma /= FT(2);
weight = cot_beta + cot_gamma;
}
return weight;
}
};
// Undocumented cotangent weight class.
// Its constructor takes a polygon mesh and a vertex to point map
// and its operator() is defined based on the halfedge_descriptor only.
// This class is using a special clamped version of the cotangent weights.
// This version is currently used in:
// Polygon_mesh_processing -> fair.h
// Polyhedron demo -> Hole_filling_plugin.cpp
template<
typename PolygonMesh,
typename VertexPointMap = typename boost::property_map<PolygonMesh, CGAL::vertex_point_t>::type>
class Secure_cotangent_weight_with_voronoi_area {
using GeomTraits = typename CGAL::Kernel_traits<
typename boost::property_traits<VertexPointMap>::value_type>::type;
using FT = typename GeomTraits::FT;
const PolygonMesh& m_pmesh;
const VertexPointMap m_pmap;
GeomTraits m_traits;
public:
using vertex_descriptor = typename boost::graph_traits<PolygonMesh>::vertex_descriptor;
using halfedge_descriptor = typename boost::graph_traits<PolygonMesh>::halfedge_descriptor;
Secure_cotangent_weight_with_voronoi_area(const PolygonMesh& pmesh, const VertexPointMap pmap) :
m_pmesh(pmesh), m_pmap(pmap), m_traits() { }
FT w_i(const vertex_descriptor v_i) const {
return FT(1) / (FT(2) * voronoi(v_i));
}
FT w_ij(const halfedge_descriptor he) const {
return cotangent_clamped(he);
}
CGAL_assertion(!is_zero(voronoi_area));
return voronoi_area;
}
};
private:
FT cotangent_clamped(const halfedge_descriptor he) const {
const auto v0 = target(he, m_pmesh);
const auto v1 = source(he, m_pmesh);
const auto& p0 = get(m_pmap, v0);
const auto& p1 = get(m_pmap, v1);
FT weight = FT(0);
if (is_border_edge(he, m_pmesh)) {
const auto he_cw = opposite(next(he, m_pmesh), m_pmesh);
auto v2 = source(he_cw, m_pmesh);
if (is_border_edge(he_cw, m_pmesh)) {
const auto he_ccw = prev(opposite(he, m_pmesh), m_pmesh);
v2 = source(he_ccw, m_pmesh);
const auto& p2 = get(m_pmap, v2);
weight = internal::cotangent_3_clamped(m_traits, p1, p2, p0);
} else {
const auto& p2 = get(m_pmap, v2);
weight = internal::cotangent_3_clamped(m_traits, p0, p2, p1);
}
} else {
const auto he_cw = opposite(next(he, m_pmesh), m_pmesh);
const auto v2 = source(he_cw, m_pmesh);
const auto he_ccw = prev(opposite(he, m_pmesh), m_pmesh);
const auto v3 = source(he_ccw, m_pmesh);
const auto& p2 = get(m_pmap, v2);
const auto& p3 = get(m_pmap, v3);
const FT cot_beta = internal::cotangent_3_clamped(m_traits, p0, p2, p1);
const FT cot_gamma = internal::cotangent_3_clamped(m_traits, p1, p3, p0);
weight = cot_beta + cot_gamma;
}
return weight;
}
FT voronoi(const vertex_descriptor v0) const {
const auto squared_length_3 =
m_traits.compute_squared_length_3_object();
const auto construct_vector_3 =
m_traits.construct_vector_3_object();
FT voronoi_area = FT(0);
CGAL_assertion(CGAL::is_triangle_mesh(m_pmesh));
for (const auto& he : halfedges_around_target(halfedge(v0, m_pmesh), m_pmesh)) {
CGAL_assertion(v0 == target(he, m_pmesh));
if (is_border(he, m_pmesh)) {
continue;
}
const auto v1 = source(he, m_pmesh);
const auto v2 = target(next(he, m_pmesh), m_pmesh);
const auto& p0 = get(m_pmap, v0);
const auto& p1 = get(m_pmap, v1);
const auto& p2 = get(m_pmap, v2);
const auto angle0 = CGAL::angle(p1, p0, p2);
const auto angle1 = CGAL::angle(p2, p1, p0);
const auto angle2 = CGAL::angle(p0, p2, p1);
const bool obtuse =
(angle0 == CGAL::OBTUSE) ||
(angle1 == CGAL::OBTUSE) ||
(angle2 == CGAL::OBTUSE);
if (!obtuse) {
const FT cot_p1 = internal::cotangent_3(m_traits, p2, p1, p0);
const FT cot_p2 = internal::cotangent_3(m_traits, p0, p2, p1);
const auto v1 = construct_vector_3(p0, p1);
const auto v2 = construct_vector_3(p0, p2);
const FT t1 = cot_p1 * squared_length_3(v2);
const FT t2 = cot_p2 * squared_length_3(v1);
voronoi_area += (t1 + t2) / FT(8);
} else {
const FT A = internal::positive_area_3(m_traits, p0, p1, p2);
if (angle0 == CGAL::OBTUSE) {
voronoi_area += A / FT(2);
} else {
voronoi_area += A / FT(4);
}
}
}
CGAL_assertion(voronoi_area != FT(0));
return voronoi_area;
}
};
/// \endcond
/// \endcond
} // namespace Weights
} // namespace CGAL

View File

@ -14,429 +14,379 @@
#ifndef CGAL_DISCRETE_HARMONIC_WEIGHTS_H
#define CGAL_DISCRETE_HARMONIC_WEIGHTS_H
// Internal includes.
#include <CGAL/Weights/internal/utils.h>
#include <CGAL/Weights/internal/polygon_utils_2.h>
#include <CGAL/Point_2.h>
#include <CGAL/Point_3.h>
#include <CGAL/property_map.h>
#include <vector>
namespace CGAL {
namespace Weights {
/// \cond SKIP_IN_MANUAL
namespace discrete_harmonic_ns {
/// \cond SKIP_IN_MANUAL
template<typename FT>
FT weight(
const FT r1, const FT r2, const FT r3,
const FT A1, const FT A2, const FT B) {
namespace discrete_harmonic_ns {
FT w = FT(0);
CGAL_precondition(A1 != FT(0) && A2 != FT(0));
const FT prod = A1 * A2;
if (prod != FT(0)) {
const FT inv = FT(1) / prod;
w = (r3 * A1 - r2 * B + r1 * A2) * inv;
}
return w;
}
}
/// \endcond
template<typename FT>
FT weight(const FT d0, const FT d2, const FT d,
const FT A0, const FT A2, const FT B)
{
FT w = FT(0);
CGAL_precondition(!is_zero(A0) && !is_zero(A2));
const FT prod = A0 * A2;
if (!is_zero(prod))
w = (d2 * A0 - d * B + d0 * A2) / prod;
#if defined(DOXYGEN_RUNNING)
return w;
}
/*!
\ingroup PkgWeightsRefDiscreteHarmonicWeights
} // namespace discrete_harmonic_ns
\brief computes the discrete harmonic weight in 2D at `q` using the points `p0`, `p1`,
and `p2`, given a traits class `traits` with geometric objects, predicates, and constructions.
*/
template<typename GeomTraits>
typename GeomTraits::FT discrete_harmonic_weight(
const typename GeomTraits::Point_2& p0,
const typename GeomTraits::Point_2& p1,
const typename GeomTraits::Point_2& p2,
const typename GeomTraits::Point_2& q,
const GeomTraits& traits) { }
/// \endcond
/*!
\ingroup PkgWeightsRefDiscreteHarmonicWeights
// 2D ==============================================================================================
\brief computes the discrete harmonic weight in 2D at `q` using the points `p0`, `p1`,
and `p2` which are parameterized by a `Kernel` K.
*/
template<typename K>
typename K::FT discrete_harmonic_weight(
const CGAL::Point_2<K>& p0,
const CGAL::Point_2<K>& p1,
const CGAL::Point_2<K>& p2,
const CGAL::Point_2<K>& q) { }
/*!
\ingroup PkgWeightsRefDiscreteHarmonicWeights
\brief computes the discrete harmonic weight in 2D at `q` using the points `p0`, `p1`, and `p2`.
\tparam GeomTraits a model of `AnalyticWeightTraits_2`
*/
template<typename GeomTraits>
typename GeomTraits::FT discrete_harmonic_weight(const typename GeomTraits::Point_2& p0,
const typename GeomTraits::Point_2& p1,
const typename GeomTraits::Point_2& p2,
const typename GeomTraits::Point_2& q,
const GeomTraits& traits)
{
using FT = typename GeomTraits::FT;
#endif // DOXYGEN_RUNNING
auto squared_distance_2 = traits.compute_squared_distance_2_object();
auto area_2 = traits.compute_area_2_object();
const FT d0 = squared_distance_2(q, p0);
const FT d = squared_distance_2(q, p1);
const FT d2 = squared_distance_2(q, p2);
const FT A0 = area_2(p1, q, p0);
const FT A2 = area_2(p2, q, p1);
const FT B = area_2(p2, q, p0);
return discrete_harmonic_ns::weight(d0, d2, d, A0, A2, B);
}
/*!
\ingroup PkgWeightsRefDiscreteHarmonicWeights
\brief computes the discrete harmonic weight in 2D at `q` using the points `p0`, `p1`, and `p2`.
\tparam Kernel a model of `Kernel`
*/
template<typename Kernel>
typename Kernel::FT discrete_harmonic_weight(const CGAL::Point_2<Kernel>& p0,
const CGAL::Point_2<Kernel>& p1,
const CGAL::Point_2<Kernel>& p2,
const CGAL::Point_2<Kernel>& q)
{
const Kernel traits;
return discrete_harmonic_weight(p0, p1, p2, q, traits);
}
// 3D ==============================================================================================
/// \cond SKIP_IN_MANUAL
template<typename GeomTraits>
typename GeomTraits::FT discrete_harmonic_weight(const typename GeomTraits::Point_3& p0,
const typename GeomTraits::Point_3& p1,
const typename GeomTraits::Point_3& p2,
const typename GeomTraits::Point_3& q,
const GeomTraits& traits)
{
using Point_2 = typename GeomTraits::Point_2;
Point_2 p0f, p1f, p2f, qf;
internal::flatten(p0, p1, p2, q,
p0f, p1f, p2f, qf,
traits);
return discrete_harmonic_weight(p0f, p1f, p2f, qf, traits);
}
template<typename Kernel>
typename Kernel::FT discrete_harmonic_weight(const CGAL::Point_3<Kernel>& p0,
const CGAL::Point_3<Kernel>& p1,
const CGAL::Point_3<Kernel>& p2,
const CGAL::Point_3<Kernel>& q)
{
const Kernel traits;
return discrete_harmonic_weight(p0, p1, p2, q, traits);
}
/// \endcond
/*!
\ingroup PkgWeightsRefBarycentricDiscreteHarmonicWeights
\brief 2D discrete harmonic weights for polygons.
This class implements 2D discrete harmonic weights (\cite cgal:bc:eddhls-maam-95,
\cite cgal:bc:fhk-gcbcocp-06, \cite cgal:pp-cdmsc-93) which can be computed
at any point inside a strictly convex polygon.
Discrete harmonic weights are well-defined inside a strictly convex polygon
but they are not necessarily positive. These weights are computed analytically
using the formulation from the `discrete_harmonic_weight()`.
\tparam VertexRange a model of `ConstRange` whose iterator type is `RandomAccessIterator`
\tparam GeomTraits a model of `AnalyticWeightTraits_2`
\tparam PointMap a model of `ReadablePropertyMap` whose key type is `VertexRange::value_type` and
value type is `Point_2`. The default is `CGAL::Identity_property_map`.
\cgalModels `BarycentricWeights_2`
*/
template<typename VertexRange,
typename GeomTraits,
typename PointMap = CGAL::Identity_property_map<typename GeomTraits::Point_2> >
class Discrete_harmonic_weights_2
{
public:
/// \name Types
/// @{
/// \cond SKIP_IN_MANUAL
template<typename GeomTraits>
typename GeomTraits::FT discrete_harmonic_weight(
const typename GeomTraits::Point_2& t,
const typename GeomTraits::Point_2& r,
const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const GeomTraits& traits) {
using FT = typename GeomTraits::FT;
const auto squared_distance_2 =
traits.compute_squared_distance_2_object();
const FT d1 = squared_distance_2(q, t);
const FT d2 = squared_distance_2(q, r);
const FT d3 = squared_distance_2(q, p);
const FT A1 = internal::area_2(traits, r, q, t);
const FT A2 = internal::area_2(traits, p, q, r);
const FT B = internal::area_2(traits, p, q, t);
return discrete_harmonic_ns::weight(
d1, d2, d3, A1, A2, B);
}
template<typename GeomTraits>
typename GeomTraits::FT discrete_harmonic_weight(
const CGAL::Point_2<GeomTraits>& t,
const CGAL::Point_2<GeomTraits>& r,
const CGAL::Point_2<GeomTraits>& p,
const CGAL::Point_2<GeomTraits>& q) {
const GeomTraits traits;
return discrete_harmonic_weight(t, r, p, q, traits);
}
namespace internal {
template<typename GeomTraits>
typename GeomTraits::FT discrete_harmonic_weight(
const typename GeomTraits::Point_3& t,
const typename GeomTraits::Point_3& r,
const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const GeomTraits& traits) {
using Point_2 = typename GeomTraits::Point_2;
Point_2 tf, rf, pf, qf;
internal::flatten(
traits,
t, r, p, q,
tf, rf, pf, qf);
return CGAL::Weights::
discrete_harmonic_weight(tf, rf, pf, qf, traits);
}
template<typename GeomTraits>
typename GeomTraits::FT discrete_harmonic_weight(
const CGAL::Point_3<GeomTraits>& t,
const CGAL::Point_3<GeomTraits>& r,
const CGAL::Point_3<GeomTraits>& p,
const CGAL::Point_3<GeomTraits>& q) {
const GeomTraits traits;
return discrete_harmonic_weight(t, r, p, q, traits);
}
} // namespace internal
using Vertex_range = VertexRange;
using Geom_traits = GeomTraits;
using Point_map = PointMap;
using Area_2 = typename GeomTraits::Compute_area_2;
using Squared_distance_2 = typename GeomTraits::Compute_squared_distance_2;
/// \endcond
/*!
\ingroup PkgWeightsRefBarycentricDiscreteHarmonicWeights
/// Number type.
typedef typename GeomTraits::FT FT;
\brief 2D discrete harmonic weights for polygons.
/// Point type.
typedef typename GeomTraits::Point_2 Point_2;
This class implements 2D discrete harmonic weights ( \cite cgal:bc:fhk-gcbcocp-06,
\cite cgal:pp-cdmsc-93, \cite cgal:bc:eddhls-maam-95 ) which can be computed
at any point inside a strictly convex polygon.
/// @}
Discrete harmonic weights are well-defined inside a strictly convex polygon
but they are not necessarily positive. These weights are computed analytically
using the formulation from the `discrete_harmonic_weight()`.
\tparam VertexRange
a model of `ConstRange` whose iterator type is `RandomAccessIterator`
\tparam GeomTraits
a model of `AnalyticWeightTraits_2`
\tparam PointMap
a model of `ReadablePropertyMap` whose key type is `VertexRange::value_type` and
value type is `Point_2`. The default is `CGAL::Identity_property_map`.
\cgalModels `BarycentricWeights_2`
*/
template<
typename VertexRange,
typename GeomTraits,
typename PointMap = CGAL::Identity_property_map<typename GeomTraits::Point_2> >
class Discrete_harmonic_weights_2 {
public:
/// \name Types
/// @{
/// \cond SKIP_IN_MANUAL
using Vertex_range = VertexRange;
using Geom_traits = GeomTraits;
using Point_map = PointMap;
using Area_2 = typename GeomTraits::Compute_area_2;
using Squared_distance_2 = typename GeomTraits::Compute_squared_distance_2;
/// \endcond
/// Number type.
typedef typename GeomTraits::FT FT;
/// Point type.
typedef typename GeomTraits::Point_2 Point_2;
/// @}
/// \name Initialization
/// @{
/*!
\brief initializes all internal data structures.
This class implements the behavior of discrete harmonic weights
for 2D query points inside strictly convex polygons.
\param polygon
an instance of `VertexRange` with the vertices of a strictly convex polygon
\param traits
a traits class with geometric objects, predicates, and constructions;
the default initialization is provided
\param point_map
an instance of `PointMap` that maps a vertex from `polygon` to `Point_2`;
the default initialization is provided
\pre polygon.size() >= 3
\pre polygon is simple
\pre polygon is strictly convex
*/
Discrete_harmonic_weights_2(
const VertexRange& polygon,
const GeomTraits traits = GeomTraits(),
const PointMap point_map = PointMap()) :
m_polygon(polygon),
m_traits(traits),
m_point_map(point_map),
m_area_2(m_traits.compute_area_2_object()),
m_squared_distance_2(m_traits.compute_squared_distance_2_object()) {
CGAL_precondition(
polygon.size() >= 3);
CGAL_precondition(
internal::is_simple_2(polygon, traits, point_map));
CGAL_precondition(
internal::polygon_type_2(polygon, traits, point_map) ==
internal::Polygon_type::STRICTLY_CONVEX);
resize();
}
/// @}
/// \name Access
/// @{
/*!
\brief computes 2D discrete harmonic weights.
This function fills a destination range with 2D discrete harmonic weights
computed at the `query` point with respect to the vertices of the input polygon.
The number of computed weights is equal to the number of polygon vertices.
\tparam OutIterator
a model of `OutputIterator` whose value type is `FT`
\param query
a query point
\param w_begin
the beginning of the destination range with the computed weights
\return an output iterator to the element in the destination range,
one past the last weight stored
*/
template<typename OutIterator>
OutIterator operator()(const Point_2& query, OutIterator w_begin) {
const bool normalize = false;
return operator()(query, w_begin, normalize);
}
/// @}
/// \cond SKIP_IN_MANUAL
template<typename OutIterator>
OutIterator operator()(const Point_2& query, OutIterator w_begin, const bool normalize) {
return optimal_weights(query, w_begin, normalize);
}
/// \endcond
private:
// Fields.
const VertexRange& m_polygon;
const GeomTraits m_traits;
const PointMap m_point_map;
const Area_2 m_area_2;
const Squared_distance_2 m_squared_distance_2;
std::vector<FT> r;
std::vector<FT> A;
std::vector<FT> B;
std::vector<FT> w;
// Functions.
void resize() {
r.resize(m_polygon.size());
A.resize(m_polygon.size());
B.resize(m_polygon.size());
w.resize(m_polygon.size());
}
template<typename OutputIterator>
OutputIterator optimal_weights(
const Point_2& query, OutputIterator weights, const bool normalize) {
// Get the number of vertices in the polygon.
const std::size_t n = m_polygon.size();
// Compute areas A, B, and distances r following the notation from [1].
// Split the loop to make this computation faster.
const auto& p1 = get(m_point_map, *(m_polygon.begin() + 0));
const auto& p2 = get(m_point_map, *(m_polygon.begin() + 1));
const auto& pn = get(m_point_map, *(m_polygon.begin() + (n - 1)));
r[0] = m_squared_distance_2(p1, query);
A[0] = m_area_2(p1, p2, query);
B[0] = m_area_2(pn, p2, query);
for (std::size_t i = 1; i < n - 1; ++i) {
const auto& pi0 = get(m_point_map, *(m_polygon.begin() + (i - 1)));
const auto& pi1 = get(m_point_map, *(m_polygon.begin() + (i + 0)));
const auto& pi2 = get(m_point_map, *(m_polygon.begin() + (i + 1)));
r[i] = m_squared_distance_2(pi1, query);
A[i] = m_area_2(pi1, pi2, query);
B[i] = m_area_2(pi0, pi2, query);
}
const auto& pm = get(m_point_map, *(m_polygon.begin() + (n - 2)));
r[n - 1] = m_squared_distance_2(pn, query);
A[n - 1] = m_area_2(pn, p1, query);
B[n - 1] = m_area_2(pm, p1, query);
// Compute unnormalized weights following the formula (25) with p = 2 from [1].
CGAL_assertion(A[n - 1] != FT(0) && A[0] != FT(0));
w[0] = (r[1] * A[n - 1] - r[0] * B[0] + r[n - 1] * A[0]) / (A[n - 1] * A[0]);
for (std::size_t i = 1; i < n - 1; ++i) {
CGAL_assertion(A[i - 1] != FT(0) && A[i] != FT(0));
w[i] = (r[i + 1] * A[i - 1] - r[i] * B[i] + r[i - 1] * A[i]) / (A[i - 1] * A[i]);
}
CGAL_assertion(A[n - 2] != FT(0) && A[n - 1] != FT(0));
w[n - 1] = (r[0] * A[n - 2] - r[n - 1] * B[n - 1] + r[n - 2] * A[n - 1]) / (A[n - 2] * A[n - 1]);
// Normalize if necessary.
if (normalize) {
internal::normalize(w);
}
// Return weights.
for (std::size_t i = 0; i < n; ++i) {
*(weights++) = w[i];
}
return weights;
}
};
/// \name Initialization
/// @{
/*!
\ingroup PkgWeightsRefBarycentricDiscreteHarmonicWeights
\brief initializes all internal data structures.
\brief computes 2D discrete harmonic weights for polygons.
This class implements the behavior of discrete harmonic weights
for 2D query points inside strictly convex polygons.
This function computes 2D discrete harmonic weights at a given `query` point
with respect to the vertices of a strictly convex `polygon`, that is one
weight per vertex. The weights are stored in a destination range
beginning at `w_begin`.
Internally, the class `Discrete_harmonic_weights_2` is used. If one wants to process
multiple query points, it is better to use that class. When using the free function,
internal memory is allocated for each query point, while when using the class,
it is allocated only once which is much more efficient. However, for a few query
points, it is easier to use this function. It can also be used when the processing
time is not a concern.
\tparam PointRange
a model of `ConstRange` whose iterator type is `RandomAccessIterator`
and value type is `GeomTraits::Point_2`
\tparam OutIterator
a model of `OutputIterator` whose value type is `GeomTraits::FT`
\tparam GeomTraits
a model of `AnalyticWeightTraits_2`
\param polygon
an instance of `PointRange` with 2D points which form a strictly convex polygon
\param query
a query point
\param w_begin
the beginning of the destination range with the computed weights
\param traits
a traits class with geometric objects, predicates, and constructions;
this parameter can be omitted if the traits class can be deduced from the point type
\return an output iterator to the element in the destination range,
one past the last weight stored
\param polygon an instance of `VertexRange` with the vertices of a strictly convex polygon
\param traits a traits class with geometric objects, predicates, and constructions;
the default initialization is provided
\param point_map an instance of `PointMap` that maps a vertex from `polygon` to `Point_2`;
the default initialization is provided
\pre polygon.size() >= 3
\pre polygon is simple
\pre polygon is strictly convex
*/
template<
typename PointRange,
typename OutIterator,
typename GeomTraits>
OutIterator discrete_harmonic_weights_2(
const PointRange& polygon, const typename GeomTraits::Point_2& query,
OutIterator w_begin, const GeomTraits& traits) {
Discrete_harmonic_weights_2(const VertexRange& polygon,
const GeomTraits traits = GeomTraits(),
const PointMap point_map = PointMap())
: m_polygon(polygon),
m_traits(traits),
m_point_map(point_map),
m_area_2(m_traits.compute_area_2_object()),
m_squared_distance_2(m_traits.compute_squared_distance_2_object())
{
CGAL_precondition(polygon.size() >= 3);
CGAL_precondition(internal::is_simple_2(polygon, traits, point_map));
CGAL_precondition(internal::polygon_type_2(polygon, traits, point_map) == internal::Polygon_type::STRICTLY_CONVEX);
Discrete_harmonic_weights_2<PointRange, GeomTraits>
discrete_harmonic(polygon, traits);
return discrete_harmonic(query, w_begin);
resize();
}
/// @}
/// \name Access
/// @{
/*!
\brief computes 2D discrete harmonic weights.
This function fills a destination range with 2D discrete harmonic weights
computed at the `query` point with respect to the vertices of the input polygon.
The number of computed weights is equal to the number of polygon vertices.
\tparam OutIterator a model of `OutputIterator` whose value type is `FT`
\param query a query point
\param w_begin the beginning of the destination range with the computed weights
\return an output iterator to the element in the destination range, one past the last weight stored
*/
template<typename OutIterator>
OutIterator operator()(const Point_2& query,
OutIterator w_begin)
{
const bool normalize = false;
return operator()(query, w_begin, normalize);
}
/// @}
/// \cond SKIP_IN_MANUAL
template<
typename PointRange,
typename OutIterator>
OutIterator discrete_harmonic_weights_2(
const PointRange& polygon,
const typename PointRange::value_type& query,
OutIterator w_begin) {
using Point_2 = typename PointRange::value_type;
using GeomTraits = typename Kernel_traits<Point_2>::Kernel;
const GeomTraits traits;
return discrete_harmonic_weights_2(
polygon, query, w_begin, traits);
template<typename OutIterator>
OutIterator operator()(const Point_2& query,
OutIterator w_begin,
const bool normalize)
{
return optimal_weights(query, w_begin, normalize);
}
/// \endcond
private:
const VertexRange& m_polygon;
const GeomTraits m_traits;
const PointMap m_point_map;
const Area_2 m_area_2;
const Squared_distance_2 m_squared_distance_2;
std::vector<FT> r;
std::vector<FT> A;
std::vector<FT> B;
std::vector<FT> w;
void resize()
{
r.resize(m_polygon.size());
A.resize(m_polygon.size());
B.resize(m_polygon.size());
w.resize(m_polygon.size());
}
template<typename OutputIterator>
OutputIterator optimal_weights(const Point_2& query,
OutputIterator weights,
const bool normalize)
{
// Get the number of vertices in the polygon.
const std::size_t n = m_polygon.size();
// Compute areas A, B, and distances r following the notation from [1].
// Split the loop to make this computation faster.
const auto& p1 = get(m_point_map, *(m_polygon.begin() + 0));
const auto& p2 = get(m_point_map, *(m_polygon.begin() + 1));
const auto& pn = get(m_point_map, *(m_polygon.begin() + (n - 1)));
r[0] = m_squared_distance_2(p1, query);
A[0] = m_area_2(p1, p2, query);
B[0] = m_area_2(pn, p2, query);
for (std::size_t i = 1; i < n - 1; ++i)
{
const auto& pi0 = get(m_point_map, *(m_polygon.begin() + (i - 1)));
const auto& pi1 = get(m_point_map, *(m_polygon.begin() + (i + 0)));
const auto& pi2 = get(m_point_map, *(m_polygon.begin() + (i + 1)));
r[i] = m_squared_distance_2(pi1, query);
A[i] = m_area_2(pi1, pi2, query);
B[i] = m_area_2(pi0, pi2, query);
}
const auto& pm = get(m_point_map, *(m_polygon.begin() + (n - 2)));
r[n - 1] = m_squared_distance_2(pn, query);
A[n - 1] = m_area_2(pn, p1, query);
B[n - 1] = m_area_2(pm, p1, query);
// Compute unnormalized weights following the formula (25) with p = 2 from [1].
CGAL_assertion(A[n - 1] != FT(0) && A[0] != FT(0));
w[0] = (r[1] * A[n - 1] - r[0] * B[0] + r[n - 1] * A[0]) / (A[n - 1] * A[0]);
for (std::size_t i = 1; i < n - 1; ++i)
{
CGAL_assertion(A[i - 1] != FT(0) && A[i] != FT(0));
w[i] = (r[i + 1] * A[i - 1] - r[i] * B[i] + r[i - 1] * A[i]) / (A[i - 1] * A[i]);
}
CGAL_assertion(A[n - 2] != FT(0) && A[n - 1] != FT(0));
w[n - 1] = (r[0] * A[n - 2] - r[n - 1] * B[n - 1] + r[n - 2] * A[n - 1]) / (A[n - 2] * A[n - 1]);
// Normalize if necessary.
if (normalize)
internal::normalize(w);
// Return weights.
for (std::size_t i = 0; i < n; ++i)
*(weights++) = w[i];
return weights;
}
};
/*!
\ingroup PkgWeightsRefBarycentricDiscreteHarmonicWeights
\brief computes 2D discrete harmonic weights for polygons.
This function computes 2D discrete harmonic weights at a given `query` point
with respect to the vertices of a strictly convex `polygon`, that is one
weight per vertex. The weights are stored in a destination range
beginning at `w_begin`.
Internally, the class `Discrete_harmonic_weights_2` is used. If one wants to process
multiple query points, it is better to use that class. When using the free function,
internal memory is allocated for each query point, while when using the class,
it is allocated only once which is much more efficient. However, for a few query
points, it is easier to use this function. It can also be used when the processing
time is not a concern.
\tparam PointRange a model of `ConstRange` whose iterator type is `RandomAccessIterator`
and value type is `GeomTraits::Point_2`
\tparam OutIterator a model of `OutputIterator` whose value type is `GeomTraits::FT`
\tparam GeomTraits a model of `AnalyticWeightTraits_2`
\param polygon an instance of `PointRange` with 2D points which form a strictly convex polygon
\param query a query point
\param w_begin the beginning of the destination range with the computed weights
\param traits a traits class with geometric objects, predicates, and constructions;
this parameter can be omitted if the traits class can be deduced from the point type
\return an output iterator to the element in the destination range, one past the last weight stored
\pre `polygon.size() >= 3`
\pre `polygon` is simple
\pre `polygon` is strictly convex
*/
template<typename PointRange,
typename OutIterator,
typename GeomTraits>
OutIterator discrete_harmonic_weights_2(const PointRange& polygon,
const typename GeomTraits::Point_2& query,
OutIterator w_begin,
const GeomTraits& traits)
{
Discrete_harmonic_weights_2<PointRange, GeomTraits> discrete_harmonic(polygon, traits);
return discrete_harmonic(query, w_begin);
}
/// \cond SKIP_IN_MANUAL
template<typename PointRange,
typename OutIterator>
OutIterator discrete_harmonic_weights_2(const PointRange& polygon,
const typename PointRange::value_type& query,
OutIterator w_begin)
{
using Point_2 = typename PointRange::value_type;
using GeomTraits = typename Kernel_traits<Point_2>::Kernel;
const GeomTraits traits;
return discrete_harmonic_weights_2(polygon, query, w_begin, traits);
}
/// \endcond
} // namespace Weights
} // namespace CGAL

View File

@ -14,7 +14,12 @@
#ifndef CGAL_WEIGHTS_INTERNAL_POLYGON_UTILS_2_H
#define CGAL_WEIGHTS_INTERNAL_POLYGON_UTILS_2_H
// STL includes.
#include <CGAL/utils.h>
#include <CGAL/assertions.h>
#include <CGAL/number_utils.h>
#include <CGAL/property_map.h>
#include <CGAL/Polygon_2_algorithms.h>
#include <cmath>
#include <string>
#include <memory>
@ -23,345 +28,329 @@
#include <iterator>
#include <iostream>
// CGAL includes.
#include <CGAL/utils.h>
#include <CGAL/assertions.h>
#include <CGAL/number_utils.h>
#include <CGAL/property_map.h>
#include <CGAL/Polygon_2_algorithms.h>
namespace CGAL {
namespace Weights {
namespace internal {
enum class Edge_case {
enum class Edge_case {
EXTERIOR = 0, // exterior part of the polygon
BOUNDARY = 1, // boundary part of the polygon
INTERIOR = 2 // interior part of the polygon
};
EXTERIOR = 0, // exterior part of the polygon
BOUNDARY = 1, // boundary part of the polygon
INTERIOR = 2 // interior part of the polygon
};
// VertexRange type enum.
enum class Polygon_type {
// VertexRange type enum.
enum class Polygon_type
{
CONCAVE = 0, // Concave polygon = non-convex polygon.
WEAKLY_CONVEX = 1, // This is a convex polygon with collinear vertices.
STRICTLY_CONVEX = 2 // This is a convex polygon without collinear vertices.
};
// Concave polygon = non-convex polygon.
CONCAVE = 0,
// This function is taken from the Polygon_2_algorithms.h header.
// But it is updated to support property maps.
template<class Point_2,
class Orientation_2,
class CompareX_2>
int which_side_in_slab_2(const Point_2& query,
const Point_2& low, const Point_2& high,
const Orientation_2& orientation_2,
const CompareX_2& compare_x_2)
{
const Comparison_result low_x_comp_res = compare_x_2(query, low);
const Comparison_result high_x_comp_res = compare_x_2(query, high);
// This is a convex polygon with collinear vertices.
WEAKLY_CONVEX = 1,
// This is a convex polygon without collinear vertices.
STRICTLY_CONVEX = 2
};
// This function is taken from the Polygon_2_algorithms.h header.
// But it is updated to support property maps.
template<
class Point_2,
class Orientation_2,
class CompareX_2>
int which_side_in_slab_2(
const Point_2& query, const Point_2& low, const Point_2& high,
const Orientation_2& orientation_2, const CompareX_2& compare_x_2) {
const auto low_x_comp_res = compare_x_2(query, low);
const auto high_x_comp_res = compare_x_2(query, high);
if (low_x_comp_res == CGAL::SMALLER) {
if (high_x_comp_res == CGAL::SMALLER) {
return -1;
}
} else {
switch (high_x_comp_res) {
case CGAL::LARGER: return 1;
case CGAL::SMALLER: break;
case CGAL::EQUAL: return (low_x_comp_res == CGAL::EQUAL) ? 0 : 1;
}
if (low_x_comp_res == CGAL::SMALLER) {
if (high_x_comp_res == CGAL::SMALLER) {
return -1;
}
switch (orientation_2(low, query, high)) {
case CGAL::LEFT_TURN: return 1;
case CGAL::RIGHT_TURN: return -1;
default: return 0;
} else {
switch (high_x_comp_res) {
case CGAL::LARGER: return 1;
case CGAL::SMALLER: break;
case CGAL::EQUAL: return (low_x_comp_res == CGAL::EQUAL) ? 0 : 1;
}
}
// This function is taken from the Polygon_2_algorithms.h header.
// But it is updated to support property maps.
template<
typename VertexRange,
typename GeomTraits,
typename PointMap>
Edge_case bounded_side_2(
const VertexRange& polygon, const typename GeomTraits::Point_2& query,
const GeomTraits& traits, const PointMap point_map) {
const auto first = polygon.begin();
const auto last = polygon.end();
auto curr = first;
if (curr == last) {
return Edge_case::EXTERIOR;
}
auto next = curr; ++next;
if (next == last) {
return Edge_case::EXTERIOR;
}
const auto compare_x_2 = traits.compare_x_2_object();
const auto compare_y_2 = traits.compare_y_2_object();
const auto orientation_2 = traits.orientation_2_object();
bool is_inside = false;
auto curr_y_comp_res = compare_y_2(get(point_map, *curr), query);
// Check if the segment (curr, next) intersects
// the ray { (t, query.y()) | t >= query.x() }.
do {
const auto& currp = get(point_map, *curr);
const auto& nextp = get(point_map, *next);
auto next_y_comp_res = compare_y_2(nextp, query);
switch (curr_y_comp_res) {
case CGAL::SMALLER:
switch (next_y_comp_res) {
case CGAL::SMALLER:
break;
case CGAL::EQUAL:
switch (compare_x_2(query, nextp)) {
case CGAL::SMALLER: is_inside = !is_inside; break;
case CGAL::EQUAL: return Edge_case::BOUNDARY;
case CGAL::LARGER: break;
}
break;
case CGAL::LARGER:
switch (which_side_in_slab_2(
query, currp, nextp, orientation_2, compare_x_2)) {
case -1: is_inside = !is_inside; break;
case 0: return Edge_case::BOUNDARY;
}
break;
}
break;
case CGAL::EQUAL:
switch (next_y_comp_res) {
case CGAL::SMALLER:
switch (compare_x_2(query, currp)) {
case CGAL::SMALLER: is_inside = !is_inside; break;
case CGAL::EQUAL: return Edge_case::BOUNDARY;
case CGAL::LARGER: break;
}
break;
case CGAL::EQUAL:
switch (compare_x_2(query, currp)) {
case CGAL::SMALLER:
if (compare_x_2(query, nextp) != CGAL::SMALLER) {
return Edge_case::BOUNDARY;
}
break;
case CGAL::EQUAL: return Edge_case::BOUNDARY;
case CGAL::LARGER:
if (compare_x_2(query, nextp) != CGAL::LARGER) {
return Edge_case::BOUNDARY;
}
break;
}
break;
case CGAL::LARGER:
if (compare_x_2(query, currp) == CGAL::EQUAL) {
return Edge_case::BOUNDARY;
}
break;
}
break;
case CGAL::LARGER:
switch (next_y_comp_res) {
case CGAL::SMALLER:
switch (which_side_in_slab_2(
query, nextp, currp, orientation_2, compare_x_2)) {
case -1: is_inside = !is_inside; break;
case 0: return Edge_case::BOUNDARY;
}
break;
case CGAL::EQUAL:
if (compare_x_2(query, nextp) == CGAL::EQUAL) {
return Edge_case::BOUNDARY;
}
break;
case CGAL::LARGER:
break;
}
break;
}
curr = next;
curr_y_comp_res = next_y_comp_res;
++next;
if (next == last) {
next = first;
}
} while (curr != first);
return is_inside ? Edge_case::INTERIOR : Edge_case::EXTERIOR;
switch (orientation_2(low, query, high)) {
case CGAL::LEFT_TURN: return 1;
case CGAL::RIGHT_TURN: return -1;
default: return 0;
}
}
// This function is taken from the Polygon_2_algorithms.h header.
// But it is updated to support property maps.
template<
typename VertexRange,
typename GeomTraits,
typename PointMap>
bool is_convex_2(
const VertexRange& polygon, const GeomTraits traits, const PointMap point_map) {
// This function is taken from the Polygon_2_algorithms.h header.
// But it is updated to support property maps.
template<typename VertexRange,
typename GeomTraits,
typename PointMap>
Edge_case bounded_side_2(const VertexRange& polygon,
const typename GeomTraits::Point_2& query,
const GeomTraits& traits,
const PointMap point_map)
{
const auto first = polygon.begin();
const auto last = polygon.end();
auto first = polygon.begin();
const auto last = polygon.end();
auto curr = first;
if (curr == last)
return Edge_case::EXTERIOR;
auto prev = first;
if (prev == last) {
return true;
}
auto next = curr;
++next;
if (next == last)
return Edge_case::EXTERIOR;
auto curr = prev; ++curr;
if (curr == last) {
return true;
}
auto compare_x_2 = traits.compare_x_2_object();
auto compare_y_2 = traits.compare_y_2_object();
auto orientation_2 = traits.orientation_2_object();
auto next = curr; ++next;
if (next == last) {
return true;
}
bool is_inside = false;
Comparison_result curr_y_comp_res = compare_y_2(get(point_map, *curr), query);
const auto equal_2 = traits.equal_2_object();
while (equal_2(get(point_map, *prev), get(point_map, *curr))) {
curr = next; ++next;
if (next == last) {
return true;
}
}
// Check if the segment (curr, next) intersects
// the ray { (t, query.y()) | t >= query.x() }.
do {
const auto& currp = get(point_map, *curr);
const auto& nextp = get(point_map, *next);
const auto less_xy_2 = traits.less_xy_2_object();
const auto orientation_2 = traits.orientation_2_object();
bool has_clockwise_triplets = false;
bool has_counterclockwise_triplets = false;
bool order = less_xy_2(
get(point_map, *prev), get(point_map, *curr));
int num_order_changes = 0;
do {
switch_orient:
switch (orientation_2(
get(point_map, *prev), get(point_map, *curr), get(point_map, *next))) {
case CGAL::CLOCKWISE:
has_clockwise_triplets = true;
break;
case CGAL::COUNTERCLOCKWISE:
has_counterclockwise_triplets = true;
break;
case CGAL::ZERO: {
if (equal_2(
get(point_map, *curr),
get(point_map, *next))) {
if (next == first) {
first = curr;
Comparison_result next_y_comp_res = compare_y_2(nextp, query);
switch (curr_y_comp_res) {
case CGAL::SMALLER:
switch (next_y_comp_res) {
case CGAL::SMALLER:
break;
case CGAL::EQUAL:
switch (compare_x_2(query, nextp)) {
case CGAL::SMALLER: is_inside = !is_inside; break;
case CGAL::EQUAL: return Edge_case::BOUNDARY;
case CGAL::LARGER: break;
}
++next;
if (next == last) {
next = first;
break;
case CGAL::LARGER:
switch (which_side_in_slab_2(
query, currp, nextp, orientation_2, compare_x_2)) {
case -1: is_inside = !is_inside; break;
case 0: return Edge_case::BOUNDARY;
}
goto switch_orient;
}
break;
break;
}
}
break;
case CGAL::EQUAL:
switch (next_y_comp_res) {
case CGAL::SMALLER:
switch (compare_x_2(query, currp)) {
case CGAL::SMALLER: is_inside = !is_inside; break;
case CGAL::EQUAL: return Edge_case::BOUNDARY;
case CGAL::LARGER: break;
}
break;
case CGAL::EQUAL:
switch (compare_x_2(query, currp)) {
case CGAL::SMALLER:
if (compare_x_2(query, nextp) != CGAL::SMALLER) {
return Edge_case::BOUNDARY;
}
break;
case CGAL::EQUAL: return Edge_case::BOUNDARY;
case CGAL::LARGER:
if (compare_x_2(query, nextp) != CGAL::LARGER) {
return Edge_case::BOUNDARY;
}
break;
}
break;
case CGAL::LARGER:
if (compare_x_2(query, currp) == CGAL::EQUAL) {
return Edge_case::BOUNDARY;
}
break;
}
break;
case CGAL::LARGER:
switch (next_y_comp_res) {
case CGAL::SMALLER:
switch (which_side_in_slab_2(
query, nextp, currp, orientation_2, compare_x_2)) {
case -1: is_inside = !is_inside; break;
case 0: return Edge_case::BOUNDARY;
}
break;
case CGAL::EQUAL:
if (compare_x_2(query, nextp) == CGAL::EQUAL) {
return Edge_case::BOUNDARY;
}
break;
case CGAL::LARGER:
break;
}
break;
}
const bool new_order = less_xy_2(
get(point_map, *curr), get(point_map, *next));
curr = next;
curr_y_comp_res = next_y_comp_res;
++next;
if (next == last) {
next = first;
}
} while (curr != first);
if (order != new_order) {
num_order_changes++;
}
return is_inside ? Edge_case::INTERIOR : Edge_case::EXTERIOR;
}
if (num_order_changes > 2) {
return false;
}
// This function is taken from the Polygon_2_algorithms.h header.
// But it is updated to support property maps.
template<typename VertexRange,
typename GeomTraits,
typename PointMap>
bool is_convex_2(const VertexRange& polygon,
const GeomTraits traits,
const PointMap point_map)
{
auto first = polygon.begin();
const auto last = polygon.end();
if (has_clockwise_triplets && has_counterclockwise_triplets) {
return false;
}
prev = curr;
curr = next;
++next;
if (next == last) {
next = first;
}
order = new_order;
} while (prev != first);
auto prev = first;
if (prev == last)
return true;
}
// This function is taken from the Polygon_2_algorithms.h header.
// But it is updated to support property maps.
template<
typename VertexRange,
typename GeomTraits,
typename PointMap>
bool is_simple_2(
const VertexRange& polygon, const GeomTraits traits, const PointMap point_map) {
auto curr = prev;
++curr;
if (curr == last)
return true;
const auto first = polygon.begin();
const auto last = polygon.end();
if (first == last) {
auto next = curr;
++next;
if (next == last)
return true;
auto equal_2 = traits.equal_2_object();
while (equal_2(get(point_map, *prev), get(point_map, *curr)))
{
curr = next; ++next;
if (next == last)
return true;
}
std::vector<typename GeomTraits::Point_2> poly;
poly.reserve(polygon.size());
for (const auto& vertex : polygon) {
poly.push_back(get(point_map, vertex));
}
return CGAL::is_simple_2(poly.begin(), poly.end(), traits);
}
template<
typename VertexRange,
typename GeomTraits,
typename PointMap>
Polygon_type polygon_type_2(
const VertexRange& polygon, const GeomTraits traits, const PointMap point_map) {
auto less_xy_2 = traits.less_xy_2_object();
auto orientation_2 = traits.orientation_2_object();
const auto collinear_2 =
traits.collinear_2_object();
CGAL_precondition(polygon.size() >= 3);
bool has_clockwise_triplets = false;
bool has_counterclockwise_triplets = false;
bool order = less_xy_2(get(point_map, *prev), get(point_map, *curr));
int num_order_changes = 0;
// First, test the polygon on convexity.
if (is_convex_2(polygon, traits, point_map)) {
do
{
switch_orient:
switch (orientation_2(get(point_map, *prev), get(point_map, *curr), get(point_map, *next)))
{
case CGAL::CLOCKWISE:
has_clockwise_triplets = true;
break;
case CGAL::COUNTERCLOCKWISE:
has_counterclockwise_triplets = true;
break;
case CGAL::ZERO: {
if (equal_2(get(point_map, *curr),
get(point_map, *next)))
{
if (next == first)
first = curr;
// Test all the consequent triplets of polygon vertices on collinearity.
// In case we find at least one, return WEAKLY_CONVEX polygon.
const std::size_t n = polygon.size();
for (std::size_t i = 0; i < n; ++i) {
const auto& p1 = get(point_map, *(polygon.begin() + i));
++next;
if (next == last)
next = first;
const std::size_t im = (i + n - 1) % n;
const std::size_t ip = (i + 1) % n;
const auto& p0 = get(point_map, *(polygon.begin() + im));
const auto& p2 = get(point_map, *(polygon.begin() + ip));
if (collinear_2(p0, p1, p2)) {
return Polygon_type::WEAKLY_CONVEX;
goto switch_orient;
}
break;
}
// Otherwise, return STRICTLY_CONVEX polygon.
return Polygon_type::STRICTLY_CONVEX;
}
// Otherwise, return CONCAVE polygon.
return Polygon_type::CONCAVE;
const bool new_order = less_xy_2(get(point_map, *curr), get(point_map, *next));
if (order != new_order)
num_order_changes++;
if (num_order_changes > 2)
return false;
if (has_clockwise_triplets && has_counterclockwise_triplets)
return false;
prev = curr;
curr = next;
++next;
if (next == last)
next = first;
order = new_order;
} while (prev != first);
return true;
}
// This function is taken from the Polygon_2_algorithms.h header.
// But it is updated to support property maps.
template<typename VertexRange,
typename GeomTraits,
typename PointMap>
bool is_simple_2(const VertexRange& polygon,
const GeomTraits traits,
const PointMap point_map)
{
const auto first = polygon.begin();
const auto last = polygon.end();
if (first == last)
return true;
std::vector<typename GeomTraits::Point_2> poly;
poly.reserve(polygon.size());
for (const auto& vertex : polygon)
poly.push_back(get(point_map, vertex));
return CGAL::is_simple_2(poly.begin(), poly.end(), traits);
}
template<typename VertexRange,
typename GeomTraits,
typename PointMap>
Polygon_type polygon_type_2(const VertexRange& polygon,
const GeomTraits traits,
const PointMap point_map)
{
auto collinear_2 = traits.collinear_2_object();
CGAL_precondition(polygon.size() >= 3);
// First, test the polygon on convexity.
if (is_convex_2(polygon, traits, point_map))
{
// Test all the consequent triplets of polygon vertices on collinearity.
// In case we find at least one, return WEAKLY_CONVEX polygon.
const std::size_t n = polygon.size();
for (std::size_t i = 0; i < n; ++i)
{
const auto& p1 = get(point_map, *(polygon.begin() + i));
const std::size_t im = (i + n - 1) % n;
const std::size_t ip = (i + 1) % n;
const auto& p0 = get(point_map, *(polygon.begin() + im));
const auto& p2 = get(point_map, *(polygon.begin() + ip));
if (collinear_2(p0, p1, p2))
return Polygon_type::WEAKLY_CONVEX;
}
// Otherwise, return STRICTLY_CONVEX polygon.
return Polygon_type::STRICTLY_CONVEX;
}
// Otherwise, return CONCAVE polygon.
return Polygon_type::CONCAVE;
}
} // namespace internal
} // namespace Weights
} // namespace CGAL

File diff suppressed because it is too large Load Diff

View File

@ -14,219 +14,167 @@
#ifndef CGAL_INVERSE_DISTANCE_WEIGHTS_H
#define CGAL_INVERSE_DISTANCE_WEIGHTS_H
// Internal includes.
#include <CGAL/Weights/internal/utils.h>
#include <CGAL/Point_2.h>
#include <CGAL/Point_3.h>
namespace CGAL {
namespace Weights {
/// \cond SKIP_IN_MANUAL
namespace inverse_distance_ns {
/// \cond SKIP_IN_MANUAL
namespace inverse_distance_ns {
template<typename FT>
FT weight(const FT d) {
template<typename FT>
FT weight(const FT d)
{
FT w = FT(0);
CGAL_precondition(!is_zero(d));
if (!is_zero(d))
w = FT(1) / d;
FT w = FT(0);
CGAL_precondition(d != FT(0));
if (d != FT(0)) {
w = FT(1) / d;
}
return w;
}
}
/// \endcond
return w;
}
#if defined(DOXYGEN_RUNNING)
} // namespace inverse_distance_ns
/*!
\ingroup PkgWeightsRefInverseDistanceWeights
/// \endcond
\brief computes the inverse distance weight in 2D using the points `p` and `q`,
given a traits class `traits` with geometric objects, predicates, and constructions.
*/
template<typename GeomTraits>
typename GeomTraits::FT inverse_distance_weight(
const typename GeomTraits::Point_2&,
const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2&,
const typename GeomTraits::Point_2& q,
const GeomTraits& traits) { }
/*!
\ingroup PkgWeightsRefInverseDistanceWeights
\brief computes the inverse distance weight in 2D using the points `p` and `q`.
\tparam GeomTraits a model of `AnalyticWeightTraits_2`
*/
template<typename GeomTraits>
typename GeomTraits::FT inverse_distance_weight(const typename GeomTraits::Point_2&,
const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2&,
const typename GeomTraits::Point_2& q,
const GeomTraits& traits)
{
using FT = typename GeomTraits::FT;
/*!
\ingroup PkgWeightsRefInverseDistanceWeights
const FT d = internal::distance_2(p, q, traits);
return inverse_distance_ns::weight(d);
}
\brief computes the inverse distance weight in 3D using the points `p` and `q`,
given a traits class `traits` with geometric objects, predicates, and constructions.
*/
template<typename GeomTraits>
typename GeomTraits::FT inverse_distance_weight(
const typename GeomTraits::Point_3&,
const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3&,
const typename GeomTraits::Point_3& q,
const GeomTraits& traits) { }
/*!
\ingroup PkgWeightsRefInverseDistanceWeights
\brief computes the inverse distance weight in 2D using the points `p` and `q`.
\tparam Kernel a model of `Kernel`
*/
template<typename Kernel>
#ifdef DOXYGEN_RUNNING
typename Kernel::FT inverse_distance_weight(const CGAL::Point_2<Kernel>&,
const CGAL::Point_2<Kernel>& p,
const CGAL::Point_2<Kernel>&,
const CGAL::Point_2<Kernel>& q)
#else
typename Kernel::FT inverse_distance_weight(const CGAL::Point_2<Kernel>& stub_l,
const CGAL::Point_2<Kernel>& p,
const CGAL::Point_2<Kernel>& stub_r,
const CGAL::Point_2<Kernel>& q)
#endif
{
const Kernel traits;
return inverse_distance_weight(stub_l, p, stub_r, q, traits);
}
/*!
\ingroup PkgWeightsRefInverseDistanceWeights
/*!
\ingroup PkgWeightsRefInverseDistanceWeights
\brief computes the inverse distance weight in 2D using the points `p` and `q`.
\tparam GeomTraits a model of `AnalyticWeightTraits_2`
*/
template<typename GeomTraits>
typename GeomTraits::FT inverse_distance_weight(const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const GeomTraits& traits)
{
typename GeomTraits::Point_2 stub;
return inverse_distance_weight(stub, p, stub, q, traits);
}
\brief computes the inverse distance weight in 2D using the points `p` and `q`,
which are parameterized by a `Kernel` K.
*/
template<typename K>
typename K::FT inverse_distance_weight(
const CGAL::Point_2<K>&,
const CGAL::Point_2<K>& p,
const CGAL::Point_2<K>&,
const CGAL::Point_2<K>& q) { }
/*!
\ingroup PkgWeightsRefInverseDistanceWeights
\brief computes the inverse distance weight in 2D using the points `p` and `q`.
\tparam Kernel a model of `Kernel`
*/
template<typename Kernel>
typename Kernel::FT inverse_distance_weight(const CGAL::Point_2<Kernel>& p,
const CGAL::Point_2<Kernel>& q)
{
CGAL::Point_2<Kernel> stub;
return inverse_distance_weight(stub, p, stub, q);
}
/*!
\ingroup PkgWeightsRefInverseDistanceWeights
// 3D ==============================================================================================
\brief computes the inverse distance weight in 3D using the points `p` and `q`,
which are parameterized by a `Kernel` K.
*/
template<typename K>
typename K::FT inverse_distance_weight(
const CGAL::Point_3<K>&,
const CGAL::Point_3<K>& p,
const CGAL::Point_3<K>&,
const CGAL::Point_3<K>& q) { }
/*!
\ingroup PkgWeightsRefInverseDistanceWeights
\brief computes the inverse distance weight in 3D using the points `p` and `q`.
\tparam GeomTraits a model of `AnalyticWeightTraits_3`
*/
template<typename GeomTraits>
typename GeomTraits::FT inverse_distance_weight(const typename GeomTraits::Point_3&,
const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3&,
const typename GeomTraits::Point_3& q,
const GeomTraits& traits)
{
using FT = typename GeomTraits::FT;
/*!
\ingroup PkgWeightsRefInverseDistanceWeights
const FT d = internal::distance_3(p, q, traits);
return inverse_distance_ns::weight(d);
}
\brief computes the inverse distance weight in 2D using the points `p` and `q`,
given a traits class `traits` with geometric objects, predicates, and constructions.
*/
template<typename GeomTraits>
typename GeomTraits::FT inverse_distance_weight(
const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const GeomTraits& traits) { }
/*!
\ingroup PkgWeightsRefInverseDistanceWeights
\brief computes the inverse distance weight in 3D using the points `p` and `q`.
\tparam Kernel a model of `Kernel`
*/
template<typename Kernel>
#ifdef DOXYGEN_RUNNING
typename Kernel::FT inverse_distance_weight(const CGAL::Point_3<Kernel>&,
const CGAL::Point_3<Kernel>& p,
const CGAL::Point_3<Kernel>&,
const CGAL::Point_3<Kernel>& q)
#else
typename Kernel::FT inverse_distance_weight(const CGAL::Point_3<Kernel>& stub_l,
const CGAL::Point_3<Kernel>& p,
const CGAL::Point_3<Kernel>& stub_r,
const CGAL::Point_3<Kernel>& q)
#endif
{
const Kernel traits;
return inverse_distance_weight(stub_l, p, stub_r, q, traits);
}
/*!
\ingroup PkgWeightsRefInverseDistanceWeights
/*!
\ingroup PkgWeightsRefInverseDistanceWeights
\brief computes the inverse distance weight in 3D using the points `p` and `q`.
\tparam GeomTraits a model of `AnalyticWeightTraits_3`
*/
template<typename GeomTraits>
typename GeomTraits::FT inverse_distance_weight(const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const GeomTraits& traits)
{
typename GeomTraits::Point_3 stub;
return inverse_distance_weight(stub, p, stub, q, traits);
}
\brief computes the inverse distance weight in 3D using the points `p` and `q`,
given a traits class `traits` with geometric objects, predicates, and constructions.
*/
template<typename GeomTraits>
typename GeomTraits::FT inverse_distance_weight(
const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const GeomTraits& traits) { }
/*!
\ingroup PkgWeightsRefInverseDistanceWeights
\brief computes the inverse distance weight in 2D using the points `p` and `q`,
which are parameterized by a `Kernel` K.
*/
template<typename K>
typename K::FT inverse_distance_weight(
const CGAL::Point_2<K>& p,
const CGAL::Point_2<K>& q) { }
/*!
\ingroup PkgWeightsRefInverseDistanceWeights
\brief computes the inverse distance weight in 3D using the points `p` and `q`,
which are parameterized by a `Kernel` K.
*/
template<typename K>
typename K::FT inverse_distance_weight(
const CGAL::Point_3<K>& p,
const CGAL::Point_3<K>& q) { }
#endif // DOXYGEN_RUNNING
/// \cond SKIP_IN_MANUAL
template<typename GeomTraits>
typename GeomTraits::FT inverse_distance_weight(
const typename GeomTraits::Point_2&,
const typename GeomTraits::Point_2& r,
const typename GeomTraits::Point_2&,
const typename GeomTraits::Point_2& q,
const GeomTraits& traits) {
using FT = typename GeomTraits::FT;
const FT d = internal::distance_2(traits, q, r);
return inverse_distance_ns::weight(d);
}
template<typename GeomTraits>
typename GeomTraits::FT inverse_distance_weight(
const CGAL::Point_2<GeomTraits>& t,
const CGAL::Point_2<GeomTraits>& r,
const CGAL::Point_2<GeomTraits>& p,
const CGAL::Point_2<GeomTraits>& q) {
const GeomTraits traits;
return inverse_distance_weight(t, r, p, q, traits);
}
template<typename GeomTraits>
typename GeomTraits::FT inverse_distance_weight(
const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const GeomTraits& traits) {
typename GeomTraits::Point_2 stub;
return inverse_distance_weight(stub, p, stub, q, traits);
}
template<typename GeomTraits>
typename GeomTraits::FT inverse_distance_weight(
const CGAL::Point_2<GeomTraits>& p,
const CGAL::Point_2<GeomTraits>& q) {
CGAL::Point_2<GeomTraits> stub;
return inverse_distance_weight(stub, p, stub, q);
}
template<typename GeomTraits>
typename GeomTraits::FT inverse_distance_weight(
const typename GeomTraits::Point_3&,
const typename GeomTraits::Point_3& r,
const typename GeomTraits::Point_3&,
const typename GeomTraits::Point_3& q,
const GeomTraits& traits) {
using FT = typename GeomTraits::FT;
const FT d = internal::distance_3(traits, q, r);
return inverse_distance_ns::weight(d);
}
template<typename GeomTraits>
typename GeomTraits::FT inverse_distance_weight(
const CGAL::Point_3<GeomTraits>& t,
const CGAL::Point_3<GeomTraits>& r,
const CGAL::Point_3<GeomTraits>& p,
const CGAL::Point_3<GeomTraits>& q) {
const GeomTraits traits;
return inverse_distance_weight(t, r, p, q, traits);
}
template<typename GeomTraits>
typename GeomTraits::FT inverse_distance_weight(
const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const GeomTraits& traits) {
typename GeomTraits::Point_3 stub;
return inverse_distance_weight(stub, p, stub, q, traits);
}
template<typename GeomTraits>
typename GeomTraits::FT inverse_distance_weight(
const CGAL::Point_3<GeomTraits>& p,
const CGAL::Point_3<GeomTraits>& q) {
CGAL::Point_3<GeomTraits> stub;
return inverse_distance_weight(stub, p, stub, q);
}
/// \endcond
/*!
\ingroup PkgWeightsRefInverseDistanceWeights
\brief computes the inverse distance weight in 3D using the points `p` and `q`.
\tparam Kernel a model of `Kernel`
*/
template<typename Kernel>
typename Kernel::FT inverse_distance_weight(const CGAL::Point_3<Kernel>& p,
const CGAL::Point_3<Kernel>& q)
{
CGAL::Point_3<Kernel> stub;
return inverse_distance_weight(stub, p, stub, q);
}
} // namespace Weights
} // namespace CGAL

View File

@ -14,498 +14,457 @@
#ifndef CGAL_MEAN_VALUE_WEIGHTS_H
#define CGAL_MEAN_VALUE_WEIGHTS_H
// Internal includes.
#include <CGAL/Weights/internal/utils.h>
#include <CGAL/Weights/internal/polygon_utils_2.h>
#include <CGAL/Kernel_traits.h>
#include <CGAL/Point_2.h>
#include <CGAL/Point_3.h>
#include <CGAL/property_map.h>
#include <vector>
namespace CGAL {
namespace Weights {
/// \cond SKIP_IN_MANUAL
namespace mean_value_ns {
/// \cond SKIP_IN_MANUAL
template<typename FT>
FT sign_of_weight(const FT A1, const FT A2, const FT B) {
namespace mean_value_ns {
if (A1 > FT(0) && A2 > FT(0) && B <= FT(0)) {
return +FT(1);
}
if (A1 < FT(0) && A2 < FT(0) && B >= FT(0)) {
return -FT(1);
}
if (B > FT(0)) {
return +FT(1);
}
if (B < FT(0)) {
return -FT(1);
}
return FT(0);
}
template<typename FT>
FT sign_of_weight(const FT A0, const FT A2, const FT B)
{
if (A0 > FT(0) && A2 > FT(0) && B <= FT(0))
return +FT(1);
template<typename GeomTraits>
typename GeomTraits::FT weight(
const GeomTraits& traits,
const typename GeomTraits::FT r1,
const typename GeomTraits::FT r2,
const typename GeomTraits::FT r3,
const typename GeomTraits::FT D1,
const typename GeomTraits::FT D2,
const typename GeomTraits::FT D,
const typename GeomTraits::FT sign) {
if (A0 < FT(0) && A2 < FT(0) && B >= FT(0))
return -FT(1);
using FT = typename GeomTraits::FT;
using Get_sqrt = internal::Get_sqrt<GeomTraits>;
const auto sqrt = Get_sqrt::sqrt_object(traits);
if (B > FT(0))
return +FT(1);
const FT P1 = r1 * r2 + D1;
const FT P2 = r2 * r3 + D2;
if (B < FT(0))
return -FT(1);
FT w = FT(0);
CGAL_precondition(P1 != FT(0) && P2 != FT(0));
const FT prod = P1 * P2;
if (prod != FT(0)) {
const FT inv = FT(1) / prod;
w = FT(2) * (r1 * r3 - D) * inv;
CGAL_assertion(w >= FT(0));
w = sqrt(w);
}
w *= FT(2); w *= sign;
return w;
}
return FT(0);
}
template<typename GeomTraits>
typename GeomTraits::FT weight(const typename GeomTraits::FT d0,
const typename GeomTraits::FT d2,
const typename GeomTraits::FT d,
const typename GeomTraits::FT D0,
const typename GeomTraits::FT D2,
const typename GeomTraits::FT D,
const typename GeomTraits::FT sign,
const GeomTraits& traits)
{
using FT = typename GeomTraits::FT;
using Get_sqrt = internal::Get_sqrt<GeomTraits>;
auto sqrt = Get_sqrt::sqrt_object(traits);
const FT P1 = d * d0 + D0;
const FT P2 = d * d2 + D2;
FT w = FT(0);
CGAL_precondition(!is_zero(P1) && !is_zero(P2));
const FT prod = P1 * P2;
if (!is_zero(prod))
{
w = FT(2) * (d0 * d2 - D) / prod;
CGAL_assertion(w >= FT(0));
w = sqrt(w);
}
/// \endcond
#if defined(DOXYGEN_RUNNING)
w *= sign * FT(2);
return w;
}
/*!
\ingroup PkgWeightsRefMeanValueWeights
} // namespace mean_value_ns
\brief computes the mean value weight in 2D at `q` using the points `p0`, `p1`,
and `p2`, given a traits class `traits` with geometric objects, predicates, and constructions.
*/
template<typename GeomTraits>
typename GeomTraits::FT mean_value_weight(
const typename GeomTraits::Point_2& p0,
const typename GeomTraits::Point_2& p1,
const typename GeomTraits::Point_2& p2,
const typename GeomTraits::Point_2& q,
const GeomTraits& traits) { }
/// \endcond
/*!
\ingroup PkgWeightsRefMeanValueWeights
// 2D ==============================================================================================
\brief computes the mean value weight in 2D at `q` using the points `p0`, `p1`,
and `p2` which are parameterized by a `Kernel` K.
*/
template<typename K>
typename K::FT mean_value_weight(
const CGAL::Point_2<K>& p0,
const CGAL::Point_2<K>& p1,
const CGAL::Point_2<K>& p2,
const CGAL::Point_2<K>& q) { }
/*!
\ingroup PkgWeightsRefMeanValueWeights
\brief computes the mean value weight in 2D at `q` using the points `p0`, `p1`, and `p2`.
\tparam GeomTraits a model of `AnalyticWeightTraits_2`
*/
template<typename GeomTraits>
typename GeomTraits::FT mean_value_weight(const typename GeomTraits::Point_2& p0,
const typename GeomTraits::Point_2& p1,
const typename GeomTraits::Point_2& p2,
const typename GeomTraits::Point_2& q,
const GeomTraits& traits)
{
using FT = typename GeomTraits::FT;
using Vector_2 = typename GeomTraits::Vector_2;
#endif // DOXYGEN_RUNNING
auto vector_2 = traits.construct_vector_2_object();
auto dot_product_2 = traits.compute_scalar_product_2_object();
auto area_2 = traits.compute_area_2_object();
const Vector_2 v1 = vector_2(q, p0);
const Vector_2 v = vector_2(q, p1);
const Vector_2 v2 = vector_2(q, p2);
const FT d0 = internal::length_2(v1, traits);
const FT d = internal::length_2(v, traits);
const FT d2 = internal::length_2(v2, traits);
const FT D0 = dot_product_2(v1, v);
const FT D2 = dot_product_2(v, v2);
const FT D = dot_product_2(v1, v2);
const FT A0 = area_2(p1, q, p0);
const FT A2 = area_2(p2, q, p1);
const FT B = area_2(p2, q, p0);
const FT sign = mean_value_ns::sign_of_weight(A0, A2, B);
return mean_value_ns::weight(d0, d2, d, D0, D2, D, sign, traits);
}
/*!
\ingroup PkgWeightsRefMeanValueWeights
\brief computes the mean value weight in 2D at `q` using the points `p0`, `p1`, and `p2`.
\tparam Kernel a model of `Kernel`
*/
template<typename Kernel>
typename Kernel::FT mean_value_weight(const CGAL::Point_2<Kernel>& p0,
const CGAL::Point_2<Kernel>& p1,
const CGAL::Point_2<Kernel>& p2,
const CGAL::Point_2<Kernel>& q)
{
const Kernel traits;
return mean_value_weight(p0, p1, p2, q, traits);
}
// 3D ==============================================================================================
/// \cond SKIP_IN_MANUAL
template<typename GeomTraits>
typename GeomTraits::FT mean_value_weight(const typename GeomTraits::Point_3& p0,
const typename GeomTraits::Point_3& p1,
const typename GeomTraits::Point_3& p2,
const typename GeomTraits::Point_3& q,
const GeomTraits& traits)
{
using Point_2 = typename GeomTraits::Point_2;
Point_2 p0f, p1f, p2f, qf;
internal::flatten(p0, p1, p2, q,
p0f, p1f, p2f, qf,
traits);
return CGAL::Weights::mean_value_weight(p0f, p1f, p2f, qf, traits);
}
template<typename Kernel>
typename Kernel::FT mean_value_weight(const CGAL::Point_3<Kernel>& p0,
const CGAL::Point_3<Kernel>& p1,
const CGAL::Point_3<Kernel>& p2,
const CGAL::Point_3<Kernel>& q)
{
const Kernel traits;
return mean_value_weight(p0, p1, p2, q, traits);
}
/// \endcond
/*!
\ingroup PkgWeightsRefBarycentricMeanValueWeights
\brief 2D mean value weights for polygons.
This class implements 2D mean value weights (\cite cgal:bc:fhk-gcbcocp-06, \cite cgal:f-mvc-03,
\cite cgal:bc:hf-mvcapp-06) which can be computed at any point inside and outside a simple polygon.
Mean value weights are well-defined inside and outside a simple polygon and are
non-negative in the kernel of a star-shaped polygon. These weights are computed
analytically using the formulation from `tangent_weight()`.
\tparam VertexRange a model of `ConstRange` whose iterator type is `RandomAccessIterator`
\tparam GeomTraits a model of `AnalyticWeightTraits_2`
\tparam PointMap a model of `ReadablePropertyMap` whose key type is `VertexRange::value_type` and
value type is `Point_2`. The default is `CGAL::Identity_property_map`.
\cgalModels `BarycentricWeights_2`
*/
template<typename VertexRange,
typename GeomTraits,
typename PointMap = CGAL::Identity_property_map<typename GeomTraits::Point_2> >
class Mean_value_weights_2
{
public:
/// \name Types
/// @{
/// \cond SKIP_IN_MANUAL
template<typename GeomTraits>
typename GeomTraits::FT mean_value_weight(
const typename GeomTraits::Point_2& t,
const typename GeomTraits::Point_2& r,
const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const GeomTraits& traits) {
using FT = typename GeomTraits::FT;
const auto dot_product_2 =
traits.compute_scalar_product_2_object();
const auto construct_vector_2 =
traits.construct_vector_2_object();
using Vertex_range = VertexRange;
using Geom_traits = GeomTraits;
using Point_map = PointMap;
const auto v1 = construct_vector_2(q, t);
const auto v2 = construct_vector_2(q, r);
const auto v3 = construct_vector_2(q, p);
const FT l1 = internal::length_2(traits, v1);
const FT l2 = internal::length_2(traits, v2);
const FT l3 = internal::length_2(traits, v3);
const FT D1 = dot_product_2(v1, v2);
const FT D2 = dot_product_2(v2, v3);
const FT D = dot_product_2(v1, v3);
const FT A1 = internal::area_2(traits, r, q, t);
const FT A2 = internal::area_2(traits, p, q, r);
const FT B = internal::area_2(traits, p, q, t);
const FT sign = mean_value_ns::sign_of_weight(A1, A2, B);
return mean_value_ns::weight(
traits, l1, l2, l3, D1, D2, D, sign);
}
template<typename GeomTraits>
typename GeomTraits::FT mean_value_weight(
const CGAL::Point_2<GeomTraits>& t,
const CGAL::Point_2<GeomTraits>& r,
const CGAL::Point_2<GeomTraits>& p,
const CGAL::Point_2<GeomTraits>& q) {
const GeomTraits traits;
return mean_value_weight(t, r, p, q, traits);
}
namespace internal {
template<typename GeomTraits>
typename GeomTraits::FT mean_value_weight(
const typename GeomTraits::Point_3& t,
const typename GeomTraits::Point_3& r,
const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const GeomTraits& traits) {
using Point_2 = typename GeomTraits::Point_2;
Point_2 tf, rf, pf, qf;
internal::flatten(
traits,
t, r, p, q,
tf, rf, pf, qf);
return CGAL::Weights::
mean_value_weight(tf, rf, pf, qf, traits);
}
template<typename GeomTraits>
typename GeomTraits::FT mean_value_weight(
const CGAL::Point_3<GeomTraits>& t,
const CGAL::Point_3<GeomTraits>& r,
const CGAL::Point_3<GeomTraits>& p,
const CGAL::Point_3<GeomTraits>& q) {
const GeomTraits traits;
return mean_value_weight(t, r, p, q, traits);
}
} // namespace internal
using Vector_2 = typename GeomTraits::Vector_2;
using Area_2 = typename GeomTraits::Compute_area_2;
using Construct_vector_2 = typename GeomTraits::Construct_vector_2;
using Squared_length_2 = typename GeomTraits::Compute_squared_length_2;
using Scalar_product_2 = typename GeomTraits::Compute_scalar_product_2;
using Get_sqrt = internal::Get_sqrt<GeomTraits>;
using Sqrt = typename Get_sqrt::Sqrt;
/// \endcond
/*!
\ingroup PkgWeightsRefBarycentricMeanValueWeights
/// Number type.
typedef typename GeomTraits::FT FT;
\brief 2D mean value weights for polygons.
/// Point type.
typedef typename GeomTraits::Point_2 Point_2;
This class implements 2D mean value weights ( \cite cgal:bc:hf-mvcapp-06,
\cite cgal:bc:fhk-gcbcocp-06, \cite cgal:f-mvc-03 ) which can be computed
at any point inside and outside a simple polygon.
/// @}
Mean value weights are well-defined inside and outside a simple polygon and are
non-negative in the kernel of a star-shaped polygon. These weights are computed
analytically using the formulation from the `tangent_weight()`.
\tparam VertexRange
a model of `ConstRange` whose iterator type is `RandomAccessIterator`
\tparam GeomTraits
a model of `AnalyticWeightTraits_2`
\tparam PointMap
a model of `ReadablePropertyMap` whose key type is `VertexRange::value_type` and
value type is `Point_2`. The default is `CGAL::Identity_property_map`.
\cgalModels `BarycentricWeights_2`
*/
template<
typename VertexRange,
typename GeomTraits,
typename PointMap = CGAL::Identity_property_map<typename GeomTraits::Point_2> >
class Mean_value_weights_2 {
public:
/// \name Types
/// @{
/// \cond SKIP_IN_MANUAL
using Vertex_range = VertexRange;
using Geom_traits = GeomTraits;
using Point_map = PointMap;
using Vector_2 = typename GeomTraits::Vector_2;
using Area_2 = typename GeomTraits::Compute_area_2;
using Construct_vector_2 = typename GeomTraits::Construct_vector_2;
using Squared_length_2 = typename GeomTraits::Compute_squared_length_2;
using Scalar_product_2 = typename GeomTraits::Compute_scalar_product_2;
using Get_sqrt = internal::Get_sqrt<GeomTraits>;
using Sqrt = typename Get_sqrt::Sqrt;
/// \endcond
/// Number type.
typedef typename GeomTraits::FT FT;
/// Point type.
typedef typename GeomTraits::Point_2 Point_2;
/// @}
/// \name Initialization
/// @{
/*!
\brief initializes all internal data structures.
This class implements the behavior of mean value weights
for 2D query points inside simple polygons.
\param polygon
an instance of `VertexRange` with the vertices of a simple polygon
\param traits
a traits class with geometric objects, predicates, and constructions;
the default initialization is provided
\param point_map
an instance of `PointMap` that maps a vertex from `polygon` to `Point_2`;
the default initialization is provided
\pre polygon.size() >= 3
\pre polygon is simple
*/
Mean_value_weights_2(
const VertexRange& polygon,
const GeomTraits traits = GeomTraits(),
const PointMap point_map = PointMap()) :
m_polygon(polygon),
m_traits(traits),
m_point_map(point_map),
m_area_2(m_traits.compute_area_2_object()),
m_construct_vector_2(m_traits.construct_vector_2_object()),
m_squared_length_2(m_traits.compute_squared_length_2_object()),
m_scalar_product_2(m_traits.compute_scalar_product_2_object()),
m_sqrt(Get_sqrt::sqrt_object(m_traits)) {
CGAL_precondition(
polygon.size() >= 3);
CGAL_precondition(
internal::is_simple_2(polygon, traits, point_map));
resize();
}
/// @}
/// \name Access
/// @{
/*!
\brief computes 2D mean value weights.
This function fills a destination range with 2D mean value weights computed at
the `query` point with respect to the vertices of the input polygon.
The number of computed weights is equal to the number of polygon vertices.
\tparam OutIterator
a model of `OutputIterator` whose value type is `FT`
\param query
a query point
\param w_begin
the beginning of the destination range with the computed weights
\return an output iterator to the element in the destination range,
one past the last weight stored
*/
template<typename OutIterator>
OutIterator operator()(const Point_2& query, OutIterator w_begin) {
const bool normalize = false;
return operator()(query, w_begin, normalize);
}
/// @}
/// \cond SKIP_IN_MANUAL
template<typename OutIterator>
OutIterator operator()(const Point_2& query, OutIterator weights, const bool normalize) {
return optimal_weights(query, weights, normalize);
}
/// \endcond
private:
// Fields.
const VertexRange& m_polygon;
const GeomTraits m_traits;
const PointMap m_point_map;
const Area_2 m_area_2;
const Construct_vector_2 m_construct_vector_2;
const Squared_length_2 m_squared_length_2;
const Scalar_product_2 m_scalar_product_2;
const Sqrt m_sqrt;
std::vector<Vector_2> s;
std::vector<FT> r;
std::vector<FT> A;
std::vector<FT> D;
std::vector<FT> t;
std::vector<FT> w;
// Functions.
void resize() {
s.resize(m_polygon.size());
r.resize(m_polygon.size());
A.resize(m_polygon.size());
D.resize(m_polygon.size());
t.resize(m_polygon.size());
w.resize(m_polygon.size());
}
template<typename OutputIterator>
OutputIterator optimal_weights(
const Point_2& query, OutputIterator weights, const bool normalize) {
// Get the number of vertices in the polygon.
const std::size_t n = m_polygon.size();
// Compute vectors s following the pseudo-code in the Figure 10 from [1].
for (std::size_t i = 0; i < n; ++i) {
const auto& pi = get(m_point_map, *(m_polygon.begin() + i));
s[i] = m_construct_vector_2(query, pi);
}
// Compute lengths r, areas A, and dot products D following the pseudo-code
// in the Figure 10 from [1]. Split the loop to make this computation faster.
const auto& p1 = get(m_point_map, *(m_polygon.begin() + 0));
const auto& p2 = get(m_point_map, *(m_polygon.begin() + 1));
r[0] = m_sqrt(m_squared_length_2(s[0]));
A[0] = m_area_2(p1, p2, query);
D[0] = m_scalar_product_2(s[0], s[1]);
for (std::size_t i = 1; i < n - 1; ++i) {
const auto& pi1 = get(m_point_map, *(m_polygon.begin() + (i + 0)));
const auto& pi2 = get(m_point_map, *(m_polygon.begin() + (i + 1)));
r[i] = m_sqrt(m_squared_length_2(s[i]));
A[i] = m_area_2(pi1, pi2, query);
D[i] = m_scalar_product_2(s[i], s[i + 1]);
}
const auto& pn = get(m_point_map, *(m_polygon.begin() + (n - 1)));
r[n - 1] = m_sqrt(m_squared_length_2(s[n - 1]));
A[n - 1] = m_area_2(pn, p1, query);
D[n - 1] = m_scalar_product_2(s[n - 1], s[0]);
// Compute intermediate values t using the formulas from slide 19 here
// - http://www.inf.usi.ch/hormann/nsfworkshop/presentations/Hormann.pdf
for (std::size_t i = 0; i < n - 1; ++i) {
CGAL_assertion((r[i] * r[i + 1] + D[i]) != FT(0));
t[i] = FT(2) * A[i] / (r[i] * r[i + 1] + D[i]);
}
CGAL_assertion((r[n - 1] * r[0] + D[n - 1]) != FT(0));
t[n - 1] = FT(2) * A[n - 1] / (r[n - 1] * r[0] + D[n - 1]);
// Compute mean value weights using the same pseudo-code as before.
CGAL_assertion(r[0] != FT(0));
w[0] = FT(2) * (t[n - 1] + t[0]) / r[0];
for (std::size_t i = 1; i < n - 1; ++i) {
CGAL_assertion(r[i] != FT(0));
w[i] = FT(2) * (t[i - 1] + t[i]) / r[i];
}
CGAL_assertion(r[n - 1] != FT(0));
w[n - 1] = FT(2) * (t[n - 2] + t[n - 1]) / r[n - 1];
// Normalize if necessary.
if (normalize) {
internal::normalize(w);
}
// Return weights.
for (std::size_t i = 0; i < n; ++i) {
*(weights++) = w[i];
}
return weights;
}
};
/// \name Initialization
/// @{
/*!
\ingroup PkgWeightsRefBarycentricMeanValueWeights
\brief initializes all internal data structures.
\brief computes 2D mean value weights for polygons.
This class implements the behavior of mean value weights
for 2D query points inside simple polygons.
This function computes 2D mean value weights at a given `query` point
with respect to the vertices of a simple `polygon`, that is one
weight per vertex. The weights are stored in a destination range
beginning at `w_begin`.
Internally, the class `Mean_value_weights_2` is used. If one wants to process
multiple query points, it is better to use that class. When using the free function,
internal memory is allocated for each query point, while when using the class,
it is allocated only once which is much more efficient. However, for a few query
points, it is easier to use this function. It can also be used when the processing
time is not a concern.
\tparam PointRange
a model of `ConstRange` whose iterator type is `RandomAccessIterator`
and value type is `GeomTraits::Point_2`
\tparam OutIterator
a model of `OutputIterator` whose value type is `GeomTraits::FT`
\tparam GeomTraits
a model of `AnalyticWeightTraits_2`
\param polygon
an instance of `PointRange` with 2D points which form a simple polygon
\param query
a query point
\param w_begin
the beginning of the destination range with the computed weights
\param traits
a traits class with geometric objects, predicates, and constructions;
this parameter can be omitted if the traits class can be deduced from the point type
\return an output iterator to the element in the destination range,
one past the last weight stored
\param polygon an instance of `VertexRange` with the vertices of a simple polygon
\param traits a traits class with geometric objects, predicates, and constructions;
the default initialization is provided
\param point_map an instance of `PointMap` that maps a vertex from `polygon` to `Point_2`;
the default initialization is provided
\pre polygon.size() >= 3
\pre polygon is simple
*/
template<
typename PointRange,
typename OutIterator,
typename GeomTraits>
OutIterator mean_value_weights_2(
const PointRange& polygon, const typename GeomTraits::Point_2& query,
OutIterator w_begin, const GeomTraits& traits) {
Mean_value_weights_2<PointRange, GeomTraits>
mean_value(polygon, traits);
return mean_value(query, w_begin);
Mean_value_weights_2(const VertexRange& polygon,
const GeomTraits traits = GeomTraits(),
const PointMap point_map = PointMap())
: m_polygon(polygon),
m_traits(traits),
m_point_map(point_map),
m_area_2(m_traits.compute_area_2_object()),
m_construct_vector_2(m_traits.construct_vector_2_object()),
m_squared_length_2(m_traits.compute_squared_length_2_object()),
m_scalar_product_2(m_traits.compute_scalar_product_2_object()),
m_sqrt(Get_sqrt::sqrt_object(m_traits))
{
CGAL_precondition(polygon.size() >= 3);
CGAL_precondition(internal::is_simple_2(polygon, traits, point_map));
resize();
}
/// @}
/// \name Access
/// @{
/*!
\brief computes 2D mean value weights.
This function fills a destination range with 2D mean value weights computed at
the `query` point with respect to the vertices of the input polygon.
The number of computed weights is equal to the number of polygon vertices.
\tparam OutIterator a model of `OutputIterator` whose value type is `FT`
\param query a query point
\param w_begin the beginning of the destination range with the computed weights
\return an output iterator to the element in the destination range, one past the last weight stored
*/
template<typename OutIterator>
OutIterator operator()(const Point_2& query, OutIterator w_begin)
{
const bool normalize = false;
return operator()(query, w_begin, normalize);
}
/// @}
/// \cond SKIP_IN_MANUAL
template<
typename PointRange,
typename OutIterator>
OutIterator mean_value_weights_2(
const PointRange& polygon,
const typename PointRange::value_type& query,
OutIterator w_begin) {
using Point_2 = typename PointRange::value_type;
using GeomTraits = typename Kernel_traits<Point_2>::Kernel;
const GeomTraits traits;
return mean_value_weights_2(
polygon, query, w_begin, traits);
template<typename OutIterator>
OutIterator operator()(const Point_2& query,
OutIterator weights,
const bool normalize)
{
return optimal_weights(query, weights, normalize);
}
/// \endcond
private:
const VertexRange& m_polygon;
const GeomTraits m_traits;
const PointMap m_point_map;
const Area_2 m_area_2;
const Construct_vector_2 m_construct_vector_2;
const Squared_length_2 m_squared_length_2;
const Scalar_product_2 m_scalar_product_2;
const Sqrt m_sqrt;
std::vector<Vector_2> s;
std::vector<FT> r;
std::vector<FT> A;
std::vector<FT> D;
std::vector<FT> t;
std::vector<FT> w;
void resize()
{
s.resize(m_polygon.size());
r.resize(m_polygon.size());
A.resize(m_polygon.size());
D.resize(m_polygon.size());
t.resize(m_polygon.size());
w.resize(m_polygon.size());
}
template<typename OutputIterator>
OutputIterator optimal_weights(const Point_2& query,
OutputIterator weights,
const bool normalize)
{
const std::size_t n = m_polygon.size();
// Compute vectors s following the pseudo-code in the Figure 10 from [1].
for (std::size_t i = 0; i < n; ++i)
{
const auto& pi = get(m_point_map, *(m_polygon.begin() + i));
s[i] = m_construct_vector_2(query, pi);
}
// Compute lengths r, areas A, and dot products D following the pseudo-code
// in the Figure 10 from [1]. Split the loop to make this computation faster.
const auto& p1 = get(m_point_map, *(m_polygon.begin() + 0));
const auto& p2 = get(m_point_map, *(m_polygon.begin() + 1));
r[0] = m_sqrt(m_squared_length_2(s[0]));
A[0] = m_area_2(p1, p2, query);
D[0] = m_scalar_product_2(s[0], s[1]);
for (std::size_t i = 1; i < n - 1; ++i)
{
const auto& pi1 = get(m_point_map, *(m_polygon.begin() + (i + 0)));
const auto& pi2 = get(m_point_map, *(m_polygon.begin() + (i + 1)));
r[i] = m_sqrt(m_squared_length_2(s[i]));
A[i] = m_area_2(pi1, pi2, query);
D[i] = m_scalar_product_2(s[i], s[i + 1]);
}
const auto& pn = get(m_point_map, *(m_polygon.begin() + (n - 1)));
r[n - 1] = m_sqrt(m_squared_length_2(s[n - 1]));
A[n - 1] = m_area_2(pn, p1, query);
D[n - 1] = m_scalar_product_2(s[n - 1], s[0]);
// Compute intermediate values t using the formulas from slide 19 here
// - http://www.inf.usi.ch/hormann/nsfworkshop/presentations/Hormann.pdf
for (std::size_t i = 0; i < n - 1; ++i)
{
CGAL_assertion((r[i] * r[i + 1] + D[i]) != FT(0));
t[i] = FT(2) * A[i] / (r[i] * r[i + 1] + D[i]);
}
CGAL_assertion((r[n - 1] * r[0] + D[n - 1]) != FT(0));
t[n - 1] = FT(2) * A[n - 1] / (r[n - 1] * r[0] + D[n - 1]);
// Compute mean value weights using the same pseudo-code as before.
CGAL_assertion(r[0] != FT(0));
w[0] = FT(2) * (t[n - 1] + t[0]) / r[0];
for (std::size_t i = 1; i < n - 1; ++i)
{
CGAL_assertion(r[i] != FT(0));
w[i] = FT(2) * (t[i - 1] + t[i]) / r[i];
}
CGAL_assertion(r[n - 1] != FT(0));
w[n - 1] = FT(2) * (t[n - 2] + t[n - 1]) / r[n - 1];
// Normalize if necessary.
if (normalize)
internal::normalize(w);
// Return weights.
for (std::size_t i = 0; i < n; ++i)
*(weights++) = w[i];
return weights;
}
};
/*!
\ingroup PkgWeightsRefBarycentricMeanValueWeights
\brief computes 2D mean value weights for polygons.
This function computes 2D mean value weights at a given `query` point
with respect to the vertices of a simple `polygon`, that is one
weight per vertex. The weights are stored in a destination range
beginning at `w_begin`.
Internally, the class `Mean_value_weights_2` is used. If one wants to process
multiple query points, it is better to use that class. When using the free function,
internal memory is allocated for each query point, while when using the class,
it is allocated only once which is much more efficient. However, for a few query
points, it is easier to use this function. It can also be used when the processing
time is not a concern.
\tparam PointRange a model of `ConstRange` whose iterator type is `RandomAccessIterator`
and value type is `GeomTraits::Point_2`
\tparam OutIterator a model of `OutputIterator` whose value type is `GeomTraits::FT`
\tparam GeomTraits a model of `AnalyticWeightTraits_2`
\param polygon an instance of `PointRange` with 2D points which form a simple polygon
\param query a query point
\param w_begin the beginning of the destination range with the computed weights
\param traits a traits class with geometric objects, predicates, and constructions;
this parameter can be omitted if the traits class can be deduced from the point type
\return an output iterator to the element in the destination range, one past the last weight stored
\pre `polygon.size() >= 3`
\pre `polygon` is simple
*/
template<typename PointRange,
typename OutIterator,
typename GeomTraits>
OutIterator mean_value_weights_2(const PointRange& polygon,
const typename GeomTraits::Point_2& query,
OutIterator w_begin,
const GeomTraits& traits)
{
Mean_value_weights_2<PointRange, GeomTraits> mean_value(polygon, traits);
return mean_value(query, w_begin);
}
/// \cond SKIP_IN_MANUAL
template<typename PointRange,
typename OutIterator>
OutIterator mean_value_weights_2(const PointRange& polygon,
const typename PointRange::value_type& query,
OutIterator w_begin)
{
using Point_2 = typename PointRange::value_type;
using GeomTraits = typename Kernel_traits<Point_2>::Kernel;
const GeomTraits traits;
return mean_value_weights_2(polygon, query, w_begin, traits);
}
/// \endcond
} // namespace Weights
} // namespace CGAL

View File

@ -14,157 +14,115 @@
#ifndef CGAL_MIXED_VORONOI_REGION_WEIGHTS_H
#define CGAL_MIXED_VORONOI_REGION_WEIGHTS_H
// Internal includes.
#include <CGAL/Weights/internal/utils.h>
#include <CGAL/Point_2.h>
#include <CGAL/Point_3.h>
namespace CGAL {
namespace Weights {
#if defined(DOXYGEN_RUNNING)
// 2D ==============================================================================================
/*!
\ingroup PkgWeightsRefMixedVoronoiRegionWeights
/*!
\ingroup PkgWeightsRefMixedVoronoiRegionWeights
\brief computes the area of the mixed Voronoi cell in 2D using the points `p`, `q`, and `r`.
\tparam GeomTraits a model of `AnalyticWeightTraits_2`
*/
template<typename GeomTraits>
typename GeomTraits::FT mixed_voronoi_area(const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const typename GeomTraits::Point_2& r,
const GeomTraits& traits)
{
using FT = typename GeomTraits::FT;
using Point_2 = typename GeomTraits::Point_2;
\brief computes the area of the mixed Voronoi cell in 2D using the points `p`, `q`
and `r`, given a traits class `traits` with geometric objects, predicates, and constructions.
*/
template<typename GeomTraits>
typename GeomTraits::FT mixed_voronoi_area(
const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const typename GeomTraits::Point_2& r,
const GeomTraits& traits) { }
auto angle_2 = traits.angle_2_object();
auto midpoint_2 = traits.construct_midpoint_2_object();
auto circumcenter_2 = traits.construct_circumcenter_2_object();
/*!
\ingroup PkgWeightsRefMixedVoronoiRegionWeights
Point_2 center;
if (angle_2(p, q, r) != CGAL::OBTUSE &&
angle_2(q, r, p) != CGAL::OBTUSE &&
angle_2(r, p, q) != CGAL::OBTUSE)
center = circumcenter_2(p, q, r);
else
center = midpoint_2(r, p);
\brief computes the area of the mixed Voronoi cell in 3D using the points `p`, `q`
and `r`, given a traits class `traits` with geometric objects, predicates, and constructions.
*/
template<typename GeomTraits>
typename GeomTraits::FT mixed_voronoi_area(
const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const typename GeomTraits::Point_3& r,
const GeomTraits& traits) { }
const Point_2 m1 = midpoint_2(q, r);
const Point_2 m2 = midpoint_2(q, p);
/*!
\ingroup PkgWeightsRefMixedVoronoiRegionWeights
const FT A1 = internal::positive_area_2(q, m1, center, traits);
const FT A2 = internal::positive_area_2(q, center, m2, traits);
\brief computes the area of the mixed Voronoi cell in 2D using the points `p`, `q`
and `r` which are parameterized by a `Kernel` K.
*/
template<typename K>
typename K::FT mixed_voronoi_area(
const CGAL::Point_2<K>& p,
const CGAL::Point_2<K>& q,
const CGAL::Point_2<K>& r) { }
return A1 + A2;
}
/*!
\ingroup PkgWeightsRefMixedVoronoiRegionWeights
/*!
\ingroup PkgWeightsRefMixedVoronoiRegionWeights
\brief computes the area of the mixed Voronoi cell in 2D using the points `p`, `q`, and `r`.
\tparam Kernel a model of `Kernel`
*/
template<typename GeomTraits>
typename GeomTraits::FT mixed_voronoi_area(const CGAL::Point_2<GeomTraits>& p,
const CGAL::Point_2<GeomTraits>& q,
const CGAL::Point_2<GeomTraits>& r)
{
const GeomTraits traits;
return mixed_voronoi_area(p, q, r, traits);
}
\brief computes the area of the mixed Voronoi cell in 3D using the points `p`, `q`
and `r` which are parameterized by a `Kernel` K.
*/
template<typename K>
typename K::FT mixed_voronoi_area(
const CGAL::Point_3<K>& p,
const CGAL::Point_3<K>& q,
const CGAL::Point_3<K>& r) { }
// 3D ==============================================================================================
#endif // DOXYGEN_RUNNING
/*!
\ingroup PkgWeightsRefMixedVoronoiRegionWeights
\brief computes the area of the mixed Voronoi cell in 3D using the points `p`, `q`, and `r`.
\tparam GeomTraits a model of `AnalyticWeightTraits_3`
*/
template<typename GeomTraits>
typename GeomTraits::FT mixed_voronoi_area(const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const typename GeomTraits::Point_3& r,
const GeomTraits& traits)
{
using FT = typename GeomTraits::FT;
using Point_3 = typename GeomTraits::Point_3;
/// \cond SKIP_IN_MANUAL
template<typename GeomTraits>
typename GeomTraits::FT mixed_voronoi_area(
const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const typename GeomTraits::Point_2& r,
const GeomTraits& traits) {
auto angle_3 = traits.angle_3_object();
auto midpoint_3 = traits.construct_midpoint_3_object();
auto circumcenter_3 = traits.construct_circumcenter_3_object();
using FT = typename GeomTraits::FT;
using Point_2 = typename GeomTraits::Point_2;
Point_3 center;
if (angle_3(p, q, r) != CGAL::OBTUSE &&
angle_3(q, r, p) != CGAL::OBTUSE &&
angle_3(r, p, q) != CGAL::OBTUSE)
center = circumcenter_3(p, q, r);
else
center = midpoint_3(r, p);
const auto angle_2 =
traits.angle_2_object();
const auto a1 = angle_2(p, q, r);
const auto a2 = angle_2(q, r, p);
const auto a3 = angle_2(r, p, q);
const Point_3 m1 = midpoint_3(q, r);
const Point_3 m2 = midpoint_3(q, p);
Point_2 center;
const auto midpoint_2 =
traits.construct_midpoint_2_object();
if (a1 != CGAL::OBTUSE && a2 != CGAL::OBTUSE && a3 != CGAL::OBTUSE) {
const auto circumcenter_2 =
traits.construct_circumcenter_2_object();
center = circumcenter_2(p, q, r);
} else {
center = midpoint_2(r, p);
}
const FT A1 = internal::positive_area_3(q, m1, center, traits);
const FT A2 = internal::positive_area_3(q, center, m2, traits);
const auto m1 = midpoint_2(q, r);
const auto m2 = midpoint_2(q, p);
return A1 + A2;
}
const FT A1 = internal::positive_area_2(traits, q, m1, center);
const FT A2 = internal::positive_area_2(traits, q, center, m2);
return A1 + A2;
}
template<typename GeomTraits>
typename GeomTraits::FT mixed_voronoi_area(
const CGAL::Point_2<GeomTraits>& p,
const CGAL::Point_2<GeomTraits>& q,
const CGAL::Point_2<GeomTraits>& r) {
const GeomTraits traits;
return mixed_voronoi_area(p, q, r, traits);
}
template<typename GeomTraits>
typename GeomTraits::FT mixed_voronoi_area(
const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const typename GeomTraits::Point_3& r,
const GeomTraits& traits) {
using FT = typename GeomTraits::FT;
using Point_3 = typename GeomTraits::Point_3;
const auto angle_3 =
traits.angle_3_object();
const auto a1 = angle_3(p, q, r);
const auto a2 = angle_3(q, r, p);
const auto a3 = angle_3(r, p, q);
Point_3 center;
const auto midpoint_3 =
traits.construct_midpoint_3_object();
if (a1 != CGAL::OBTUSE && a2 != CGAL::OBTUSE && a3 != CGAL::OBTUSE) {
const auto circumcenter_3 =
traits.construct_circumcenter_3_object();
center = circumcenter_3(p, q, r);
} else {
center = midpoint_3(r, p);
}
const auto m1 = midpoint_3(q, r);
const auto m2 = midpoint_3(q, p);
const FT A1 = internal::positive_area_3(traits, q, m1, center);
const FT A2 = internal::positive_area_3(traits, q, center, m2);
return A1 + A2;
}
template<typename GeomTraits>
typename GeomTraits::FT mixed_voronoi_area(
const CGAL::Point_3<GeomTraits>& p,
const CGAL::Point_3<GeomTraits>& q,
const CGAL::Point_3<GeomTraits>& r) {
const GeomTraits traits;
return mixed_voronoi_area(p, q, r, traits);
}
/// \endcond
/*!
\ingroup PkgWeightsRefMixedVoronoiRegionWeights
\brief computes the area of the mixed Voronoi cell in 3D using the points `p`, `q`, and `r`.
\tparam Kernel a model of `Kernel`
*/
template<typename Kernel>
typename Kernel::FT mixed_voronoi_area(const CGAL::Point_3<Kernel>& p,
const CGAL::Point_3<Kernel>& q,
const CGAL::Point_3<Kernel>& r)
{
const Kernel traits;
return mixed_voronoi_area(p, q, r, traits);
}
} // namespace Weights
} // namespace CGAL

View File

@ -14,251 +14,178 @@
#ifndef CGAL_SHEPARD_WEIGHTS_H
#define CGAL_SHEPARD_WEIGHTS_H
// Internal includes.
#include <CGAL/Weights/internal/utils.h>
#include <CGAL/Point_2.h>
#include <CGAL/Point_3.h>
namespace CGAL {
namespace Weights {
/// \cond SKIP_IN_MANUAL
namespace shepard_ns {
/// \cond SKIP_IN_MANUAL
template<typename GeomTraits>
typename GeomTraits::FT weight(
const GeomTraits& traits,
const typename GeomTraits::FT d,
const typename GeomTraits::FT p) {
namespace shepard_ns {
using FT = typename GeomTraits::FT;
FT w = FT(0);
CGAL_precondition(d != FT(0));
if (d != FT(0)) {
FT denom = d;
if (p != FT(1)) {
denom = internal::power(traits, d, p);
}
w = FT(1) / denom;
}
return w;
}
}
/// \endcond
template<typename FT>
FT weight(const FT d, const FT p)
{
FT w = FT(0);
CGAL_precondition(is_positive(d));
if(is_positive(d))
w = internal::power(d, -p);
#if defined(DOXYGEN_RUNNING)
return w;
}
/*!
\ingroup PkgWeightsRefShepardWeights
} // namespace shepard_ns
\brief computes the Shepard weight in 2D using the points `p` and `q` and the power parameter `a`,
given a traits class `traits` with geometric objects, predicates, and constructions.
*/
template<typename GeomTraits>
typename GeomTraits::FT shepard_weight(
const typename GeomTraits::Point_2&,
const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2&,
const typename GeomTraits::Point_2& q,
const typename GeomTraits::FT a,
const GeomTraits& traits) { }
/// \endcond
/*!
\ingroup PkgWeightsRefShepardWeights
// 2D ==============================================================================================
\brief computes the Shepard weight in 3D using the points `p` and `q` and the power parameter `a`,
given a traits class `traits` with geometric objects, predicates, and constructions.
*/
template<typename GeomTraits>
typename GeomTraits::FT shepard_weight(
const typename GeomTraits::Point_3&,
const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3&,
const typename GeomTraits::Point_3& q,
const typename GeomTraits::FT a,
const GeomTraits& traits) { }
/*!
\ingroup PkgWeightsRefShepardWeights
\brief computes the Shepard weight in 2D using the points `p` and `q` and the power parameter `a`.
\tparam GeomTraits a model of `AnalyticWeightTraits_2`
*/
template<typename GeomTraits>
typename GeomTraits::FT shepard_weight(const typename GeomTraits::Point_2&,
const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2&,
const typename GeomTraits::Point_2& q,
const typename GeomTraits::FT a,
const GeomTraits& traits)
{
using FT = typename GeomTraits::FT;
const FT d = internal::distance_2(p, q, traits);
return shepard_ns::weight(d, a);
}
/*!
\ingroup PkgWeightsRefShepardWeights
/*!
\ingroup PkgWeightsRefShepardWeights
\brief computes the Shepard weight in 2D using the points `p` and `q`, and the power parameter `a`
\tparam Kernel a model of `Kernel`
*/
template<typename Kernel>
#ifdef DOXYGEN_RUNNING
typename Kernel::FT shepard_weight(const CGAL::Point_2<Kernel>&,
const CGAL::Point_2<Kernel>& p^,
const CGAL::Point_2<Kernel>&,
const CGAL::Point_2<Kernel>& q,
const typename Kernel::FT a = {1})
#else
typename Kernel::FT shepard_weight(const CGAL::Point_2<Kernel>& stub_l,
const CGAL::Point_2<Kernel>& p,
const CGAL::Point_2<Kernel>& stub_r,
const CGAL::Point_2<Kernel>& q,
const typename Kernel::FT a = {1})
#endif
{
const Kernel traits;
return shepard_weight(stub_l, p, stub_r, q, a, traits);
}
\brief computes the Shepard weight in 2D using the points `p` and `q`,
which are parameterized by a `Kernel` K, and the power parameter `a` which
can be omitted.
*/
template<typename K>
typename K::FT shepard_weight(
const CGAL::Point_2<K>&,
const CGAL::Point_2<K>& p,
const CGAL::Point_2<K>&,
const CGAL::Point_2<K>& q,
const typename K::FT a = typename K::FT(1)) { }
/*!
\ingroup PkgWeightsRefShepardWeights
\brief computes the Shepard weight in 2D using the points `p` and `q` and the power parameter `a`.
\tparam GeomTraits a model of `AnalyticWeightTraits_2`
*/
template<typename GeomTraits>
typename GeomTraits::FT shepard_weight(const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const typename GeomTraits::FT a,
const GeomTraits& traits)
{
typename GeomTraits::Point_2 stub;
return shepard_weight(stub, p, stub, q, a, traits);
}
/*!
\ingroup PkgWeightsRefShepardWeights
/*!
\ingroup PkgWeightsRefShepardWeights
\brief computes the Shepard weight in 2D using the points `p` and `q`, and the power parameter `a`.
\tparam Kernel a model of `Kernel`
*/
template<typename Kernel>
typename Kernel::FT shepard_weight(const CGAL::Point_2<Kernel>& p,
const CGAL::Point_2<Kernel>& q,
const typename Kernel::FT a = {1})
{
CGAL::Point_2<Kernel> stub;
return shepard_weight(stub, p, stub, q, a);
}
\brief computes the Shepard weight in 3D using the points `p` and `q`,
which are parameterized by a `Kernel` K, and the power parameter `a` which
can be omitted.
*/
template<typename K>
typename K::FT shepard_weight(
const CGAL::Point_3<K>&,
const CGAL::Point_3<K>& p,
const CGAL::Point_3<K>&,
const CGAL::Point_3<K>& q,
const typename K::FT a = typename K::FT(1)) { }
// 3D ==============================================================================================
/*!
\ingroup PkgWeightsRefShepardWeights
/*!
\ingroup PkgWeightsRefShepardWeights
\brief computes the Shepard weight in 3D using the points `p` and `q` and the power parameter `a`.
\tparam GeomTraits a model of `AnalyticWeightTraits_3`
*/
template<typename GeomTraits>
typename GeomTraits::FT shepard_weight(const typename GeomTraits::Point_3&,
const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3&,
const typename GeomTraits::Point_3& q,
const typename GeomTraits::FT a,
const GeomTraits& traits)
{
using FT = typename GeomTraits::FT;
const FT d = internal::distance_3(p, q, traits);
return shepard_ns::weight(d, a);
}
\brief computes the Shepard weight in 2D using the points `p` and `q` and the power parameter `a`,
given a traits class `traits` with geometric objects, predicates, and constructions.
*/
template<typename GeomTraits>
typename GeomTraits::FT shepard_weight(
const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const typename GeomTraits::FT a,
const GeomTraits& traits) { }
/*!
\ingroup PkgWeightsRefShepardWeights
\brief computes the Shepard weight in 3D using the points `p` and `q`, and the power parameter `a`.
\tparam Kernel a model of `Kernel`
*/
template<typename Kernel>
#ifdef DOXYGEN_RUNNING
typename Kernel::FT shepard_weight(const CGAL::Point_3<Kernel>& p,
const CGAL::Point_3<Kernel>&,
const CGAL::Point_3<Kernel>& q,
const CGAL::Point_3<Kernel>&,
const typename Kernel::FT a = {1})
#else
typename Kernel::FT shepard_weight(const CGAL::Point_3<Kernel>& stub_l,
const CGAL::Point_3<Kernel>& p,
const CGAL::Point_3<Kernel>& stub_r,
const CGAL::Point_3<Kernel>& q,
const typename Kernel::FT a = {1})
#endif
{
const Kernel traits;
return shepard_weight(stub_l, p, stub_r, q, a, traits);
}
/*!
\ingroup PkgWeightsRefShepardWeights
/*!
\ingroup PkgWeightsRefShepardWeights
\brief computes the Shepard weight in 3D using the points `p` and `q` and the power parameter `a`.
\tparam GeomTraits a model of `AnalyticWeightTraits_3`
*/
template<typename GeomTraits>
typename GeomTraits::FT shepard_weight(const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const typename GeomTraits::FT a,
const GeomTraits& traits)
{
typename GeomTraits::Point_3 stub;
return shepard_weight(stub, p, stub, q, a, traits);
}
\brief computes the Shepard weight in 3D using the points `p` and `q` and the power parameter `a`,
given a traits class `traits` with geometric objects, predicates, and constructions.
*/
template<typename GeomTraits>
typename GeomTraits::FT shepard_weight(
const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const typename GeomTraits::FT a,
const GeomTraits& traits) { }
/*!
\ingroup PkgWeightsRefShepardWeights
\brief computes the Shepard weight in 2D using the points `p` and `q`,
which are parameterized by a `Kernel` K, and the power parameter `a` which
can be omitted.
*/
template<typename K>
typename K::FT shepard_weight(
const CGAL::Point_2<K>& p,
const CGAL::Point_2<K>& q,
const typename K::FT a = typename K::FT(1)) { }
/*!
\ingroup PkgWeightsRefShepardWeights
\brief computes the Shepard weight in 3D using the points `p` and `q`,
which are parameterized by a `Kernel` K, and the power parameter `a` which
can be omitted.
*/
template<typename K>
typename K::FT shepard_weight(
const CGAL::Point_3<K>& p,
const CGAL::Point_3<K>& q,
const typename K::FT a = typename K::FT(1)) { }
#endif // DOXYGEN_RUNNING
/// \cond SKIP_IN_MANUAL
template<typename GeomTraits>
typename GeomTraits::FT shepard_weight(
const typename GeomTraits::Point_2&,
const typename GeomTraits::Point_2& r,
const typename GeomTraits::Point_2&,
const typename GeomTraits::Point_2& q,
const typename GeomTraits::FT a,
const GeomTraits& traits) {
using FT = typename GeomTraits::FT;
const FT d = internal::distance_2(traits, q, r);
return shepard_ns::weight(traits, d, a);
}
template<typename GeomTraits>
typename GeomTraits::FT shepard_weight(
const CGAL::Point_2<GeomTraits>& t,
const CGAL::Point_2<GeomTraits>& r,
const CGAL::Point_2<GeomTraits>& p,
const CGAL::Point_2<GeomTraits>& q,
const typename GeomTraits::FT a =
typename GeomTraits::FT(1)) {
const GeomTraits traits;
return shepard_weight(t, r, p, q, a, traits);
}
template<typename GeomTraits>
typename GeomTraits::FT shepard_weight(
const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const typename GeomTraits::FT a,
const GeomTraits& traits) {
typename GeomTraits::Point_2 stub;
return shepard_weight(stub, p, stub, q, a, traits);
}
template<typename GeomTraits>
typename GeomTraits::FT shepard_weight(
const CGAL::Point_2<GeomTraits>& p,
const CGAL::Point_2<GeomTraits>& q,
const typename GeomTraits::FT a =
typename GeomTraits::FT(1)) {
CGAL::Point_2<GeomTraits> stub;
return shepard_weight(stub, p, stub, q, a);
}
template<typename GeomTraits>
typename GeomTraits::FT shepard_weight(
const typename GeomTraits::Point_3&,
const typename GeomTraits::Point_3& r,
const typename GeomTraits::Point_3&,
const typename GeomTraits::Point_3& q,
const typename GeomTraits::FT a,
const GeomTraits& traits) {
using FT = typename GeomTraits::FT;
const FT d = internal::distance_3(traits, q, r);
return shepard_ns::weight(traits, d, a);
}
template<typename GeomTraits>
typename GeomTraits::FT shepard_weight(
const CGAL::Point_3<GeomTraits>& t,
const CGAL::Point_3<GeomTraits>& r,
const CGAL::Point_3<GeomTraits>& p,
const CGAL::Point_3<GeomTraits>& q,
const typename GeomTraits::FT a =
typename GeomTraits::FT(1)) {
const GeomTraits traits;
return shepard_weight(t, r, p, q, a, traits);
}
template<typename GeomTraits>
typename GeomTraits::FT shepard_weight(
const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const typename GeomTraits::FT a,
const GeomTraits& traits) {
typename GeomTraits::Point_3 stub;
return shepard_weight(stub, p, stub, q, a, traits);
}
template<typename GeomTraits>
typename GeomTraits::FT shepard_weight(
const CGAL::Point_3<GeomTraits>& p,
const CGAL::Point_3<GeomTraits>& q,
const typename GeomTraits::FT a =
typename GeomTraits::FT(1)) {
CGAL::Point_3<GeomTraits> stub;
return shepard_weight(stub, p, stub, q, a);
}
/// \endcond
/*!
\ingroup PkgWeightsRefShepardWeights
\brief computes the Shepard weight in 3D using the points `p` and `q`, and the power parameter `a`.
\tparam Kernel a model of `Kernel`
*/
template<typename Kernel>
typename Kernel::FT shepard_weight(const CGAL::Point_3<Kernel>& p,
const CGAL::Point_3<Kernel>& q,
const typename Kernel::FT a = {1})
{
CGAL::Point_3<Kernel> stub;
return shepard_weight(stub, p, stub, q, a);
}
} // namespace Weights
} // namespace CGAL

View File

@ -14,495 +14,507 @@
#ifndef CGAL_TANGENT_WEIGHTS_H
#define CGAL_TANGENT_WEIGHTS_H
// Internal includes.
#include <CGAL/Weights/internal/utils.h>
#include <CGAL/Weights/utils.h>
#include <CGAL/Point_2.h>
#include <CGAL/Point_3.h>
#include <cmath>
namespace CGAL {
namespace Weights {
/// \cond SKIP_IN_MANUAL
namespace tangent_ns {
/// \cond SKIP_IN_MANUAL
template<typename FT>
FT half_angle_tangent(const FT r, const FT d, const FT A, const FT D) {
namespace tangent_ns {
FT t = FT(0);
const FT P = r * d + D;
CGAL_precondition(P != FT(0));
if (P != FT(0)) {
const FT inv = FT(2) / P;
t = A * inv;
}
return t;
}
template<typename FT>
FT half_weight(const FT t, const FT r)
{
FT w = FT(0);
CGAL_precondition(!is_zero(r));
if (!is_zero(r))
w = FT(2) * t / r;
template<typename FT>
FT half_weight(const FT t, const FT r) {
return w;
}
FT w = FT(0);
CGAL_precondition(r != FT(0));
if (r != FT(0)) {
const FT inv = FT(2) / r;
w = t * inv;
}
return w;
}
template<typename FT>
FT weight(const FT t0, const FT t2, const FT r)
{
FT w = FT(0);
CGAL_precondition(r != FT(0));
if (r != FT(0))
w = FT(2) * (t0 + t2) / r;
template<typename FT>
FT weight(const FT t1, const FT t2, const FT r) {
return w;
}
FT w = FT(0);
CGAL_precondition(r != FT(0));
if (r != FT(0)) {
const FT inv = FT(2) / r;
w = (t1 + t2) * inv;
}
return w;
}
template<typename FT>
FT weight(const FT d0, const FT d2, const FT d,
const FT A0, const FT A2,
const FT D0, const FT D2)
{
const FT P0 = d * d0 + D0;
const FT P2 = d * d2 + D2;
template<typename FT>
FT weight(
const FT d1, const FT r, const FT d2,
const FT A1, const FT A2,
const FT D1, const FT D2) {
const FT P1 = d1 * r + D1;
const FT P2 = d2 * r + D2;
FT w = FT(0);
CGAL_precondition(P1 != FT(0) && P2 != FT(0));
if (P1 != FT(0) && P2 != FT(0)) {
const FT inv1 = FT(2) / P1;
const FT inv2 = FT(2) / P2;
const FT t1 = A1 * inv1;
const FT t2 = A2 * inv2;
w = weight(t1, t2, r);
}
return w;
}
// This is positive case only.
// This version is based on the positive area.
// This version is more precise for all positive cases.
template<typename GeomTraits>
typename GeomTraits::FT tangent_weight_v1(
const typename GeomTraits::Point_3& t,
const typename GeomTraits::Point_3& r,
const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const GeomTraits& traits) {
using FT = typename GeomTraits::FT;
const auto dot_product_3 =
traits.compute_scalar_product_3_object();
const auto construct_vector_3 =
traits.construct_vector_3_object();
const auto v1 = construct_vector_3(q, t);
const auto v2 = construct_vector_3(q, r);
const auto v3 = construct_vector_3(q, p);
const FT l1 = internal::length_3(traits, v1);
const FT l2 = internal::length_3(traits, v2);
const FT l3 = internal::length_3(traits, v3);
const FT A1 = internal::positive_area_3(traits, r, q, t);
const FT A2 = internal::positive_area_3(traits, p, q, r);
const FT D1 = dot_product_3(v1, v2);
const FT D2 = dot_product_3(v2, v3);
return weight(l1, l2, l3, A1, A2, D1, D2);
}
// This version handles both positive and negative cases.
// However, it is less precise.
template<typename GeomTraits>
typename GeomTraits::FT tangent_weight_v2(
const typename GeomTraits::Point_3& t,
const typename GeomTraits::Point_3& r,
const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const GeomTraits& traits) {
using FT = typename GeomTraits::FT;
const auto construct_vector_3 =
traits.construct_vector_3_object();
auto v1 = construct_vector_3(q, t);
auto v2 = construct_vector_3(q, r);
auto v3 = construct_vector_3(q, p);
const FT l2 = internal::length_3(traits, v2);
internal::normalize_3(traits, v1);
internal::normalize_3(traits, v2);
internal::normalize_3(traits, v3);
const double ha_rad_1 = internal::angle_3(traits, v1, v2) / 2.0;
const double ha_rad_2 = internal::angle_3(traits, v2, v3) / 2.0;
const FT t1 = static_cast<FT>(std::tan(ha_rad_1));
const FT t2 = static_cast<FT>(std::tan(ha_rad_2));
return weight(t1, t2, l2);
}
}
/// \endcond
/*!
\ingroup PkgWeightsRefTangentWeights
\brief computes the tangent of the half angle.
This function computes the tangent of the half angle using the precomputed
distance, area, and dot product values. The returned value is
\f$\frac{2\textbf{A}}{\textbf{d}\textbf{l} + \textbf{D}}\f$.
\tparam FT
a model of `FieldNumberType`
\param d
the distance value
\param l
the distance value
\param A
the area value
\param D
the dot product value
\pre (d * l + D) != 0
\sa `half_tangent_weight()`
*/
template<typename FT>
FT tangent_half_angle(const FT d, const FT l, const FT A, const FT D) {
return tangent_ns::half_angle_tangent(d, l, A, D);
FT w = FT(0);
CGAL_precondition(!is_zero(P0) && !is_zero(P2));
if (!is_zero(P0) && !is_zero(P2))
{
const FT t0 = FT(2) * A0 / P0;
const FT t2 = FT(2) * A2 / P2;
w = weight(t0, t2, d);
}
/*!
\ingroup PkgWeightsRefTangentWeights
return w;
}
\brief computes the half value of the tangent weight.
// This is positive case only.
// This version is based on the positive area.
// This version is more precise for all positive cases.
template<typename GeomTraits>
typename GeomTraits::FT tangent_weight_v1(const typename GeomTraits::Point_3& p0,
const typename GeomTraits::Point_3& p1,
const typename GeomTraits::Point_3& p2,
const typename GeomTraits::Point_3& q,
const GeomTraits& traits)
{
using FT = typename GeomTraits::FT;
using Vector_3 = typename GeomTraits::Vector_3;
This function constructs the half of the tangent weight using the precomputed
half angle tangent and distance values. The returned value is
\f$\frac{2\textbf{tan05}}{\textbf{d}}\f$.
auto dot_product_3 = traits.compute_scalar_product_3_object();
auto vector_3 = traits.construct_vector_3_object();
\tparam FT
a model of `FieldNumberType`
const Vector_3 v0 = vector_3(q, p0);
const Vector_3 v = vector_3(q, p1);
const Vector_3 v2 = vector_3(q, p2);
\param tan05
the half angle tangent value
const FT d0 = internal::length_3(v0, traits);
const FT d = internal::length_3(v, traits);
const FT d2 = internal::length_3(v2, traits);
\param d
the distance value
const FT A0 = internal::positive_area_3(p1, q, p0, traits);
const FT A2 = internal::positive_area_3(p2, q, p1, traits);
\pre d != 0
const FT D0 = dot_product_3(v0, v);
const FT D2 = dot_product_3(v, v2);
\sa `tangent_half_angle()`
\sa `tangent_weight()`
*/
template<typename FT>
FT half_tangent_weight(const FT tan05, const FT d) {
return tangent_ns::half_weight(tan05, d);
}
return weight(d0, d2, d, A0, A2, D0, D2);
}
/*!
\ingroup PkgWeightsRefTangentWeights
// This version handles both positive and negative cases.
// However, it is less precise.
template<typename GeomTraits>
typename GeomTraits::FT tangent_weight_v2(const typename GeomTraits::Point_3& p0,
const typename GeomTraits::Point_3& p1,
const typename GeomTraits::Point_3& p2,
const typename GeomTraits::Point_3& q,
const GeomTraits& traits)
{
using FT = typename GeomTraits::FT;
using Vector_3 = typename GeomTraits::Vector_3;
\brief computes the half value of the tangent weight.
auto vector_3 = traits.construct_vector_3_object();
This function constructs the half of the tangent weight using the precomputed
distance, area, and dot product values. The returned value is
\f$\frac{2\textbf{t}}{\textbf{d}}\f$ where
\f$\textbf{t} = \frac{2\textbf{A}}{\textbf{d}\textbf{l} + \textbf{D}}\f$.
Vector_3 v0 = vector_3(q, p0);
Vector_3 v = vector_3(q, p1);
Vector_3 v2 = vector_3(q, p2);
\tparam FT
a model of `FieldNumberType`
const FT l2 = internal::length_3(v, traits);
\param d
the distance value
const double ha_rad_1 = internal::angle_3(v0, v, traits) / 2.0;
const double ha_rad_2 = internal::angle_3(v, v2, traits) / 2.0;
const FT t0 = static_cast<FT>(std::tan(ha_rad_1));
const FT t2 = static_cast<FT>(std::tan(ha_rad_2));
\param l
the distance value
return weight(t0, t2, l2);
}
\param A
the area value
} // namespace tangent_ns
\param D
the dot product value
/// \endcond
\pre (d * l + D) != 0 && d != 0
// 2D ==============================================================================================
\sa `tangent_weight()`
*/
template<typename FT>
FT half_tangent_weight(const FT d, const FT l, const FT A, const FT D) {
const FT tan05 = tangent_half_angle(d, l, A, D);
return half_tangent_weight(tan05, d);
}
/*!
\ingroup PkgWeightsRefTangentWeights
#if defined(DOXYGEN_RUNNING)
\brief computes the half value of the tangent weight.
/*!
\ingroup PkgWeightsRefTangentWeights
This function constructs the half of the tangent weight using the precomputed
half angle tangent and distance values. The returned value is
\f$\frac{2\textbf{tan05}}{\textbf{d}}\f$.
\brief computes the tangent weight in 2D at `q` using the points `p0`, `p1`,
and `p2`, given a traits class `traits` with geometric objects, predicates, and constructions.
*/
template<typename GeomTraits>
typename GeomTraits::FT tangent_weight(
const typename GeomTraits::Point_2& p0,
const typename GeomTraits::Point_2& p1,
const typename GeomTraits::Point_2& p2,
const typename GeomTraits::Point_2& q,
const GeomTraits& traits) { }
\tparam FT a model of `FieldNumberType`
/*!
\ingroup PkgWeightsRefTangentWeights
\param tan05 the half angle tangent value
\param d the distance value
\brief computes the tangent weight in 3D at `q` using the points `p0`, `p1`,
and `p2`, given a traits class `traits` with geometric objects, predicates, and constructions.
*/
template<typename GeomTraits>
typename GeomTraits::FT tangent_weight(
const typename GeomTraits::Point_3& p0,
const typename GeomTraits::Point_3& p1,
const typename GeomTraits::Point_3& p2,
const typename GeomTraits::Point_3& q,
const GeomTraits& traits) { }
\pre d != 0
/*!
\ingroup PkgWeightsRefTangentWeights
\sa `tangent_half_angle()`
\sa `tangent_weight()`
*/
template<typename FT>
FT half_tangent_weight(const FT tan05, const FT d)
{
return tangent_ns::half_weight(tan05, d);
}
\brief computes the tangent weight in 2D at `q` using the points `p0`, `p1`,
and `p2` which are parameterized by a `Kernel` K.
*/
template<typename K>
typename K::FT tangent_weight(
const CGAL::Point_2<K>& p0,
const CGAL::Point_2<K>& p1,
const CGAL::Point_2<K>& p2,
const CGAL::Point_2<K>& q) { }
/*!
\ingroup PkgWeightsRefTangentWeights
/*!
\ingroup PkgWeightsRefTangentWeights
\brief computes the tangent of the half angle.
\brief computes the tangent weight in 3D at `q` using the points `p0`, `p1`,
and `p2` which are parameterized by a `Kernel` K.
*/
template<typename K>
typename K::FT tangent_weight(
const CGAL::Point_3<K>& p0,
const CGAL::Point_3<K>& p1,
const CGAL::Point_3<K>& p2,
const CGAL::Point_3<K>& q) { }
This function computes the tangent of the half angle using the precomputed
distance, area, and dot product values. The returned value is
\f$\frac{2\textbf{A}}{\textbf{d}\textbf{l} + \textbf{D}}\f$.
#endif // DOXYGEN_RUNNING
\tparam FT a model of `FieldNumberType`
/// \cond SKIP_IN_MANUAL
template<typename GeomTraits>
typename GeomTraits::FT tangent_weight(
const typename GeomTraits::Point_2& t,
const typename GeomTraits::Point_2& r,
const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const GeomTraits& traits) {
\param d the distance value
\param l the distance value
\param A the area value
\param D the dot product value
using FT = typename GeomTraits::FT;
const auto dot_product_2 =
traits.compute_scalar_product_2_object();
const auto construct_vector_2 =
traits.construct_vector_2_object();
\pre (d * l + D) != 0
const auto v1 = construct_vector_2(q, t);
const auto v2 = construct_vector_2(q, r);
const auto v3 = construct_vector_2(q, p);
\sa `half_tangent_weight()`
*/
template<typename FT>
FT tangent_half_angle(const FT d, const FT l, const FT A, const FT D)
{
// tan(theta/2) = sin(theta) / ( 1 + cos(theta) ), also = (1 - cos(theta)) / sin(theta).
// = ( 2*A / |v1|*|v2| ) / ( 1 + v1.v2 / |v1|*|v2| )
// = 2*A / ( |v1|*|v2| + v1.v2 )
const FT l1 = internal::length_2(traits, v1);
const FT l2 = internal::length_2(traits, v2);
const FT l3 = internal::length_2(traits, v3);
FT t = FT(0);
const FT P = d * l + D;
CGAL_precondition(!is_zero(P));
if (!is_zero(P))
t = FT(2) * A / P;
const FT A1 = internal::area_2(traits, r, q, t);
const FT A2 = internal::area_2(traits, p, q, r);
return t;
}
const FT D1 = dot_product_2(v1, v2);
const FT D2 = dot_product_2(v2, v3);
/*!
\ingroup PkgWeightsRefTangentWeights
return tangent_ns::weight(
l1, l2, l3, A1, A2, D1, D2);
}
\brief computes the half value of the tangent weight.
template<typename GeomTraits>
typename GeomTraits::FT tangent_weight(
const CGAL::Point_2<GeomTraits>& t,
const CGAL::Point_2<GeomTraits>& r,
const CGAL::Point_2<GeomTraits>& p,
const CGAL::Point_2<GeomTraits>& q) {
This function constructs the half of the tangent weight using the precomputed
distance, area, and dot product values. The returned value is
\f$\frac{2\textbf{t}}{\textbf{d}}\f$ where
\f$\textbf{t} = \frac{2\textbf{A}}{\textbf{d}\textbf{l} + \textbf{D}}\f$.
const GeomTraits traits;
return tangent_weight(t, r, p, q, traits);
}
\tparam FT a model of `FieldNumberType`
template<typename GeomTraits>
typename GeomTraits::FT tangent_weight(
const typename GeomTraits::Point_3& t,
const typename GeomTraits::Point_3& r,
const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const GeomTraits& traits) {
\param d the distance value
\param l the distance value
\param A the area value
\param D the dot product value
// return tangent_ns::tangent_weight_v1(t, r, p, q, traits);
return tangent_ns::tangent_weight_v2(t, r, p, q, traits);
}
\pre (d * l + D) != 0 && d != 0
template<typename GeomTraits>
typename GeomTraits::FT tangent_weight(
const CGAL::Point_3<GeomTraits>& t,
const CGAL::Point_3<GeomTraits>& r,
const CGAL::Point_3<GeomTraits>& p,
const CGAL::Point_3<GeomTraits>& q) {
\sa `tangent_weight()`
*/
template<typename FT>
FT half_tangent_weight(const FT d, const FT l, const FT A, const FT D)
{
const FT tan05 = tangent_half_angle(d, l, A, D);
return tangent_ns::half_weight(tan05, d);
}
const GeomTraits traits;
return tangent_weight(t, r, p, q, traits);
}
/// \cond SKIP_IN_MANUAL
// Undocumented tangent weight class.
// Its constructor takes a polygon mesh and a vertex to point map
// and its operator() is defined based on the halfedge_descriptor only.
// This version is currently used in:
// Surface_mesh_parameterizer -> Iterative_authalic_parameterizer_3.h
template<
typename PolygonMesh,
typename VertexPointMap = typename boost::property_map<PolygonMesh, CGAL::vertex_point_t>::type>
class Edge_tangent_weight {
template<typename GeomTraits>
typename GeomTraits::FT half_tangent_weight(const typename GeomTraits::Point_2& p0,
const typename GeomTraits::Point_2& q,
const typename GeomTraits::Point_2& p2,
const GeomTraits& traits)
{
using FT = typename GeomTraits::FT;
using Vector_2 = typename GeomTraits::Vector_2;
using GeomTraits = typename CGAL::Kernel_traits<
typename boost::property_traits<VertexPointMap>::value_type>::type;
using FT = typename GeomTraits::FT;
auto vector_2 = traits.construct_vector_2_object();
auto dot_product_2 = traits.compute_scalar_product_2_object();
auto area_2 = traits.compute_area_2_object();
const PolygonMesh& m_pmesh;
const VertexPointMap m_pmap;
const GeomTraits m_traits;
const Vector_2 v0 = vector_2(q, p0);
const Vector_2 v2 = vector_2(q, p2);
public:
using vertex_descriptor = typename boost::graph_traits<PolygonMesh>::vertex_descriptor;
using halfedge_descriptor = typename boost::graph_traits<PolygonMesh>::halfedge_descriptor;
const FT l0 = internal::length_2(v0, traits);
const FT l2 = internal::length_2(v2, traits);
const FT A = area_2(p2, q, p0);
const FT D = dot_product_2(v0, v2);
Edge_tangent_weight(const PolygonMesh& pmesh, const VertexPointMap pmap) :
m_pmesh(pmesh), m_pmap(pmap), m_traits() { }
return half_tangent_weight(l0, l2, A, D);
}
FT operator()(const halfedge_descriptor he) const {
template<typename GeomTraits>
typename GeomTraits::FT half_tangent_weight(const typename GeomTraits::Point_3& p0,
const typename GeomTraits::Point_3& q,
const typename GeomTraits::Point_3& p2,
const GeomTraits& traits)
{
using FT = typename GeomTraits::FT;
using Vector_3 = typename GeomTraits::Vector_3;
FT weight = FT(0);
if (is_border_edge(he, m_pmesh)) {
const auto h1 = next(he, m_pmesh);
auto vector_3 = traits.construct_vector_3_object();
auto dot_product_3 = traits.compute_scalar_product_3_object();
const auto v0 = target(he, m_pmesh);
const auto v1 = source(he, m_pmesh);
const auto v2 = target(h1, m_pmesh);
const Vector_3 v0 = vector_3(q, p0);
const Vector_3 v2 = vector_3(q, p2);
const auto& p0 = get(m_pmap, v0);
const auto& p1 = get(m_pmap, v1);
const auto& p2 = get(m_pmap, v2);
const FT l0 = internal::length_3(v0, traits);
const FT l2 = internal::length_3(v2, traits);
const FT A = internal::area_3(p2, q, p0, traits);
const FT D = dot_product_3(v0, v2);
weight = internal::tangent_3(m_traits, p0, p2, p1);
return half_tangent_weight(l0, l2, A, D);
}
} else {
const auto h1 = next(he, m_pmesh);
const auto h2 = prev(opposite(he, m_pmesh), m_pmesh);
/// \endcond
const auto v0 = target(he, m_pmesh);
const auto v1 = source(he, m_pmesh);
const auto v2 = target(h1, m_pmesh);
const auto v3 = source(h2, m_pmesh);
// 2D ==============================================================================================
const auto& p0 = get(m_pmap, v0);
const auto& p1 = get(m_pmap, v1);
const auto& p2 = get(m_pmap, v2);
const auto& p3 = get(m_pmap, v3);
/*!
\ingroup PkgWeightsRefTangentWeights
\brief computes the tangent weight in 2D at `q` using the points `p0`, `p1`, and `p2`
\tparam GeomTraits a model of `AnalyticWeightTraits_2`
*/
template<typename GeomTraits>
typename GeomTraits::FT tangent_weight(const typename GeomTraits::Point_2& p0,
const typename GeomTraits::Point_2& p1,
const typename GeomTraits::Point_2& p2,
const typename GeomTraits::Point_2& q,
const GeomTraits& traits)
{
using FT = typename GeomTraits::FT;
using Vector_2 = typename GeomTraits::Vector_2;
weight = tangent_weight(p2, p1, p3, p0) / FT(2);
}
return weight;
auto vector_2 = traits.construct_vector_2_object();
auto dot_product_2 = traits.compute_scalar_product_2_object();
auto area_2 = traits.compute_area_2_object();
const Vector_2 v0 = vector_2(q, p0);
const Vector_2 v = vector_2(q, p1);
const Vector_2 v2 = vector_2(q, p2);
const FT l0 = internal::length_2(v0, traits);
const FT l = internal::length_2(v, traits);
const FT l2 = internal::length_2(v2, traits);
const FT A0 = area_2(p1, q, p0);
const FT A2 = area_2(p2, q, p1);
const FT D0 = dot_product_2(v0, v);
const FT D2 = dot_product_2(v, v2);
return tangent_ns::weight(l0, l2, l, A0, A2, D0, D2);
}
/*!
\ingroup PkgWeightsRefTangentWeights
\brief computes the tangent weight in 2D at `q` using the points `p0`, `p1`, and `p2`
\tparam Kernel a model of `Kernel`
*/
template<typename Kernel>
typename Kernel::FT tangent_weight(const CGAL::Point_2<Kernel>& p0,
const CGAL::Point_2<Kernel>& p1,
const CGAL::Point_2<Kernel>& p2,
const CGAL::Point_2<Kernel>& q)
{
const Kernel traits;
return tangent_weight(p0, p1, p2, q, traits);
}
// 3D ==============================================================================================
/*!
\ingroup PkgWeightsRefTangentWeights
\brief computes the tangent weight in 3D at `q` using the points `p0`, `p1`, and `p2`
\tparam GeomTraits a model of `AnalyticWeightTraits_3`
*/
template<typename GeomTraits>
typename GeomTraits::FT tangent_weight(const typename GeomTraits::Point_3& p0,
const typename GeomTraits::Point_3& p1,
const typename GeomTraits::Point_3& p2,
const typename GeomTraits::Point_3& q,
const GeomTraits& traits)
{
// return tangent_ns::tangent_weight_v1(p0, p1, p2, q, traits);
return tangent_ns::tangent_weight_v2(p0, p1, p2, q, traits);
}
/*!
\ingroup PkgWeightsRefTangentWeights
\brief computes the tangent weight in 3D at `q` using the points `p0`, `p1`, and `p2`
\tparam Kernel a model of `Kernel`
*/
template<typename Kernel>
typename Kernel::FT tangent_weight(const CGAL::Point_3<Kernel>& p0,
const CGAL::Point_3<Kernel>& p1,
const CGAL::Point_3<Kernel>& p2,
const CGAL::Point_3<Kernel>& q)
{
const Kernel traits;
return tangent_weight(p0, p1, p2, q, traits);
}
/// \cond SKIP_IN_MANUAL
// Undocumented tangent weight class.
//
// Its constructor takes a polygon mesh and a vertex to point map
// and its operator() is defined based on the halfedge_descriptor only.
// This version is currently used in:
// Surface_mesh_parameterizer -> Iterative_authalic_parameterizer_3.h
template<
typename PolygonMesh,
typename VertexPointMap,
typename GeomTraits>
class Edge_tangent_weight
{
using vertex_descriptor = typename boost::graph_traits<PolygonMesh>::vertex_descriptor;
using halfedge_descriptor = typename boost::graph_traits<PolygonMesh>::halfedge_descriptor;
using Point_ref = typename boost::property_traits<VertexPointMap>::reference;
using FT = typename GeomTraits::FT;
const PolygonMesh& m_pmesh;
const VertexPointMap m_pmap;
const GeomTraits m_traits;
public:
Edge_tangent_weight(const PolygonMesh& pmesh,
const VertexPointMap pmap,
const GeomTraits& traits)
: m_pmesh(pmesh), m_pmap(pmap), m_traits(traits)
{ }
FT operator()(halfedge_descriptor he) const
{
if(is_border(he, m_pmesh))
return FT(0);
FT weight = FT(0);
if (is_border_edge(he, m_pmesh)) // ie, opp(he, pmesh) is a border halfedge
{
const halfedge_descriptor h1 = next(he, m_pmesh);
const vertex_descriptor v0 = target(he, m_pmesh);
const vertex_descriptor v1 = source(he, m_pmesh);
const vertex_descriptor v2 = target(h1, m_pmesh);
const Point_ref p0 = get(m_pmap, v0);
const Point_ref p1 = get(m_pmap, v1);
const Point_ref p2 = get(m_pmap, v2);
weight = half_tangent_weight(p1, p0, p2, m_traits) / FT(2);
}
};
else
{
const halfedge_descriptor h1 = next(he, m_pmesh);
const halfedge_descriptor h2 = prev(opposite(he, m_pmesh), m_pmesh);
// Undocumented tangent weight class.
// Its constructor takes three points either in 2D or 3D.
// This version is currently used in:
// Surface_mesh_parameterizer -> MVC_post_processor_3.h
// Surface_mesh_parameterizer -> Orbifold_Tutte_parameterizer_3.h
template<typename FT>
class Tangent_weight {
FT m_d_r, m_d_p, m_w_base;
const vertex_descriptor v0 = target(he, m_pmesh);
const vertex_descriptor v1 = source(he, m_pmesh);
const vertex_descriptor v2 = target(h1, m_pmesh);
const vertex_descriptor v3 = source(h2, m_pmesh);
public:
template<typename GeomTraits>
Tangent_weight(
const CGAL::Point_2<GeomTraits>& p,
const CGAL::Point_2<GeomTraits>& q,
const CGAL::Point_2<GeomTraits>& r) {
const Point_ref p0 = get(m_pmap, v0);
const Point_ref p1 = get(m_pmap, v1);
const Point_ref p2 = get(m_pmap, v2);
const Point_ref p3 = get(m_pmap, v3);
const GeomTraits traits;
const auto scalar_product_2 =
traits.compute_scalar_product_2_object();
const auto construct_vector_2 =
traits.construct_vector_2_object();
m_d_r = internal::distance_2(traits, q, r);
CGAL_assertion(m_d_r != FT(0)); // two points are identical!
m_d_p = internal::distance_2(traits, q, p);
CGAL_assertion(m_d_p != FT(0)); // two points are identical!
const auto v1 = construct_vector_2(q, r);
const auto v2 = construct_vector_2(q, p);
const auto A = internal::positive_area_2(traits, p, q, r);
CGAL_assertion(A != FT(0)); // three points are identical!
const auto S = scalar_product_2(v1, v2);
m_w_base = -tangent_half_angle(m_d_r, m_d_p, A, S);
weight = tangent_weight(p2, p1, p3, p0, m_traits) / FT(2);
}
return weight;
}
};
template<typename GeomTraits>
Tangent_weight(
const CGAL::Point_3<GeomTraits>& p,
const CGAL::Point_3<GeomTraits>& q,
const CGAL::Point_3<GeomTraits>& r) {
// Undocumented tangent weight class.
// Returns - std::tan(theta/2); uses positive areas.
//
// Its constructor takes three points either in 2D or 3D.
// This version is currently used in:
// Surface_mesh_parameterizer -> MVC_post_processor_3.h
// Surface_mesh_parameterizer -> Orbifold_Tutte_parameterizer_3.h
template<typename FT>
class Tangent_weight
{
FT m_d_r, m_d_p, m_w_base;
const GeomTraits traits;
const auto scalar_product_3 =
traits.compute_scalar_product_3_object();
const auto construct_vector_3 =
traits.construct_vector_3_object();
public:
template<typename Kernel>
Tangent_weight(const CGAL::Point_2<Kernel>& p,
const CGAL::Point_2<Kernel>& q,
const CGAL::Point_2<Kernel>& r)
{
const Kernel traits;
m_d_r = internal::distance_3(traits, q, r);
CGAL_assertion(m_d_r != FT(0)); // two points are identical!
m_d_p = internal::distance_3(traits, q, p);
CGAL_assertion(m_d_p != FT(0)); // two points are identical!
using Vector_2 = typename Kernel::Vector_2;
const auto v1 = construct_vector_3(q, r);
const auto v2 = construct_vector_3(q, p);
auto vector_2 = traits.construct_vector_2_object();
auto scalar_product_2 = traits.compute_scalar_product_2_object();
const auto A = internal::positive_area_3(traits, p, q, r);
CGAL_assertion(A != FT(0)); // three points are identical!
const auto S = scalar_product_3(v1, v2);
m_w_base = -tangent_half_angle(m_d_r, m_d_p, A, S);
}
m_d_r = internal::distance_2(q, r, traits);
CGAL_assertion(is_positive(m_d_r)); // two points are identical!
m_d_p = internal::distance_2(q, p, traits);
CGAL_assertion(is_positive(m_d_p)); // two points are identical!
FT get_w_r() const {
return half_tangent_weight(m_w_base, m_d_r) / FT(2);
}
const Vector_2 v1 = vector_2(q, r);
const Vector_2 v2 = vector_2(q, p);
FT get_w_p() const {
return half_tangent_weight(m_w_base, m_d_p) / FT(2);
}
};
const FT A = internal::positive_area_2(p, q, r, traits);
CGAL_assertion(!is_zero(A));
/// \endcond
const FT S = scalar_product_2(v1, v2);
m_w_base = -tangent_half_angle(m_d_r, m_d_p, A, S);
}
template<typename Kernel>
Tangent_weight(const CGAL::Point_3<Kernel>& p,
const CGAL::Point_3<Kernel>& q,
const CGAL::Point_3<Kernel>& r)
{
const Kernel traits;
using Vector_3 = typename Kernel::Vector_3;
auto vector_3 = traits.construct_vector_3_object();
auto scalar_product_3 = traits.compute_scalar_product_3_object();
m_d_r = internal::distance_3(q, r, traits);
CGAL_assertion(is_positive(m_d_r)); // two points are identical!
m_d_p = internal::distance_3(q, p, traits);
CGAL_assertion(is_positive(m_d_p)); // two points are identical!
const Vector_3 v1 = vector_3(q, r);
const Vector_3 v2 = vector_3(q, p);
const FT A = internal::positive_area_3(p, q, r, traits);
CGAL_assertion(is_positive(A));
const FT S = scalar_product_3(v1, v2);
m_w_base = -tangent_half_angle(m_d_r, m_d_p, A, S);
}
FT get_w_r() const
{
return half_tangent_weight(m_w_base, m_d_r) / FT(2);
}
FT get_w_p() const
{
return half_tangent_weight(m_w_base, m_d_p) / FT(2);
}
};
/// \endcond
} // namespace Weights
} // namespace CGAL

View File

@ -14,155 +14,122 @@
#ifndef CGAL_THREE_POINT_FAMILY_WEIGHTS_H
#define CGAL_THREE_POINT_FAMILY_WEIGHTS_H
// Internal includes.
#include <CGAL/Weights/internal/utils.h>
#include <CGAL/Point_2.h>
#include <CGAL/Point_3.h>
namespace CGAL {
namespace Weights {
/// \cond SKIP_IN_MANUAL
namespace three_point_family_ns {
/// \cond SKIP_IN_MANUAL
namespace three_point_family_ns {
template<typename GeomTraits>
typename GeomTraits::FT weight(
const GeomTraits& traits,
const typename GeomTraits::FT d1,
const typename GeomTraits::FT d2,
const typename GeomTraits::FT d3,
const typename GeomTraits::FT A1,
const typename GeomTraits::FT A2,
const typename GeomTraits::FT B,
const typename GeomTraits::FT p) {
template<typename FT>
FT weight(const FT d0, const FT d2, const FT d,
const FT A0, const FT A2, const FT B,
const FT p)
{
FT w = FT(0);
CGAL_precondition(!is_zero(A0) && !is_zero(A2));
const FT prod = A0 * A2;
if (!is_zero(prod))
{
const FT r0 = internal::power(d0, p);
const FT r = internal::power(d , p);
const FT r2 = internal::power(d2, p);
using FT = typename GeomTraits::FT;
FT w = FT(0);
CGAL_precondition(A1 != FT(0) && A2 != FT(0));
const FT prod = A1 * A2;
if (prod != FT(0)) {
const FT inv = FT(1) / prod;
FT r1 = d1;
FT r2 = d2;
FT r3 = d3;
if (p != FT(1)) {
r1 = internal::power(traits, d1, p);
r2 = internal::power(traits, d2, p);
r3 = internal::power(traits, d3, p);
}
w = (r3 * A1 - r2 * B + r1 * A2) * inv;
}
return w;
}
w = (r2 * A0 - r * B + r0 * A2) / prod;
}
/// \endcond
return w;
}
#if defined(DOXYGEN_RUNNING)
} // namespace three_point_family_ns
/*!
\ingroup PkgWeightsRefThreePointFamilyWeights
/// \endcond
\brief computes the three-point family weight in 2D at `q` using the points `p0`, `p1`,
and `p2` and the power parameter `a`, given a traits class `traits` with geometric objects,
predicates, and constructions.
*/
template<typename GeomTraits>
typename GeomTraits::FT three_point_family_weight(
const typename GeomTraits::Point_2& p0,
const typename GeomTraits::Point_2& p1,
const typename GeomTraits::Point_2& p2,
const typename GeomTraits::Point_2& q,
const typename GeomTraits::FT a,
const GeomTraits& traits) { }
// 2D ==============================================================================================
/*!
\ingroup PkgWeightsRefThreePointFamilyWeights
/*!
\ingroup PkgWeightsRefThreePointFamilyWeights
\brief computes the three-point family weight in 2D at `q` using the points `p0`, `p1` and `p2`,
and the power parameter `a`.
\tparam GeomTraits a model of `AnalyticWeightTraits_2`
*/
template<typename GeomTraits>
typename GeomTraits::FT three_point_family_weight(const typename GeomTraits::Point_2& p0,
const typename GeomTraits::Point_2& p1,
const typename GeomTraits::Point_2& p2,
const typename GeomTraits::Point_2& q,
const typename GeomTraits::FT a,
const GeomTraits& traits)
{
using FT = typename GeomTraits::FT;
\brief computes the three-point family weight in 2D at `q` using the points `p0`, `p1`,
and `p2` which are parameterized by a `Kernel` K, and the power parameter `a` which
can be omitted.
*/
template<typename K>
typename K::FT three_point_family_weight(
const CGAL::Point_2<K>& p0,
const CGAL::Point_2<K>& p1,
const CGAL::Point_2<K>& p2,
const CGAL::Point_2<K>& q,
const typename K::FT a = typename K::FT(1)) { }
auto area_2 = traits.compute_area_2_object();
#endif // DOXYGEN_RUNNING
const FT d0 = internal::distance_2(q, p0, traits);
const FT d = internal::distance_2(q, p1, traits);
const FT d2 = internal::distance_2(q, p2, traits);
/// \cond SKIP_IN_MANUAL
template<typename GeomTraits>
typename GeomTraits::FT three_point_family_weight(
const typename GeomTraits::Point_2& t,
const typename GeomTraits::Point_2& r,
const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const typename GeomTraits::FT a,
const GeomTraits& traits) {
const FT A0 = area_2(p1, q, p0);
const FT A2 = area_2(p2, q, p1);
const FT B = area_2(p2, q, p0);
using FT = typename GeomTraits::FT;
const FT d1 = internal::distance_2(traits, q, t);
const FT d2 = internal::distance_2(traits, q, r);
const FT d3 = internal::distance_2(traits, q, p);
return three_point_family_ns::weight(d0, d2, d, A0, A2, B, a);
}
const FT A1 = internal::area_2(traits, r, q, t);
const FT A2 = internal::area_2(traits, p, q, r);
const FT B = internal::area_2(traits, p, q, t);
/*!
\ingroup PkgWeightsRefThreePointFamilyWeights
\brief computes the three-point family weight in 2D at `q` using the points `p0`, `p1` and `p2`,
and the power parameter `a`.
\tparam Kernel a model of `Kernel`
*/
template<typename Kernel>
typename Kernel::FT three_point_family_weight(const CGAL::Point_2<Kernel>& p0,
const CGAL::Point_2<Kernel>& p1,
const CGAL::Point_2<Kernel>& p2,
const CGAL::Point_2<Kernel>& q,
const typename Kernel::FT a = {1})
{
const Kernel traits;
return three_point_family_weight(p0, p1, p2, q, a, traits);
}
return three_point_family_ns::weight(
traits, d1, d2, d3, A1, A2, B, a);
}
// 3D ==============================================================================================
template<typename GeomTraits>
typename GeomTraits::FT three_point_family_weight(
const CGAL::Point_2<GeomTraits>& t,
const CGAL::Point_2<GeomTraits>& r,
const CGAL::Point_2<GeomTraits>& p,
const CGAL::Point_2<GeomTraits>& q,
const typename GeomTraits::FT a =
typename GeomTraits::FT(1)) {
/// \cond SKIP_IN_MANUAL
const GeomTraits traits;
return three_point_family_weight(t, r, p, q, a, traits);
}
template<typename GeomTraits>
typename GeomTraits::FT three_point_family_weight(const typename GeomTraits::Point_3& p0,
const typename GeomTraits::Point_3& p1,
const typename GeomTraits::Point_3& p2,
const typename GeomTraits::Point_3& q,
const typename GeomTraits::FT a,
const GeomTraits& traits)
{
using Point_2 = typename GeomTraits::Point_2;
namespace internal {
Point_2 p0f, p1f, p2f, qf;
internal::flatten(p0, p1, p2 , q,
p0f, p1f, p2f, qf,
traits);
template<typename GeomTraits>
typename GeomTraits::FT three_point_family_weight(
const typename GeomTraits::Point_3& t,
const typename GeomTraits::Point_3& r,
const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const typename GeomTraits::FT a,
const GeomTraits& traits) {
return CGAL::Weights::three_point_family_weight(p0f, p1f, p2f, qf, a, traits);
}
using Point_2 = typename GeomTraits::Point_2;
Point_2 tf, rf, pf, qf;
internal::flatten(
traits,
t, r, p, q,
tf, rf, pf, qf);
return CGAL::Weights::
three_point_family_weight(tf, rf, pf, qf, a, traits);
}
template<typename Kernel>
typename Kernel::FT three_point_family_weight(const CGAL::Point_3<Kernel>& p0,
const CGAL::Point_3<Kernel>& p1,
const CGAL::Point_3<Kernel>& p2,
const CGAL::Point_3<Kernel>& q,
const typename Kernel::FT a = {1})
{
const Kernel traits;
return three_point_family_weight(p0, p1, p2, q, a, traits);
}
template<typename GeomTraits>
typename GeomTraits::FT three_point_family_weight(
const CGAL::Point_3<GeomTraits>& t,
const CGAL::Point_3<GeomTraits>& r,
const CGAL::Point_3<GeomTraits>& p,
const CGAL::Point_3<GeomTraits>& q,
const typename GeomTraits::FT a =
typename GeomTraits::FT(1)) {
const GeomTraits traits;
return three_point_family_weight(t, r, p, q, a, traits);
}
} // namespace internal
/// \endcond
/// \endcond
} // namespace Weights
} // namespace CGAL

View File

@ -14,107 +14,73 @@
#ifndef CGAL_TRIANGULAR_REGION_WEIGHTS_H
#define CGAL_TRIANGULAR_REGION_WEIGHTS_H
// Internal includes.
#include <CGAL/Weights/internal/utils.h>
#include <CGAL/Point_2.h>
#include <CGAL/Point_3.h>
namespace CGAL {
namespace Weights {
#if defined(DOXYGEN_RUNNING)
// 2D ==============================================================================================
/*!
\ingroup PkgWeightsRefTriangularRegionWeights
/*!
\ingroup PkgWeightsRefTriangularRegionWeights
\brief computes the area of the triangular cell in 2D using the points `p`, `q`, and `r`
\tparam GeomTraits a model of `AnalyticWeightTraits_2`
*/
template<typename GeomTraits>
typename GeomTraits::FT triangular_area(const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const typename GeomTraits::Point_2& r,
const GeomTraits& traits)
{
return internal::positive_area_2(p, q, r, traits);
}
\brief computes the area of the triangular cell in 2D using the points `p`, `q`
and `r`, given a traits class `traits` with geometric objects, predicates, and constructions.
*/
template<typename GeomTraits>
typename GeomTraits::FT triangular_area(
const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const typename GeomTraits::Point_2& r,
const GeomTraits& traits) { }
/*!
\ingroup PkgWeightsRefTriangularRegionWeights
\brief computes the area of the triangular cell in 2D using the points `p`, `q`, and `r`.
\tparam Kernel a model of `Kernel`
*/
template<typename Kernel>
typename Kernel::FT triangular_area(const CGAL::Point_2<Kernel>& p,
const CGAL::Point_2<Kernel>& q,
const CGAL::Point_2<Kernel>& r)
{
const Kernel traits;
return triangular_area(p, q, r, traits);
}
/*!
\ingroup PkgWeightsRefTriangularRegionWeights
// 3D ==============================================================================================
\brief computes the area of the triangular cell in 3D using the points `p`, `q`
and `r`, given a traits class `traits` with geometric objects, predicates, and constructions.
*/
template<typename GeomTraits>
typename GeomTraits::FT triangular_area(
const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const typename GeomTraits::Point_3& r,
const GeomTraits& traits) { }
/*!
\ingroup PkgWeightsRefTriangularRegionWeights
\brief computes the area of the triangular cell in 3D using the points `p`, `q`, and `r`.
\tparam GeomTraits a model of `AnalyticWeightTraits_3`
*/
template<typename GeomTraits>
typename GeomTraits::FT triangular_area(const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const typename GeomTraits::Point_3& r,
const GeomTraits& traits)
{
return internal::positive_area_3(p, q, r, traits);
}
/*!
\ingroup PkgWeightsRefTriangularRegionWeights
\brief computes the area of the triangular cell in 2D using the points `p`, `q`
and `r` which are parameterized by a `Kernel` K.
*/
template<typename K>
typename K::FT triangular_area(
const CGAL::Point_2<K>& p,
const CGAL::Point_2<K>& q,
const CGAL::Point_2<K>& r) { }
/*!
\ingroup PkgWeightsRefTriangularRegionWeights
\brief computes the area of the triangular cell in 3D using the points `p`, `q`
and `r` which are parameterized by a `Kernel` K.
*/
template<typename K>
typename K::FT triangular_area(
const CGAL::Point_3<K>& p,
const CGAL::Point_3<K>& q,
const CGAL::Point_3<K>& r) { }
#endif // DOXYGEN_RUNNING
/// \cond SKIP_IN_MANUAL
template<typename GeomTraits>
typename GeomTraits::FT triangular_area(
const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const typename GeomTraits::Point_2& r,
const GeomTraits& traits) {
return internal::positive_area_2(traits, p, q, r);
}
template<typename GeomTraits>
typename GeomTraits::FT triangular_area(
const CGAL::Point_2<GeomTraits>& p,
const CGAL::Point_2<GeomTraits>& q,
const CGAL::Point_2<GeomTraits>& r) {
const GeomTraits traits;
return triangular_area(p, q, r, traits);
}
template<typename GeomTraits>
typename GeomTraits::FT triangular_area(
const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const typename GeomTraits::Point_3& r,
const GeomTraits& traits) {
return internal::positive_area_3(traits, p, q, r);
}
template<typename GeomTraits>
typename GeomTraits::FT triangular_area(
const CGAL::Point_3<GeomTraits>& p,
const CGAL::Point_3<GeomTraits>& q,
const CGAL::Point_3<GeomTraits>& r) {
const GeomTraits traits;
return triangular_area(p, q, r, traits);
}
/// \endcond
/*!
\ingroup PkgWeightsRefTriangularRegionWeights
\brief computes the area of the triangular cell in 3D using the points `p`, `q`, and `r`.
\tparam Kernel a model of `Kernel`
*/
template<typename Kernel>
typename Kernel::FT triangular_area(const CGAL::Point_3<Kernel>& p,
const CGAL::Point_3<Kernel>& q,
const CGAL::Point_3<Kernel>& r)
{
const Kernel traits;
return triangular_area(p, q, r, traits);
}
} // namespace Weights
} // namespace CGAL

View File

@ -14,109 +14,73 @@
#ifndef CGAL_UNIFORM_REGION_WEIGHTS_H
#define CGAL_UNIFORM_REGION_WEIGHTS_H
// Internal includes.
#include <CGAL/Weights/internal/utils.h>
#include <CGAL/Point_2.h>
#include <CGAL/Point_3.h>
namespace CGAL {
namespace Weights {
#if defined(DOXYGEN_RUNNING)
// 2D ==============================================================================================
/*!
\ingroup PkgWeightsRefUniformRegionWeights
/*!
\ingroup PkgWeightsRefUniformRegionWeights
\brief this function always returns `1`, given three 2D points.
\tparam GeomTraits a model of `AnalyticWeightTraits_2`
*/
template<typename GeomTraits>
typename GeomTraits::FT uniform_area(const typename GeomTraits::Point_2&,
const typename GeomTraits::Point_2&,
const typename GeomTraits::Point_2&,
const GeomTraits&)
{
using FT = typename GeomTraits::FT;
return FT(1);
}
\brief this function always returns 1, given three points in 2D and a traits class
with geometric objects, predicates, and constructions.
*/
template<typename GeomTraits>
typename GeomTraits::FT uniform_area(
const typename GeomTraits::Point_2&,
const typename GeomTraits::Point_2&,
const typename GeomTraits::Point_2&,
const GeomTraits&) { }
/*!
\ingroup PkgWeightsRefUniformRegionWeights
\brief this function always returns `1`, given three 2D points in 2D.
\tparam Kernel a model of `Kernel`
*/
template<typename Kernel>
typename Kernel::FT uniform_area(const CGAL::Point_2<Kernel>& p,
const CGAL::Point_2<Kernel>& q,
const CGAL::Point_2<Kernel>& r)
{
const Kernel traits;
return uniform_area(p, q, r, traits);
}
/*!
\ingroup PkgWeightsRefUniformRegionWeights
// 3D ==============================================================================================
\brief this function always returns 1, given three points in 3D and a traits class
with geometric objects, predicates, and constructions.
*/
template<typename GeomTraits>
typename GeomTraits::FT uniform_area(
const typename GeomTraits::Point_3&,
const typename GeomTraits::Point_3&,
const typename GeomTraits::Point_3&,
const GeomTraits&) { }
/*!
\ingroup PkgWeightsRefUniformRegionWeights
\brief this function always returns `1`, given three 3D points.
\tparam GeomTraits a model of `AnalyticWeightTraits_3`
*/
template<typename GeomTraits>
typename GeomTraits::FT uniform_area(const typename GeomTraits::Point_3&,
const typename GeomTraits::Point_3&,
const typename GeomTraits::Point_3&,
const GeomTraits&)
{
using FT = typename GeomTraits::FT;
return FT(1);
}
/*!
\ingroup PkgWeightsRefUniformRegionWeights
\brief this function always returns 1, given three points in 2D which are
parameterized by a `Kernel` K.
*/
template<typename K>
typename K::FT uniform_area(
const CGAL::Point_2<K>&,
const CGAL::Point_2<K>&,
const CGAL::Point_2<K>&) { }
/*!
\ingroup PkgWeightsRefUniformRegionWeights
\brief this function always returns 1, given three points in 3D which are
parameterized by a `Kernel` K.
*/
template<typename K>
typename K::FT uniform_area(
const CGAL::Point_3<K>&,
const CGAL::Point_3<K>&,
const CGAL::Point_3<K>&) { }
#endif // DOXYGEN_RUNNING
/// \cond SKIP_IN_MANUAL
template<typename GeomTraits>
typename GeomTraits::FT uniform_area(
const typename GeomTraits::Point_2&,
const typename GeomTraits::Point_2&,
const typename GeomTraits::Point_2&,
const GeomTraits&) {
using FT = typename GeomTraits::FT;
return FT(1);
}
template<typename GeomTraits>
typename GeomTraits::FT uniform_area(
const CGAL::Point_2<GeomTraits>& p,
const CGAL::Point_2<GeomTraits>& q,
const CGAL::Point_2<GeomTraits>& r) {
const GeomTraits traits;
return uniform_area(p, q, r, traits);
}
template<typename GeomTraits>
typename GeomTraits::FT uniform_area(
const typename GeomTraits::Point_3&,
const typename GeomTraits::Point_3&,
const typename GeomTraits::Point_3&,
const GeomTraits&) {
using FT = typename GeomTraits::FT;
return FT(1);
}
template<typename GeomTraits>
typename GeomTraits::FT uniform_area(
const CGAL::Point_3<GeomTraits>& p,
const CGAL::Point_3<GeomTraits>& q,
const CGAL::Point_3<GeomTraits>& r) {
const GeomTraits traits;
return uniform_area(p, q, r, traits);
}
/// \endcond
/*!
\ingroup PkgWeightsRefUniformRegionWeights
\brief this function always returns `1`, given three 3D points.
\tparam Kernel a model of `Kernel`
*/
template<typename Kernel>
typename Kernel::FT uniform_area(const CGAL::Point_3<Kernel>& p,
const CGAL::Point_3<Kernel>& q,
const CGAL::Point_3<Kernel>& r)
{
const Kernel traits;
return uniform_area(p, q, r, traits);
}
} // namespace Weights
} // namespace CGAL

View File

@ -14,134 +14,98 @@
#ifndef CGAL_UNIFORM_WEIGHTS_H
#define CGAL_UNIFORM_WEIGHTS_H
// Internal includes.
#include <CGAL/Weights/internal/utils.h>
#include <CGAL/Point_2.h>
#include <CGAL/Point_3.h>
#include <boost/graph/graph_traits.hpp>
namespace CGAL {
namespace Weights {
#if defined(DOXYGEN_RUNNING)
// 2D ==============================================================================================
/*!
\ingroup PkgWeightsRefUniformWeights
/*!
\ingroup PkgWeightsRefUniformWeights
\brief returns `1`.
\tparam GeomTraits a model of `AnalyticWeightTraits_2`
*/
template<typename GeomTraits>
typename GeomTraits::FT uniform_weight(const typename GeomTraits::Point_2&,
const typename GeomTraits::Point_2&,
const typename GeomTraits::Point_2&,
const typename GeomTraits::Point_2&,
const GeomTraits&)
{
return {1};
}
\brief this function always returns 1, given four points in 2D and a traits class
with geometric objects, predicates, and constructions.
*/
template<typename GeomTraits>
typename GeomTraits::FT uniform_weight(
const typename GeomTraits::Point_2&,
const typename GeomTraits::Point_2&,
const typename GeomTraits::Point_2&,
const typename GeomTraits::Point_2&,
const GeomTraits&) { }
/*!
\ingroup PkgWeightsRefUniformWeights
\brief returns `1`.
\tparam Kernel a model of `Kernel`
*/
template<typename GeomTraits>
typename GeomTraits::FT uniform_weight(const CGAL::Point_2<GeomTraits>& p0,
const CGAL::Point_2<GeomTraits>& p1,
const CGAL::Point_2<GeomTraits>& p2,
const CGAL::Point_2<GeomTraits>& q)
{
const GeomTraits traits;
return uniform_weight(p0, p1, p2, q, traits);
}
/*!
\ingroup PkgWeightsRefUniformWeights
// 3D ==============================================================================================
\brief this function always returns 1, given four points in 3D and a traits class
with geometric objects, predicates, and constructions.
*/
template<typename GeomTraits>
typename GeomTraits::FT uniform_weight(
const typename GeomTraits::Point_3&,
const typename GeomTraits::Point_3&,
const typename GeomTraits::Point_3&,
const typename GeomTraits::Point_3&,
const GeomTraits&) { }
/*!
\ingroup PkgWeightsRefUniformWeights
\brief returns `1`.
\tparam GeomTraits a model of `AnalyticWeightTraits_2`
*/
template<typename GeomTraits>
typename GeomTraits::FT uniform_weight(const typename GeomTraits::Point_3&,
const typename GeomTraits::Point_3&,
const typename GeomTraits::Point_3&,
const typename GeomTraits::Point_3&,
const GeomTraits&)
{
return {1};
}
/*!
\ingroup PkgWeightsRefUniformWeights
/*!
\ingroup PkgWeightsRefUniformWeights
\brief returns `1`.
\tparam Kernel a model of `Kernel`
*/
template<typename GeomTraits>
typename GeomTraits::FT uniform_weight(const CGAL::Point_3<GeomTraits>& p0,
const CGAL::Point_3<GeomTraits>& p1,
const CGAL::Point_3<GeomTraits>& p2,
const CGAL::Point_3<GeomTraits>& q)
{
const GeomTraits traits;
return uniform_weight(p0, p1, p2, q, traits);
}
\brief this function always returns 1, given four points in 2D which are
parameterized by a `Kernel` K.
*/
template<typename K>
typename K::FT uniform_weight(
const CGAL::Point_2<K>&,
const CGAL::Point_2<K>&,
const CGAL::Point_2<K>&,
const CGAL::Point_2<K>&) { }
/// \cond SKIP_IN_MANUAL
/*!
\ingroup PkgWeightsRefUniformWeights
// Undocumented uniform weight class taking as input a polygon mesh.
//
// It is currently used in:
// Polygon_mesh_processing -> triangulate_hole_Polyhedron_3_test.cpp
// Polygon_mesh_processing -> triangulate_hole_Polyhedron_3_no_delaunay_test.cpp
// Polyhedron demo -> Fairing_plugin.cpp
// Polyhedron demo -> Hole_filling_plugin.cpp
template<class PolygonMesh>
class Uniform_weight
{
public:
using vertex_descriptor = typename boost::graph_traits<PolygonMesh>::vertex_descriptor;
using halfedge_descriptor = typename boost::graph_traits<PolygonMesh>::halfedge_descriptor;
double w_i(vertex_descriptor) { return 1.; }
double w_ij(halfedge_descriptor) { return 1.; }
};
\brief this function always returns 1, given four points in 3D which are
parameterized by a `Kernel` K.
*/
template<typename K>
typename K::FT uniform_weight(
const CGAL::Point_3<K>&,
const CGAL::Point_3<K>&,
const CGAL::Point_3<K>&,
const CGAL::Point_3<K>&) { }
#endif // DOXYGEN_RUNNING
/// \cond SKIP_IN_MANUAL
template<typename GeomTraits>
typename GeomTraits::FT uniform_weight(
const typename GeomTraits::Point_2&,
const typename GeomTraits::Point_2&,
const typename GeomTraits::Point_2&,
const typename GeomTraits::Point_2&,
const GeomTraits&) {
using FT = typename GeomTraits::FT;
return FT(1);
}
template<typename GeomTraits>
typename GeomTraits::FT uniform_weight(
const CGAL::Point_2<GeomTraits>& q,
const CGAL::Point_2<GeomTraits>& t,
const CGAL::Point_2<GeomTraits>& r,
const CGAL::Point_2<GeomTraits>& p) {
const GeomTraits traits;
return uniform_weight(q, t, r, p, traits);
}
template<typename GeomTraits>
typename GeomTraits::FT uniform_weight(
const typename GeomTraits::Point_3&,
const typename GeomTraits::Point_3&,
const typename GeomTraits::Point_3&,
const typename GeomTraits::Point_3&,
const GeomTraits&) {
using FT = typename GeomTraits::FT;
return FT(1);
}
template<typename GeomTraits>
typename GeomTraits::FT uniform_weight(
const CGAL::Point_3<GeomTraits>& q,
const CGAL::Point_3<GeomTraits>& t,
const CGAL::Point_3<GeomTraits>& r,
const CGAL::Point_3<GeomTraits>& p) {
const GeomTraits traits;
return uniform_weight(q, t, r, p, traits);
}
// Undocumented uniform weight class taking as input a polygon mesh.
// It is currently used in:
// Polygon_mesh_processing -> triangulate_hole_Polyhedron_3_test.cpp
// Polygon_mesh_processing -> triangulate_hole_Polyhedron_3_no_delaunay_test.cpp
// Polyhedron demo -> Fairing_plugin.cpp
// Polyhedron demo -> Hole_filling_plugin.cpp
template<class PolygonMesh>
class Uniform_weight {
public:
using vertex_descriptor = typename boost::graph_traits<PolygonMesh>::vertex_descriptor;
using halfedge_descriptor = typename boost::graph_traits<PolygonMesh>::halfedge_descriptor;
double w_i(vertex_descriptor) { return 1; }
double w_ij(halfedge_descriptor) { return 1; }
};
/// \endcond
/// \endcond
} // namespace Weights
} // namespace CGAL

View File

@ -14,193 +14,242 @@
#ifndef CGAL_WEIGHTS_UTILS_H
#define CGAL_WEIGHTS_UTILS_H
// Internal includes.
#include <CGAL/Weights/internal/utils.h>
#include <boost/algorithm/clamp.hpp>
namespace CGAL {
namespace Weights {
/// \cond SKIP_IN_MANUAL
template<typename GeomTraits>
typename GeomTraits::FT tangent(
const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const typename GeomTraits::Point_2& r,
const GeomTraits& traits) {
/// \cond SKIP_IN_MANUAL
return internal::tangent_2(traits, p, q, r);
// Computes cotangent between two 2D vectors.
template<typename GeomTraits>
typename GeomTraits::FT cotangent_2(const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const typename GeomTraits::Point_2& r,
const GeomTraits& traits)
{
using FT = typename GeomTraits::FT;
using Vector_2 = typename GeomTraits::Vector_2;
auto dot_product_2 = traits.compute_scalar_product_2_object();
auto determinant_2 = traits.compute_determinant_2_object();
auto vector_2 = traits.construct_vector_2_object();
const Vector_2 v1 = vector_2(q, r);
const Vector_2 v2 = vector_2(q, p);
const FT dot = dot_product_2(v1, v2);
const FT length = CGAL::abs(determinant_2(v1, v2));
if (!is_zero(length))
return dot / length;
return FT(0); // undefined
}
template<typename GeomTraits>
typename GeomTraits::FT cotangent(const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const typename GeomTraits::Point_2& r,
const GeomTraits& traits)
{
return cotangent_2(p, q, r, traits);
}
template<typename Kernel>
typename Kernel::FT cotangent(const CGAL::Point_2<Kernel>& p,
const CGAL::Point_2<Kernel>& q,
const CGAL::Point_2<Kernel>& r)
{
const Kernel traits;
return cotangent(p, q, r, traits);
}
// =================================================================================================
// Computes cotangent between two 3D vectors.
template<typename GeomTraits>
typename GeomTraits::FT cotangent_3(const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const typename GeomTraits::Point_3& r,
const GeomTraits& traits)
{
using FT = typename GeomTraits::FT;
using Vector_3 = typename GeomTraits::Vector_3;
auto dot_product_3 = traits.compute_scalar_product_3_object();
auto cross_product_3 = traits.construct_cross_product_vector_3_object();
auto vector_3 = traits.construct_vector_3_object();
const Vector_3 v1 = vector_3(q, r);
const Vector_3 v2 = vector_3(q, p);
const FT dot = dot_product_3(v1, v2);
const Vector_3 cross = cross_product_3(v1, v2);
const FT length = internal::length_3(cross, traits);
if (!is_zero(length))
return dot / length;
return FT(0); // undefined
}
template<typename GeomTraits>
typename GeomTraits::FT cotangent(const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const typename GeomTraits::Point_3& r,
const GeomTraits& traits)
{
return cotangent_3(p, q, r, traits);
}
template<typename Kernel>
typename Kernel::FT cotangent(const CGAL::Point_3<Kernel>& p,
const CGAL::Point_3<Kernel>& q,
const CGAL::Point_3<Kernel>& r)
{
const Kernel traits;
return cotangent(p, q, r, traits);
}
// =================================================================================================
// Computes tangent between two 2D vectors.
template<typename GeomTraits>
typename GeomTraits::FT tangent_2(const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const typename GeomTraits::Point_2& r,
const GeomTraits& traits)
{
using FT = typename GeomTraits::FT;
using Vector_2 = typename GeomTraits::Vector_2;
auto dot_product_2 = traits.compute_scalar_product_2_object();
auto determinant_2 = traits.compute_determinant_2_object();
auto vector_2 = traits.construct_vector_2_object();
const Vector_2 v1 = vector_2(q, r);
const Vector_2 v2 = vector_2(q, p);
const FT dot = dot_product_2(v1, v2);
if (!is_zero(dot))
{
const FT cross = determinant_2(v1, v2);
const FT length = CGAL::abs(cross);
return length / dot;
}
template<typename GeomTraits>
typename GeomTraits::FT tangent(
const CGAL::Point_2<GeomTraits>& p,
const CGAL::Point_2<GeomTraits>& q,
const CGAL::Point_2<GeomTraits>& r) {
return FT(0); // undefined
}
const GeomTraits traits;
return tangent(p, q, r, traits);
template<typename GeomTraits>
typename GeomTraits::FT tangent(const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const typename GeomTraits::Point_2& r,
const GeomTraits& traits)
{
return tangent_2(p, q, r, traits);
}
template<typename Kernel>
typename Kernel::FT tangent(const CGAL::Point_2<Kernel>& p,
const CGAL::Point_2<Kernel>& q,
const CGAL::Point_2<Kernel>& r)
{
const Kernel traits;
return tangent(p, q, r, traits);
}
// =================================================================================================
// Computes tangent between two 3D vectors.
template<typename GeomTraits>
typename GeomTraits::FT tangent_3(const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const typename GeomTraits::Point_3& r,
const GeomTraits& traits)
{
using FT = typename GeomTraits::FT;
using Vector_3 = typename GeomTraits::Vector_3;
auto dot_product_3 = traits.compute_scalar_product_3_object();
auto cross_product_3 = traits.construct_cross_product_vector_3_object();
auto vector_3 = traits.construct_vector_3_object();
const Vector_3 v1 = vector_3(q, r);
const Vector_3 v2 = vector_3(q, p);
const FT dot = dot_product_3(v1, v2);
if (!is_zero(dot))
{
const Vector_3 cross = cross_product_3(v1, v2);
const FT length = internal::length_3(cross, traits);
return length / dot;
}
template<typename GeomTraits>
typename GeomTraits::FT tangent(
const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const typename GeomTraits::Point_3& r,
const GeomTraits& traits) {
return FT(0); // undefined
}
return internal::tangent_3(traits, p, q, r);
}
template<typename GeomTraits>
typename GeomTraits::FT tangent(const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const typename GeomTraits::Point_3& r,
const GeomTraits& traits)
{
return tangent_3(p, q, r, traits);
}
template<typename GeomTraits>
typename GeomTraits::FT tangent(
const CGAL::Point_3<GeomTraits>& p,
const CGAL::Point_3<GeomTraits>& q,
const CGAL::Point_3<GeomTraits>& r) {
template<typename Kernel>
typename Kernel::FT tangent(const CGAL::Point_3<Kernel>& p,
const CGAL::Point_3<Kernel>& q,
const CGAL::Point_3<Kernel>& r)
{
const Kernel traits;
return tangent(p, q, r, traits);
}
const GeomTraits traits;
return tangent(p, q, r, traits);
}
// =================================================================================================
template<typename GeomTraits>
typename GeomTraits::FT cotangent(
const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const typename GeomTraits::Point_2& r,
const GeomTraits& traits) {
// Computes a clamped cotangent between two 3D vectors.
// In the old version of weights in PMP, it was called "Cotangent_value_Meyer_secure".
// See Weights/internal/pmp_weights_deprecated.h for more information.
template<typename GeomTraits>
typename GeomTraits::FT cotangent_3_clamped(const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const typename GeomTraits::Point_3& r,
const GeomTraits& traits)
{
using FT = typename GeomTraits::FT;
using Vector_3 = typename GeomTraits::Vector_3;
return internal::cotangent_2(traits, p, q, r);
}
using Get_sqrt = internal::Get_sqrt<GeomTraits>;
auto sqrt = Get_sqrt::sqrt_object(traits);
template<typename GeomTraits>
typename GeomTraits::FT cotangent(
const CGAL::Point_2<GeomTraits>& p,
const CGAL::Point_2<GeomTraits>& q,
const CGAL::Point_2<GeomTraits>& r) {
auto dot_product_3 = traits.compute_scalar_product_3_object();
auto vector_3 = traits.construct_vector_3_object();
const GeomTraits traits;
return cotangent(p, q, r, traits);
}
const Vector_3 v1 = vector_3(q, r);
const Vector_3 v2 = vector_3(q, p);
template<typename GeomTraits>
typename GeomTraits::FT cotangent(
const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const typename GeomTraits::Point_3& r,
const GeomTraits& traits) {
const FT dot = dot_product_3(v1, v2);
const FT length_v1 = internal::length_3(v1, traits);
const FT length_v2 = internal::length_3(v2, traits);
return internal::cotangent_3(traits, p, q, r);
}
const FT lb = -FT(999) / FT(1000),
ub = FT(999) / FT(1000);
const FT cosine = boost::algorithm::clamp<FT>(dot / (length_v1 * length_v2), lb, ub);
const FT sine = sqrt(FT(1) - square(cosine));
template<typename GeomTraits>
typename GeomTraits::FT cotangent(
const CGAL::Point_3<GeomTraits>& p,
const CGAL::Point_3<GeomTraits>& q,
const CGAL::Point_3<GeomTraits>& r) {
CGAL_assertion(!is_zero(sine));
if (!is_zero(sine))
return cosine / sine;
const GeomTraits traits;
return cotangent(p, q, r, traits);
}
/// \endcond
return FT(0); // undefined
}
/// \cond SKIP_IN_MANUAL
// These are free functions to be used when building weights from parts rather
// than using the predefined weight functions. In principle, they can be removed.
// They are here to have unified interface within the Weights package and its
// construction weight system.
template<typename GeomTraits>
typename GeomTraits::FT squared_distance(
const CGAL::Point_2<GeomTraits>& p,
const CGAL::Point_2<GeomTraits>& q) {
const GeomTraits traits;
const auto squared_distance_2 =
traits.compute_squared_distance_2_object();
return squared_distance_2(p, q);
}
template<typename GeomTraits>
typename GeomTraits::FT squared_distance(
const CGAL::Point_3<GeomTraits>& p,
const CGAL::Point_3<GeomTraits>& q) {
const GeomTraits traits;
const auto squared_distance_3 =
traits.compute_squared_distance_3_object();
return squared_distance_3(p, q);
}
template<typename GeomTraits>
typename GeomTraits::FT distance(
const CGAL::Point_2<GeomTraits>& p,
const CGAL::Point_2<GeomTraits>& q) {
const GeomTraits traits;
return internal::distance_2(traits, p, q);
}
template<typename GeomTraits>
typename GeomTraits::FT distance(
const CGAL::Point_3<GeomTraits>& p,
const CGAL::Point_3<GeomTraits>& q) {
const GeomTraits traits;
return internal::distance_3(traits, p, q);
}
template<typename GeomTraits>
typename GeomTraits::FT area(
const CGAL::Point_2<GeomTraits>& p,
const CGAL::Point_2<GeomTraits>& q,
const CGAL::Point_2<GeomTraits>& r) {
const GeomTraits traits;
return internal::area_2(traits, p, q, r);
}
template<typename GeomTraits>
typename GeomTraits::FT area(
const CGAL::Point_3<GeomTraits>& p,
const CGAL::Point_3<GeomTraits>& q,
const CGAL::Point_3<GeomTraits>& r) {
const GeomTraits traits;
return internal::positive_area_3(traits, p, q, r);
}
template<typename GeomTraits>
typename GeomTraits::FT scalar_product(
const CGAL::Point_2<GeomTraits>& p,
const CGAL::Point_2<GeomTraits>& q,
const CGAL::Point_2<GeomTraits>& r) {
const GeomTraits traits;
const auto scalar_product_2 =
traits.compute_scalar_product_2_object();
const auto construct_vector_2 =
traits.construct_vector_2_object();
const auto v1 = construct_vector_2(q, r);
const auto v2 = construct_vector_2(q, p);
return scalar_product_2(v1, v2);
}
template<typename GeomTraits>
typename GeomTraits::FT scalar_product(
const CGAL::Point_3<GeomTraits>& p,
const CGAL::Point_3<GeomTraits>& q,
const CGAL::Point_3<GeomTraits>& r) {
const GeomTraits traits;
const auto scalar_product_3 =
traits.compute_scalar_product_3_object();
const auto construct_vector_3 =
traits.construct_vector_3_object();
const auto v1 = construct_vector_3(q, r);
const auto v2 = construct_vector_3(q, p);
return scalar_product_3(v1, v2);
}
/// \endcond
/// \endcond
} // namespace Weights
} // namespace CGAL

View File

@ -14,131 +14,99 @@
#ifndef CGAL_VORONOI_REGION_WEIGHTS_H
#define CGAL_VORONOI_REGION_WEIGHTS_H
// Internal includes.
#include <CGAL/Weights/internal/utils.h>
#include <CGAL/Point_2.h>
#include <CGAL/Point_3.h>
namespace CGAL {
namespace Weights {
#if defined(DOXYGEN_RUNNING)
// 2D ==============================================================================================
/*!
\ingroup PkgWeightsRefVoronoiRegionWeights
/*!
\ingroup PkgWeightsRefVoronoiRegionWeights
\brief computes the area of the Voronoi cell in 2D using the points `p`, `q`, and `r`.
\tparam GeomTraits a model of `AnalyticWeightTraits_2`
*/
template<typename GeomTraits>
typename GeomTraits::FT voronoi_area(const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const typename GeomTraits::Point_2& r,
const GeomTraits& traits)
{
using FT = typename GeomTraits::FT;
using Point_2 = typename GeomTraits::Point_2;
\brief computes the area of the Voronoi cell in 2D using the points `p`, `q`
and `r`, given a traits class `traits` with geometric objects, predicates, and constructions.
*/
template<typename GeomTraits>
typename GeomTraits::FT voronoi_area(
const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const typename GeomTraits::Point_2& r,
const GeomTraits& traits) { }
auto circumcenter_2 = traits.construct_circumcenter_2_object();
auto midpoint_2 = traits.construct_midpoint_2_object();
/*!
\ingroup PkgWeightsRefVoronoiRegionWeights
const Point_2 center = circumcenter_2(p, q, r);
const Point_2 m1 = midpoint_2(q, r);
const Point_2 m2 = midpoint_2(q, p);
\brief computes the area of the Voronoi cell in 3D using the points `p`, `q`
and `r`, given a traits class `traits` with geometric objects, predicates, and constructions.
*/
template<typename GeomTraits>
typename GeomTraits::FT voronoi_area(
const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const typename GeomTraits::Point_3& r,
const GeomTraits& traits) { }
const FT A1 = internal::positive_area_2(q, m1, center,traits);
const FT A2 = internal::positive_area_2(q, center, m2, traits);
/*!
\ingroup PkgWeightsRefVoronoiRegionWeights
return A1 + A2;
}
\brief computes the area of the Voronoi cell in 2D using the points `p`, `q`
and `r` which are parameterized by a `Kernel` K.
*/
template<typename K>
typename K::FT voronoi_area(
const CGAL::Point_2<K>& p,
const CGAL::Point_2<K>& q,
const CGAL::Point_2<K>& r) { }
/*!
\ingroup PkgWeightsRefVoronoiRegionWeights
\brief computes the area of the Voronoi cell in 2D using the points `p`, `q`, and `r`.
\tparam Kernel a model of `Kernel`
*/
template<typename Kernel>
typename Kernel::FT voronoi_area(const CGAL::Point_2<Kernel>& p,
const CGAL::Point_2<Kernel>& q,
const CGAL::Point_2<Kernel>& r)
{
const Kernel traits;
return voronoi_area(p, q, r, traits);
}
/*!
\ingroup PkgWeightsRefVoronoiRegionWeights
// 3D ==============================================================================================
\brief computes the area of the Voronoi cell in 3D using the points `p`, `q`
and `r` which are parameterized by a `Kernel` K.
*/
template<typename K>
typename K::FT voronoi_area(
const CGAL::Point_3<K>& p,
const CGAL::Point_3<K>& q,
const CGAL::Point_3<K>& r) { }
/*!
\ingroup PkgWeightsRefVoronoiRegionWeights
\brief computes the area of the Voronoi cell in 3D using the points `p`, `q`, and `r`
\tparam GeomTraits a model of `AnalyticWeightTraits_3`.
*/
template<typename GeomTraits>
typename GeomTraits::FT voronoi_area(const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const typename GeomTraits::Point_3& r,
const GeomTraits& traits)
{
using FT = typename GeomTraits::FT;
using Point_3 = typename GeomTraits::Point_3;
#endif // DOXYGEN_RUNNING
auto circumcenter_3 = traits.construct_circumcenter_3_object();
auto midpoint_3 = traits.construct_midpoint_3_object();
/// \cond SKIP_IN_MANUAL
template<typename GeomTraits>
typename GeomTraits::FT voronoi_area(
const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const typename GeomTraits::Point_2& r,
const GeomTraits& traits) {
const Point_3 center = circumcenter_3(p, q, r);
const Point_3 m1 = midpoint_3(q, r);
const Point_3 m2 = midpoint_3(q, p);
using FT = typename GeomTraits::FT;
const auto circumcenter_2 =
traits.construct_circumcenter_2_object();
const auto midpoint_2 =
traits.construct_midpoint_2_object();
const FT A1 = internal::positive_area_3(q, m1, center, traits);
const FT A2 = internal::positive_area_3(q, center, m2, traits);
const auto center = circumcenter_2(p, q, r);
const auto m1 = midpoint_2(q, r);
const auto m2 = midpoint_2(q, p);
return A1 + A2;
}
const FT A1 = internal::positive_area_2(traits, q, m1, center);
const FT A2 = internal::positive_area_2(traits, q, center, m2);
return A1 + A2;
}
template<typename GeomTraits>
typename GeomTraits::FT voronoi_area(
const CGAL::Point_2<GeomTraits>& p,
const CGAL::Point_2<GeomTraits>& q,
const CGAL::Point_2<GeomTraits>& r) {
const GeomTraits traits;
return voronoi_area(p, q, r, traits);
}
template<typename GeomTraits>
typename GeomTraits::FT voronoi_area(
const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const typename GeomTraits::Point_3& r,
const GeomTraits& traits) {
using FT = typename GeomTraits::FT;
const auto circumcenter_3 =
traits.construct_circumcenter_3_object();
const auto midpoint_3 =
traits.construct_midpoint_3_object();
const auto center = circumcenter_3(p, q, r);
const auto m1 = midpoint_3(q, r);
const auto m2 = midpoint_3(q, p);
const FT A1 = internal::positive_area_3(traits, q, m1, center);
const FT A2 = internal::positive_area_3(traits, q, center, m2);
return A1 + A2;
}
template<typename GeomTraits>
typename GeomTraits::FT voronoi_area(
const CGAL::Point_3<GeomTraits>& p,
const CGAL::Point_3<GeomTraits>& q,
const CGAL::Point_3<GeomTraits>& r) {
const GeomTraits traits;
return voronoi_area(p, q, r, traits);
}
/// \endcond
/*!
\ingroup PkgWeightsRefVoronoiRegionWeights
\brief computes the area of the Voronoi cell in 3D using the points `p`, `q`, and `r`.
\tparam Kernel a model of `Kernel`
*/
template<typename Kernel>
typename Kernel::FT voronoi_area(const CGAL::Point_3<Kernel>& p,
const CGAL::Point_3<Kernel>& q,
const CGAL::Point_3<Kernel>& r)
{
const Kernel traits;
return voronoi_area(p, q, r, traits);
}
} // namespace Weights
} // namespace CGAL

View File

@ -14,409 +14,373 @@
#ifndef CGAL_WACHSPRESS_WEIGHTS_H
#define CGAL_WACHSPRESS_WEIGHTS_H
// Internal includes.
#include <CGAL/Weights/internal/utils.h>
#include <CGAL/Weights/internal/polygon_utils_2.h>
#include <CGAL/Point_2.h>
#include <CGAL/Point_3.h>
#include <CGAL/property_map.h>
#include <vector>
namespace CGAL {
namespace Weights {
/// \cond SKIP_IN_MANUAL
namespace wachspress_ns {
/// \cond SKIP_IN_MANUAL
template<typename FT>
FT weight(const FT A1, const FT A2, const FT C) {
namespace wachspress_ns {
FT w = FT(0);
CGAL_precondition(A1 != FT(0) && A2 != FT(0));
const FT prod = A1 * A2;
if (prod != FT(0)) {
const FT inv = FT(1) / prod;
w = C * inv;
}
return w;
}
}
/// \endcond
template<typename FT>
FT weight(const FT A0, const FT A2, const FT C)
{
FT w = FT(0);
CGAL_precondition(!is_zero(A0) && !is_zero(A2));
const FT prod = A0 * A2;
if (!is_zero(prod))
w = C / prod;
#if defined(DOXYGEN_RUNNING)
return w;
}
/*!
\ingroup PkgWeightsRefWachspressWeights
} // namespace wachspress_ns
\brief computes the Wachspress weight in 2D at `q` using the points `p0`, `p1`,
and `p2`, given a traits class `traits` with geometric objects, predicates, and constructions.
*/
template<typename GeomTraits>
typename GeomTraits::FT wachspress_weight(
const typename GeomTraits::Point_2& p0,
const typename GeomTraits::Point_2& p1,
const typename GeomTraits::Point_2& p2,
const typename GeomTraits::Point_2& q,
const GeomTraits& traits) { }
/// \endcond
/*!
\ingroup PkgWeightsRefWachspressWeights
// 2D ==============================================================================================
\brief computes the Wachspress weight in 2D at `q` using the points `p0`, `p1`,
and `p2` which are parameterized by a `Kernel` K.
*/
template<typename K>
typename K::FT wachspress_weight(
const CGAL::Point_2<K>& p0,
const CGAL::Point_2<K>& p1,
const CGAL::Point_2<K>& p2,
const CGAL::Point_2<K>& q) { }
/*!
\ingroup PkgWeightsRefWachspressWeights
\brief computes the Wachspress weight in 2D at `q` using the points `p0`, `p1`, and `p2`.
\tparam GeomTraits a model of `AnalyticWeightTraits_2`
*/
template<typename GeomTraits>
typename GeomTraits::FT wachspress_weight(const typename GeomTraits::Point_2& p0,
const typename GeomTraits::Point_2& p1,
const typename GeomTraits::Point_2& p2,
const typename GeomTraits::Point_2& q,
const GeomTraits& traits)
{
using FT = typename GeomTraits::FT;
#endif // DOXYGEN_RUNNING
auto area_2 = traits.compute_area_2_object();
const FT A0 = area_2(p1, q, p0);
const FT A2 = area_2(p2, q, p1);
const FT C = area_2(p0, p1, p2);
return wachspress_ns::weight(A0, A2, C);
}
/*!
\ingroup PkgWeightsRefWachspressWeights
\brief computes the Wachspress weight in 2D at `q` using the points `p0`, `p1`, and `p2`.
\tparam Kernel a model of `Kernel`
*/
template<typename Kernel>
typename Kernel::FT wachspress_weight(const CGAL::Point_2<Kernel>& p0,
const CGAL::Point_2<Kernel>& p1,
const CGAL::Point_2<Kernel>& p2,
const CGAL::Point_2<Kernel>& q)
{
const Kernel traits;
return wachspress_weight(p0, p1, p2, q, traits);
}
// 3D ==============================================================================================
/// \cond SKIP_IN_MANUAL
template<typename GeomTraits>
typename GeomTraits::FT wachspress_weight(const typename GeomTraits::Point_3& p0,
const typename GeomTraits::Point_3& p1,
const typename GeomTraits::Point_3& p2,
const typename GeomTraits::Point_3& q,
const GeomTraits& traits)
{
using Point_2 = typename GeomTraits::Point_2;
Point_2 p0f, p1f, p2f, qf;
internal::flatten(p0, p1, p2, q,
p0f, p1f, p2f, qf,
traits);
return CGAL::Weights::wachspress_weight(p0f, p1f, p2f, qf, traits);
}
template<typename Kernel>
typename Kernel::FT wachspress_weight(const CGAL::Point_3<Kernel>& p0,
const CGAL::Point_3<Kernel>& p1,
const CGAL::Point_3<Kernel>& p2,
const CGAL::Point_3<Kernel>& q)
{
const Kernel traits;
return wachspress_weight(p0, p1, p2, q, traits);
}
/// \endcond
/*!
\ingroup PkgWeightsRefBarycentricWachspressWeights
\brief 2D Wachspress weights for polygons.
This class implements 2D Wachspress weights ( \cite cgal:bc:fhk-gcbcocp-06,
\cite cgal:bc:mlbd-gbcip-02, \cite cgal:bc:w-rfeb-75 ) which can be computed
at any point inside a strictly convex polygon.
Wachspress weights are well-defined and non-negative inside a strictly convex polygon.
The weights are computed analytically using the formulation from the `wachspress_weight()`.
\tparam VertexRange a model of `ConstRange` whose iterator type is `RandomAccessIterator`
\tparam GeomTraits a model of `AnalyticWeightTraits_2`
\tparam PointMap a model of `ReadablePropertyMap` whose key type is `VertexRange::value_type` and
value type is `Point_2`. The default is `CGAL::Identity_property_map`.
\cgalModels `BarycentricWeights_2`
*/
template<typename VertexRange,
typename GeomTraits,
typename PointMap = CGAL::Identity_property_map<typename GeomTraits::Point_2> >
class Wachspress_weights_2
{
public:
/// \name Types
/// @{
/// \cond SKIP_IN_MANUAL
template<typename GeomTraits>
typename GeomTraits::FT wachspress_weight(
const typename GeomTraits::Point_2& t,
const typename GeomTraits::Point_2& r,
const typename GeomTraits::Point_2& p,
const typename GeomTraits::Point_2& q,
const GeomTraits& traits) {
using FT = typename GeomTraits::FT;
const FT A1 = internal::area_2(traits, r, q, t);
const FT A2 = internal::area_2(traits, p, q, r);
const FT C = internal::area_2(traits, t, r, p);
return wachspress_ns::weight(A1, A2, C);
}
using Vertex_range = VertexRange;
using Geom_traits = GeomTraits;
using Point_map = PointMap;
template<typename GeomTraits>
typename GeomTraits::FT wachspress_weight(
const CGAL::Point_2<GeomTraits>& t,
const CGAL::Point_2<GeomTraits>& r,
const CGAL::Point_2<GeomTraits>& p,
const CGAL::Point_2<GeomTraits>& q) {
const GeomTraits traits;
return wachspress_weight(t, r, p, q, traits);
}
namespace internal {
template<typename GeomTraits>
typename GeomTraits::FT wachspress_weight(
const typename GeomTraits::Point_3& t,
const typename GeomTraits::Point_3& r,
const typename GeomTraits::Point_3& p,
const typename GeomTraits::Point_3& q,
const GeomTraits& traits) {
using Point_2 = typename GeomTraits::Point_2;
Point_2 tf, rf, pf, qf;
internal::flatten(
traits,
t, r, p, q,
tf, rf, pf, qf);
return CGAL::Weights::
wachspress_weight(tf, rf, pf, qf, traits);
}
template<typename GeomTraits>
typename GeomTraits::FT wachspress_weight(
const CGAL::Point_3<GeomTraits>& t,
const CGAL::Point_3<GeomTraits>& r,
const CGAL::Point_3<GeomTraits>& p,
const CGAL::Point_3<GeomTraits>& q) {
const GeomTraits traits;
return wachspress_weight(t, r, p, q, traits);
}
} // namespace internal
using Area_2 = typename GeomTraits::Compute_area_2;
/// \endcond
/// Number type.
typedef typename GeomTraits::FT FT;
/// Point type.
typedef typename GeomTraits::Point_2 Point_2;
/// @}
/// \name Initialization
/// @{
/*!
\ingroup PkgWeightsRefBarycentricWachspressWeights
\brief initializes all internal data structures.
\brief 2D Wachspress weights for polygons.
This class implements the behavior of Wachspress weights
for 2D query points inside strictly convex polygons.
This class implements 2D Wachspress weights ( \cite cgal:bc:fhk-gcbcocp-06,
\cite cgal:bc:mlbd-gbcip-02, \cite cgal:bc:w-rfeb-75 ) which can be computed
at any point inside a strictly convex polygon.
\param polygon an instance of `VertexRange` with the vertices of a strictly convex polygon
\param traits a traits class with geometric objects, predicates, and constructions;
the default initialization is provided
\param point_map an instance of `PointMap` that maps a vertex from `polygon` to `Point_2`;
the default initialization is provided
Wachspress weights are well-defined and non-negative inside a strictly convex polygon.
The weights are computed analytically using the formulation from the `wachspress_weight()`.
\tparam VertexRange
a model of `ConstRange` whose iterator type is `RandomAccessIterator`
\tparam GeomTraits
a model of `AnalyticWeightTraits_2`
\tparam PointMap
a model of `ReadablePropertyMap` whose key type is `VertexRange::value_type` and
value type is `Point_2`. The default is `CGAL::Identity_property_map`.
\cgalModels `BarycentricWeights_2`
\pre `polygon.size() >= 3`
\pre `polygon` is simple
\pre `polygon` is strictly convex
*/
template<
typename VertexRange,
typename GeomTraits,
typename PointMap = CGAL::Identity_property_map<typename GeomTraits::Point_2> >
class Wachspress_weights_2 {
Wachspress_weights_2(const VertexRange& polygon,
const GeomTraits traits = GeomTraits(),
const PointMap point_map = PointMap())
: m_polygon(polygon),
m_traits(traits),
m_point_map(point_map),
m_area_2(m_traits.compute_area_2_object())
{
CGAL_precondition(polygon.size() >= 3);
CGAL_precondition(internal::is_simple_2(polygon, traits, point_map));
CGAL_precondition(internal::polygon_type_2(polygon, traits, point_map) ==
internal::Polygon_type::STRICTLY_CONVEX);
resize();
}
public:
/// @}
/// \name Types
/// @{
/// \cond SKIP_IN_MANUAL
using Vertex_range = VertexRange;
using Geom_traits = GeomTraits;
using Point_map = PointMap;
using Area_2 = typename GeomTraits::Compute_area_2;
/// \endcond
/// Number type.
typedef typename GeomTraits::FT FT;
/// Point type.
typedef typename GeomTraits::Point_2 Point_2;
/// @}
/// \name Initialization
/// @{
/*!
\brief initializes all internal data structures.
This class implements the behavior of Wachspress weights
for 2D query points inside strictly convex polygons.
\param polygon
an instance of `VertexRange` with the vertices of a strictly convex polygon
\param traits
a traits class with geometric objects, predicates, and constructions;
the default initialization is provided
\param point_map
an instance of `PointMap` that maps a vertex from `polygon` to `Point_2`;
the default initialization is provided
\pre polygon.size() >= 3
\pre polygon is simple
\pre polygon is strictly convex
*/
Wachspress_weights_2(
const VertexRange& polygon,
const GeomTraits traits = GeomTraits(),
const PointMap point_map = PointMap()) :
m_polygon(polygon),
m_traits(traits),
m_point_map(point_map),
m_area_2(m_traits.compute_area_2_object()) {
CGAL_precondition(
polygon.size() >= 3);
CGAL_precondition(
internal::is_simple_2(polygon, traits, point_map));
CGAL_precondition(
internal::polygon_type_2(polygon, traits, point_map) ==
internal::Polygon_type::STRICTLY_CONVEX);
resize();
}
/// @}
/// \name Access
/// @{
/*!
\brief computes 2D Wachspress weights.
This function fills a destination range with 2D Wachspress weights computed
at the `query` point with respect to the vertices of the input polygon.
The number of computed weights is equal to the number of polygon vertices.
\tparam OutIterator
a model of `OutputIterator` whose value type is `FT`
\param query
a query point
\param w_begin
the beginning of the destination range with the computed weights
\return an output iterator to the element in the destination range,
one past the last weight stored
*/
template<typename OutIterator>
OutIterator operator()(const Point_2& query, OutIterator w_begin) {
const bool normalize = false;
return operator()(query, w_begin, normalize);
}
/// @}
/// \cond SKIP_IN_MANUAL
template<typename OutIterator>
OutIterator operator()(const Point_2& query, OutIterator w_begin, const bool normalize) {
return optimal_weights(query, w_begin, normalize);
}
/// \endcond
private:
// Fields.
const VertexRange& m_polygon;
const GeomTraits m_traits;
const PointMap m_point_map;
const Area_2 m_area_2;
std::vector<FT> A;
std::vector<FT> C;
std::vector<FT> w;
// Functions.
void resize() {
A.resize(m_polygon.size());
C.resize(m_polygon.size());
w.resize(m_polygon.size());
}
template<typename OutputIterator>
OutputIterator optimal_weights(
const Point_2& query, OutputIterator weights, const bool normalize) {
// Get the number of vertices in the polygon.
const std::size_t n = m_polygon.size();
// Compute areas A and C following the area notation from [1].
// Split the loop to make this computation faster.
const auto& p1 = get(m_point_map, *(m_polygon.begin() + 0));
const auto& p2 = get(m_point_map, *(m_polygon.begin() + 1));
const auto& pn = get(m_point_map, *(m_polygon.begin() + (n - 1)));
A[0] = m_area_2(p1, p2, query);
C[0] = m_area_2(pn, p1, p2);
for (std::size_t i = 1; i < n - 1; ++i) {
const auto& pi0 = get(m_point_map, *(m_polygon.begin() + (i - 1)));
const auto& pi1 = get(m_point_map, *(m_polygon.begin() + (i + 0)));
const auto& pi2 = get(m_point_map, *(m_polygon.begin() + (i + 1)));
A[i] = m_area_2(pi1, pi2, query);
C[i] = m_area_2(pi0, pi1, pi2);
}
const auto& pm = get(m_point_map, *(m_polygon.begin() + (n - 2)));
A[n - 1] = m_area_2(pn, p1, query);
C[n - 1] = m_area_2(pm, pn, p1);
// Compute unnormalized weights following the formula (28) from [1].
CGAL_assertion(A[n - 1] != FT(0) && A[0] != FT(0));
w[0] = C[0] / (A[n - 1] * A[0]);
for (std::size_t i = 1; i < n - 1; ++i) {
CGAL_assertion(A[i - 1] != FT(0) && A[i] != FT(0));
w[i] = C[i] / (A[i - 1] * A[i]);
}
CGAL_assertion(A[n - 2] != FT(0) && A[n - 1] != FT(0));
w[n - 1] = C[n - 1] / (A[n - 2] * A[n - 1]);
// Normalize if necessary.
if (normalize) {
internal::normalize(w);
}
// Return weights.
for (std::size_t i = 0; i < n; ++i) {
*(weights++) = w[i];
}
return weights;
}
};
/// \name Access
/// @{
/*!
\ingroup PkgWeightsRefBarycentricWachspressWeights
\brief computes 2D Wachspress weights.
\brief computes 2D Wachspress weights for polygons.
This function fills a destination range with 2D Wachspress weights computed
at the `query` point with respect to the vertices of the input polygon.
This function computes 2D Wachspress weights at a given `query` point
with respect to the vertices of a strictly convex `polygon`, that is one
weight per vertex. The weights are stored in a destination range
beginning at `w_begin`.
The number of computed weights is equal to the number of polygon vertices.
Internally, the class `Wachspress_weights_2` is used. If one wants to process
multiple query points, it is better to use that class. When using the free function,
internal memory is allocated for each query point, while when using the class,
it is allocated only once which is much more efficient. However, for a few query
points, it is easier to use this function. It can also be used when the processing
time is not a concern.
\tparam OutIterator a model of `OutputIterator` whose value type is `FT`
\tparam PointRange
a model of `ConstRange` whose iterator type is `RandomAccessIterator`
and value type is `GeomTraits::Point_2`
\tparam OutIterator
a model of `OutputIterator` whose value type is `GeomTraits::FT`
\tparam GeomTraits
a model of `AnalyticWeightTraits_2`
\param polygon
an instance of `PointRange` with 2D points which form a strictly convex polygon
\param query
a query point
\param w_begin
the beginning of the destination range with the computed weights
\param traits
a traits class with geometric objects, predicates, and constructions;
this parameter can be omitted if the traits class can be deduced from the point type
\param query a query point
\param w_begin the beginning of the destination range with the computed weights
\return an output iterator to the element in the destination range,
one past the last weight stored
\pre polygon.size() >= 3
\pre polygon is simple
\pre polygon is strictly convex
*/
template<
typename PointRange,
typename OutIterator,
typename GeomTraits>
OutIterator wachspress_weights_2(
const PointRange& polygon, const typename GeomTraits::Point_2& query,
OutIterator w_begin, const GeomTraits& traits) {
Wachspress_weights_2<PointRange, GeomTraits>
wachspress(polygon, traits);
return wachspress(query, w_begin);
template<typename OutIterator>
OutIterator operator()(const Point_2& query,
OutIterator w_begin)
{
const bool normalize = false;
return operator()(query, w_begin, normalize);
}
/// @}
/// \cond SKIP_IN_MANUAL
template<
typename PointRange,
typename OutIterator>
OutIterator wachspress_weights_2(
const PointRange& polygon,
const typename PointRange::value_type& query,
OutIterator w_begin) {
using Point_2 = typename PointRange::value_type;
using GeomTraits = typename Kernel_traits<Point_2>::Kernel;
const GeomTraits traits;
return wachspress_weights_2(
polygon, query, w_begin, traits);
template<typename OutIterator>
OutIterator operator()(const Point_2& query,
OutIterator w_begin,
const bool normalize)
{
return optimal_weights(query, w_begin, normalize);
}
/// \endcond
private:
const VertexRange& m_polygon;
const GeomTraits m_traits;
const PointMap m_point_map;
const Area_2 m_area_2;
std::vector<FT> A;
std::vector<FT> C;
std::vector<FT> w;
void resize()
{
A.resize(m_polygon.size());
C.resize(m_polygon.size());
w.resize(m_polygon.size());
}
template<typename OutputIterator>
OutputIterator optimal_weights(const Point_2& query,
OutputIterator weights,
const bool normalize)
{
// Get the number of vertices in the polygon.
const std::size_t n = m_polygon.size();
// Compute areas A and C following the area notation from [1].
// Split the loop to make this computation faster.
const auto& p1 = get(m_point_map, *(m_polygon.begin() + 0));
const auto& p2 = get(m_point_map, *(m_polygon.begin() + 1));
const auto& pn = get(m_point_map, *(m_polygon.begin() + (n - 1)));
A[0] = m_area_2(p1, p2, query);
C[0] = m_area_2(pn, p1, p2);
for (std::size_t i = 1; i < n - 1; ++i)
{
const auto& pi0 = get(m_point_map, *(m_polygon.begin() + (i - 1)));
const auto& pi1 = get(m_point_map, *(m_polygon.begin() + (i + 0)));
const auto& pi2 = get(m_point_map, *(m_polygon.begin() + (i + 1)));
A[i] = m_area_2(pi1, pi2, query);
C[i] = m_area_2(pi0, pi1, pi2);
}
const auto& pm = get(m_point_map, *(m_polygon.begin() + (n - 2)));
A[n - 1] = m_area_2(pn, p1, query);
C[n - 1] = m_area_2(pm, pn, p1);
// Compute unnormalized weights following the formula (28) from [1].
CGAL_assertion(A[n - 1] != FT(0) && A[0] != FT(0));
w[0] = C[0] / (A[n - 1] * A[0]);
for (std::size_t i = 1; i < n - 1; ++i)
{
CGAL_assertion(A[i - 1] != FT(0) && A[i] != FT(0));
w[i] = C[i] / (A[i - 1] * A[i]);
}
CGAL_assertion(A[n - 2] != FT(0) && A[n - 1] != FT(0));
w[n - 1] = C[n - 1] / (A[n - 2] * A[n - 1]);
// Normalize if necessary.
if (normalize)
internal::normalize(w);
// Return weights.
for (std::size_t i = 0; i < n; ++i)
*(weights++) = w[i];
return weights;
}
};
/*!
\ingroup PkgWeightsRefBarycentricWachspressWeights
\brief computes 2D Wachspress weights for polygons.
This function computes 2D Wachspress weights at a given `query` point
with respect to the vertices of a strictly convex `polygon`, that is one
weight per vertex. The weights are stored in a destination range
beginning at `w_begin`.
Internally, the class `Wachspress_weights_2` is used. If one wants to process
multiple query points, it is better to use that class. When using the free function,
internal memory is allocated for each query point, while when using the class,
it is allocated only once which is much more efficient. However, for a few query
points, it is easier to use this function. It can also be used when the processing
time is not a concern.
\tparam PointRange a model of `ConstRange` whose iterator type is `RandomAccessIterator`
and value type is `GeomTraits::Point_2`
\tparam OutIterator a model of `OutputIterator` whose value type is `GeomTraits::FT`
\tparam GeomTraits a model of `AnalyticWeightTraits_2`
\param polygon an instance of `PointRange` with 2D points which form a strictly convex polygon
\param query a query point
\param w_begin the beginning of the destination range with the computed weights
\param traits a traits class with geometric objects, predicates, and constructions;
this parameter can be omitted if the traits class can be deduced from the point type
\return an output iterator to the element in the destination range, one past the last weight stored
\pre `polygon.size() >= 3`
\pre `polygon` is simple
\pre `polygon` is strictly convex
*/
template<typename PointRange,
typename OutIterator,
typename GeomTraits>
OutIterator wachspress_weights_2(const PointRange& polygon,
const typename GeomTraits::Point_2& query,
OutIterator w_begin,
const GeomTraits& traits)
{
Wachspress_weights_2<PointRange, GeomTraits> wachspress(polygon, traits);
return wachspress(query, w_begin);
}
/// \cond SKIP_IN_MANUAL
template<typename PointRange,
typename OutIterator>
OutIterator wachspress_weights_2(const PointRange& polygon,
const typename PointRange::value_type& query,
OutIterator w_begin)
{
using Point_2 = typename PointRange::value_type;
using GeomTraits = typename Kernel_traits<Point_2>::Kernel;
const GeomTraits traits;
return wachspress_weights_2(polygon, query, w_begin, traits);
}
/// \endcond
} // namespace Weights
} // namespace CGAL

View File

@ -1,113 +1,177 @@
#ifndef CGAL_WEIGHTS_TESTS_UTILS_H
#define CGAL_WEIGHTS_TESTS_UTILS_H
// STL includes.
#include <CGAL/Weights/utils.h>
#include <CGAL/assertions.h>
#include <CGAL/Projection_traits_xy_3.h>
#include <array>
#include <vector>
#include <cassert>
#include <algorithm>
#include <string>
// CGAL includes.
#include <CGAL/assertions.h>
#include <CGAL/Projection_traits_xy_3.h>
#include <CGAL/Weights/utils.h>
namespace CGAL {
namespace Weights {
namespace internal {
template<typename Kernel>
typename Kernel::FT squared_distance(const CGAL::Point_2<Kernel>& p,
const CGAL::Point_2<Kernel>& q)
{
const Kernel traits;
auto squared_distance_2 = traits.compute_squared_distance_2_object();
return squared_distance_2(p, q);
}
template<typename Kernel>
typename Kernel::FT squared_distance(const CGAL::Point_3<Kernel>& p,
const CGAL::Point_3<Kernel>& q)
{
const Kernel traits;
auto squared_distance_3 = traits.compute_squared_distance_3_object();
return squared_distance_3(p, q);
}
template<typename Kernel>
typename Kernel::FT distance(const CGAL::Point_2<Kernel>& p,
const CGAL::Point_2<Kernel>& q)
{
const Kernel traits;
return CGAL::Weights::internal::distance_2(p, q, traits);
}
template<typename Kernel>
typename Kernel::FT distance(const CGAL::Point_3<Kernel>& p,
const CGAL::Point_3<Kernel>& q)
{
const Kernel traits;
return CGAL::Weights::internal::distance_3(p, q, traits);
}
template<typename Kernel>
typename Kernel::FT area(const CGAL::Point_2<Kernel>& p,
const CGAL::Point_2<Kernel>& q,
const CGAL::Point_2<Kernel>& r)
{
const Kernel traits;
return CGAL::Weights::internal::positive_area_2(p, q, r, traits);
}
template<typename Kernel>
typename Kernel::FT area(const CGAL::Point_3<Kernel>& p,
const CGAL::Point_3<Kernel>& q,
const CGAL::Point_3<Kernel>& r)
{
const Kernel traits;
return CGAL::Weights::internal::positive_area_3(p, q, r, traits);
}
template<typename Kernel>
typename Kernel::FT scalar_product(const CGAL::Point_2<Kernel>& p,
const CGAL::Point_2<Kernel>& q,
const CGAL::Point_2<Kernel>& r)
{
const Kernel traits;
auto scalar_product_2 = traits.compute_scalar_product_2_object();
auto vector_2 = traits.construct_vector_2_object();
const auto v1 = vector_2(q, r);
const auto v2 = vector_2(q, p);
return scalar_product_2(v1, v2);
}
template<typename Kernel>
typename Kernel::FT scalar_product(const CGAL::Point_3<Kernel>& p,
const CGAL::Point_3<Kernel>& q,
const CGAL::Point_3<Kernel>& r)
{
const Kernel traits;
auto scalar_product_3 = traits.compute_scalar_product_3_object();
auto vector_3 = traits.construct_vector_3_object();
const auto v1 = vector_3(q, r);
const auto v2 = vector_3(q, p);
return scalar_product_3(v1, v2);
}
} // namespace internal
} // namespace Weights
} // namespace CGAL
namespace tests {
template<typename FT>
FT get_tolerance() {
return FT(1) / FT(10000000000);
}
FT get_tolerance() { return FT(1e-10); }
template<typename Kernel>
std::vector< std::array<typename Kernel::Point_2, 3> >
get_all_triangles() {
get_all_triangles()
{
using Point_2 = typename Kernel::Point_2;
const std::array<Point_2, 3> triangle0 = {
Point_2(-1, 0), Point_2(0, -1), Point_2(1, 0)
};
const std::array<Point_2, 3> triangle1 = {
Point_2(-2, 0), Point_2(0, -1), Point_2(2, 0)
};
const std::array<Point_2, 3> triangle2 = {
Point_2(-2, 0), Point_2(-2, -2), Point_2(2, 0)
};
const std::array<Point_2, 3> triangle3 = {
Point_2(-2, 0), Point_2(2, -2), Point_2(2, 0)
};
const std::array<Point_2, 3> triangle0 = { Point_2(-1, 0), Point_2(0, -1), Point_2(1, 0) };
const std::array<Point_2, 3> triangle1 = { Point_2(-2, 0), Point_2(0, -1), Point_2(2, 0) };
const std::array<Point_2, 3> triangle2 = { Point_2(-2, 0), Point_2(-2, -2), Point_2(2, 0) };
const std::array<Point_2, 3> triangle3 = { Point_2(-2, 0), Point_2(2, -2), Point_2(2, 0) };
return { triangle0, triangle1, triangle2, triangle3 };
}
template<typename Kernel>
std::vector< std::array<typename Kernel::Point_2, 3> >
get_symmetric_triangles() {
get_symmetric_triangles()
{
using Point_2 = typename Kernel::Point_2;
const std::array<Point_2, 3> triangle0 = {
Point_2(-1, 0), Point_2(0, -1), Point_2(1, 0)
};
const std::array<Point_2, 3> triangle1 = {
Point_2(-2, 0), Point_2(0, -1), Point_2(2, 0)
};
const std::array<Point_2, 3> triangle2 = {
Point_2(-3, 0), Point_2(0, -1), Point_2(3, 0)
};
const std::array<Point_2, 3> triangle0 = { Point_2(-1, 0), Point_2(0, -1), Point_2(1, 0) };
const std::array<Point_2, 3> triangle1 = { Point_2(-2, 0), Point_2(0, -1), Point_2(2, 0) };
const std::array<Point_2, 3> triangle2 = { Point_2(-3, 0), Point_2(0, -1), Point_2(3, 0) };
return { triangle0, triangle1, triangle2 };
}
template<typename Kernel>
std::vector< std::array<typename Kernel::Point_2, 3> >
get_uniform_triangles() {
get_uniform_triangles()
{
using Point_2 = typename Kernel::Point_2;
const std::array<Point_2, 3> triangle0 = {
Point_2(-1, 0), Point_2(0, -1), Point_2(1, 0)
};
const std::array<Point_2, 3> triangle1 = {
Point_2(-2, 0), Point_2(0, -2), Point_2(2, 0)
};
const std::array<Point_2, 3> triangle2 = {
Point_2(1, 0), Point_2(-1, 0), Point_2(-1, -2)
};
const std::array<Point_2, 3> triangle3 = {
Point_2(1, -2), Point_2(1, 0), Point_2(-1, 0)
};
const std::array<Point_2, 3> triangle0 = { Point_2(-1, 0), Point_2(0, -1), Point_2(1, 0) };
const std::array<Point_2, 3> triangle1 = { Point_2(-2, 0), Point_2(0, -2), Point_2(2, 0) };
const std::array<Point_2, 3> triangle2 = { Point_2(1, 0), Point_2(-1, 0), Point_2(-1, -2) };
const std::array<Point_2, 3> triangle3 = { Point_2(1, -2), Point_2(1, 0), Point_2(-1, 0) };
return { triangle0, triangle1, triangle2, triangle3 };
}
template<typename Kernel>
std::vector< std::vector<typename Kernel::Point_2> >
get_all_polygons() {
get_all_polygons()
{
using Point_2 = typename Kernel::Point_2;
const std::vector<Point_2> polygon0 = {
Point_2(-2, -2), Point_2(2, -2), Point_2(0, 2)
};
const std::vector<Point_2> polygon1 = {
Point_2(-1, -1), Point_2(1, -1), Point_2(1, 1), Point_2(-1, 1)
};
const std::vector<Point_2> polygon2 = {
Point_2(-2, 0), Point_2(0, -2), Point_2(2, 0), Point_2(0, 2)
};
const std::vector<Point_2> polygon3 = {
Point_2(-2, -2), Point_2(2, -2), Point_2(2, 0), Point_2(0, 2), Point_2(-2, 0)
};
const std::vector<Point_2> polygon0 = { Point_2(-2, -2), Point_2(2, -2), Point_2(0, 2) };
const std::vector<Point_2> polygon1 = { Point_2(-1, -1), Point_2(1, -1), Point_2(1, 1), Point_2(-1, 1) };
const std::vector<Point_2> polygon2 = { Point_2(-2, 0), Point_2(0, -2), Point_2(2, 0), Point_2(0, 2) };
const std::vector<Point_2> polygon3 = { Point_2(-2, -2), Point_2(2, -2), Point_2(2, 0),
Point_2(0, 2), Point_2(-2, 0) };
return { polygon0, polygon1, polygon2, polygon3 };
}
template<
typename Kernel,
typename Weight_wrapper>
bool test_query(
const Weight_wrapper& wrapper,
const typename Kernel::Point_2& query,
const std::array<typename Kernel::Point_2, 3>& neighbors) {
template<typename Kernel,
typename Weight_wrapper>
void test_query(const Weight_wrapper& wrapper,
const typename Kernel::Point_2& query,
const std::array<typename Kernel::Point_2, 3>& neighbors)
{
using FT = typename Kernel::FT;
using Point_2 = typename Kernel::Point_2;
using Point_3 = typename Kernel::Point_3;
const FT tol = get_tolerance<FT>();
// 2D configuration.
@ -122,36 +186,28 @@ bool test_query(
const Point_3 p3(p2.x(), p2.y(), 1);
const Point_3 q3(q2.x(), q2.y(), 1);
const auto a2 = wrapper.weight_a(t2, r2, p2, q2);
const auto b2 = wrapper.weight_b(t2, r2, p2, q2);
const FT a2 = wrapper.weight_a(t2, r2, p2, q2);
const FT b2 = wrapper.weight_b(t2, r2, p2, q2);
assert(a2 >= FT(0) && b2 >= FT(0));
if (a2 < FT(0) || b2 < FT(0)) return false;
assert(CGAL::abs(a2 - b2) < tol);
if (CGAL::abs(a2 - b2) >= tol) return false;
if (wrapper.supports_3d()) {
const auto a3 = wrapper.weight_a(t3, r3, p3, q3);
const auto b3 = wrapper.weight_b(t3, r3, p3, q3);
if (wrapper.supports_3d())
{
const FT a3 = wrapper.weight_a(t3, r3, p3, q3);
const FT b3 = wrapper.weight_b(t3, r3, p3, q3);
assert(a3 >= FT(0) && b3 >= FT(0));
if (a3 < FT(0) || b3 < FT(0)) return false;
assert(CGAL::abs(a3 - b3) < tol);
if (CGAL::abs(a3 - b3) >= tol) return false;
assert(CGAL::abs(a2 - a3) < tol);
assert(CGAL::abs(b2 - b3) < tol);
if (CGAL::abs(a2 - a3) >= tol) return false;
if (CGAL::abs(b2 - b3) >= tol) return false;
}
return true;
}
template<
typename Kernel,
typename Weight_wrapper>
bool test_symmetry_x(
const Weight_wrapper& wrapper,
const std::array<typename Kernel::Point_2, 3>& neighbors,
const typename Kernel::FT& x) {
template<typename Kernel,
typename Weight_wrapper>
void test_symmetry_x(const Weight_wrapper& wrapper,
const std::array<typename Kernel::Point_2, 3>& neighbors,
const typename Kernel::FT& x)
{
using FT = typename Kernel::FT;
using Point_2 = typename Kernel::Point_2;
using Point_3 = typename Kernel::Point_3;
@ -167,41 +223,34 @@ bool test_symmetry_x(
const Point_3 r3(r2.x(), r2.y(), 1);
const Point_3 p3(p2.x(), p2.y(), 1);
const auto a2 = wrapper.weight_a(t2, r2, p2, Point_2(-x, 0));
const auto b2 = wrapper.weight_a(t2, r2, p2, Point_2(+x, 0));
const FT a2 = wrapper.weight_a(t2, r2, p2, Point_2(-x, 0));
const FT b2 = wrapper.weight_a(t2, r2, p2, Point_2(+x, 0));
assert(a2 >= FT(0) && b2 >= FT(0));
if (a2 < FT(0) || b2 < FT(0)) return false;
assert(CGAL::abs(a2 - b2) < tol);
if (CGAL::abs(a2 - b2) >= tol) return false;
if (wrapper.supports_3d()) {
const auto a3 = wrapper.weight_a(t3, r3, p3, Point_3(-x, 0, 1));
const auto b3 = wrapper.weight_a(t3, r3, p3, Point_3(+x, 0, 1));
if (wrapper.supports_3d())
{
const FT a3 = wrapper.weight_a(t3, r3, p3, Point_3(-x, 0, 1));
const FT b3 = wrapper.weight_a(t3, r3, p3, Point_3(+x, 0, 1));
assert(a3 >= FT(0) && b3 >= FT(0));
if (a3 < FT(0) || b3 < FT(0)) return false;
assert(CGAL::abs(a3 - b3) < tol);
if (CGAL::abs(a3 - b3) >= tol) return false;
assert(CGAL::abs(a2 - a3) < tol);
assert(CGAL::abs(b2 - b3) < tol);
if (CGAL::abs(a2 - a3) >= tol) return false;
if (CGAL::abs(b2 - b3) >= tol) return false;
}
return true;
}
template<
typename Kernel,
typename Weight_wrapper_1,
typename Weight_wrapper_2>
bool test_compare(
const Weight_wrapper_1& wrapper1,
const Weight_wrapper_2& wrapper2,
const typename Kernel::Point_2& query,
const std::array<typename Kernel::Point_2, 3>& neighbors) {
template<typename Kernel,
typename Weight_wrapper_1,
typename Weight_wrapper_2>
void test_compare(const Weight_wrapper_1& wrapper1,
const Weight_wrapper_2& wrapper2,
const typename Kernel::Point_2& query,
const std::array<typename Kernel::Point_2, 3>& neighbors)
{
using FT = typename Kernel::FT;
using Point_2 = typename Kernel::Point_2;
using Point_3 = typename Kernel::Point_3;
const FT tol = get_tolerance<FT>();
// 2D configuration.
@ -216,34 +265,29 @@ bool test_compare(
const Point_3 p3(p2.x(), p2.y(), 1);
const Point_3 q3(q2.x(), q2.y(), 1);
const auto a2 = wrapper1.weight_a(t2, r2, p2, q2);
const auto b2 = wrapper2.weight_a(t2, r2, p2, q2);
const FT a2 = wrapper1.weight_a(t2, r2, p2, q2);
const FT b2 = wrapper2.weight_a(t2, r2, p2, q2);
assert(a2 >= FT(0) && b2 >= FT(0));
if (a2 < FT(0) || b2 < FT(0)) return false;
assert(CGAL::abs(a2 - b2) < tol);
if (CGAL::abs(a2 - b2) >= tol) return false;
if (wrapper1.supports_3d() && wrapper2.supports_3d()) {
const auto a3 = wrapper1.weight_a(t3, r3, p3, q3);
const auto b3 = wrapper2.weight_a(t3, r3, p3, q3);
if (wrapper1.supports_3d() && wrapper2.supports_3d())
{
const FT a3 = wrapper1.weight_a(t3, r3, p3, q3);
const FT b3 = wrapper2.weight_a(t3, r3, p3, q3);
assert(a3 >= FT(0) && b3 >= FT(0));
if (a3 < FT(0) || b3 < FT(0)) return false;
assert(CGAL::abs(a3 - b3) < tol);
if (CGAL::abs(a3 - b3) >= tol) return false;
}
return true;
}
template<
typename Kernel,
typename Weight_wrapper>
bool test_neighbors(
const Weight_wrapper& wrapper,
const std::array<typename Kernel::Point_2, 3>& neighbors) {
template<typename Kernel,
typename Weight_wrapper>
void test_neighbors(const Weight_wrapper& wrapper,
const std::array<typename Kernel::Point_2, 3>& neighbors)
{
using FT = typename Kernel::FT;
using Point_2 = typename Kernel::Point_2;
using Point_3 = typename Kernel::Point_3;
const FT tol = get_tolerance<FT>();
// 2D configuration.
@ -256,22 +300,17 @@ bool test_neighbors(
const Point_3 q3(q2.x(), q2.y(), 1);
const Point_3 r3(r2.x(), r2.y(), 1);
const auto a2 = wrapper.weight(p2, q2, r2);
const auto a3 = wrapper.weight(p3, q3, r3);
const FT a2 = wrapper.weight(p2, q2, r2);
const FT a3 = wrapper.weight(p3, q3, r3);
assert(a2 >= FT(0) && a3 >= FT(0));
if (a2 < FT(0) || a3 < FT(0)) return false;
assert(CGAL::abs(a2 - a3) < tol);
if (CGAL::abs(a2 - a3) >= tol) return false;
return true;
}
template<
typename Kernel,
typename Weight_wrapper>
bool test_area(
const Weight_wrapper& wrapper,
const std::array<typename Kernel::Point_2, 3>& neighbors) {
template<typename Kernel,
typename Weight_wrapper>
void test_area(const Weight_wrapper& wrapper,
const std::array<typename Kernel::Point_2, 3>& neighbors)
{
using FT = typename Kernel::FT;
using Point_2 = typename Kernel::Point_2;
using Point_3 = typename Kernel::Point_3;
@ -286,129 +325,105 @@ bool test_area(
const Point_3 q3(q2.x(), q2.y(), 1);
const Point_3 r3(r2.x(), r2.y(), 1);
const auto a2 = wrapper.weight(p2, q2, r2);
const auto a3 = wrapper.weight(p3, q3, r3);
assert(a2 <= CGAL::Weights::area(p2, q2, r2));
assert(a3 <= CGAL::Weights::area(p3, q3, r3));
if (a2 > CGAL::Weights::area(p2, q2, r2)) return false;
if (a3 > CGAL::Weights::area(p3, q3, r3)) return false;
const FT a2 = wrapper.weight(p2, q2, r2);
const FT a3 = wrapper.weight(p3, q3, r3);
assert(a2 <= CGAL::Weights::internal::area(p2, q2, r2));
assert(a3 <= CGAL::Weights::internal::area(p3, q3, r3));
assert(a2 >= FT(0));
assert(a3 >= FT(0));
if (a2 < FT(0)) return false;
if (a3 < FT(0)) return false;
return true;
}
template<typename FT, typename Point>
bool test_coordinates(
const Point& query,
const std::vector<Point>& polygon,
const std::vector<FT>& weights) {
void test_coordinates(const Point& query,
const std::vector<Point>& polygon,
const std::vector<FT>& weights)
{
assert(weights.size() > 0);
if (weights.size() == 0) return false;
// Compute the sum of weights.
const FT tol = get_tolerance<FT>();
FT sum = FT(0);
for (const FT& weight : weights) {
for (const FT& weight : weights)
sum += weight;
}
assert(sum >= tol);
if (sum < tol) return false;
// Compute coordinates.
std::vector<FT> coordinates;
coordinates.reserve(weights.size());
for (const FT& weight : weights) {
for (const FT& weight : weights)
coordinates.push_back(weight / sum);
}
assert(coordinates.size() == weights.size());
if (coordinates.size() != weights.size()) return false;
// Test partition of unity.
sum = FT(0);
for (const FT& coordinate : coordinates) {
for (const FT& coordinate : coordinates)
sum += coordinate;
}
assert(CGAL::abs(FT(1) - sum) < tol);
if (CGAL::abs(FT(1) - sum) >= tol) return false;
// Test linear precision.
FT x = FT(0), y = FT(0);
for (std::size_t i = 0; i < polygon.size(); ++i) {
for (std::size_t i = 0; i < polygon.size(); ++i)
{
x += coordinates[i] * polygon[i].x();
y += coordinates[i] * polygon[i].y();
}
assert(CGAL::abs(query.x() - x) < tol);
assert(CGAL::abs(query.y() - y) < tol);
if (CGAL::abs(query.x() - x) >= tol) return false;
if (CGAL::abs(query.y() - y) >= tol) return false;
return true;
}
template<
typename Kernel,
typename Weight_wrapper>
bool test_on_polygon(
const Weight_wrapper& wrapper,
const typename Kernel::Point_2& query_2,
const std::vector<typename Kernel::Point_2>& polygon_2) {
template<typename Kernel,
typename Weight_wrapper>
void test_on_polygon(const Weight_wrapper& wrapper,
const typename Kernel::Point_2& query_2,
const std::vector<typename Kernel::Point_2>& polygon_2)
{
// Get weights.
using FT = typename Kernel::FT;
assert(polygon_2.size() >= 3);
if (polygon_2.size() < 3) return false;
// 2D version.
std::vector<FT> weights_2;
weights_2.reserve(polygon_2.size());
wrapper.compute_on_polygon(
polygon_2, query_2, Kernel(), std::back_inserter(weights_2));
wrapper.compute_on_polygon(polygon_2, query_2, Kernel(), std::back_inserter(weights_2));
assert(weights_2.size() == polygon_2.size());
if (weights_2.size() != polygon_2.size()) return false;
if (!test_coordinates(query_2, polygon_2, weights_2)) return false;
test_coordinates(query_2, polygon_2, weights_2);
// 3D version.
using Point_3 = typename Kernel::Point_3;
const Point_3 query_3(query_2.x(), query_2.y(), 1);
std::vector<Point_3> polygon_3;
polygon_3.reserve(polygon_2.size());
for (const auto& vertex_2 : polygon_2) {
polygon_3.push_back(Point_3(vertex_2.x(), vertex_2.y(), 1));
}
for (const auto& vertex_2 : polygon_2)
polygon_3.emplace_back(vertex_2.x(), vertex_2.y(), 1);
assert(polygon_3.size() == polygon_2.size());
if (polygon_3.size() != polygon_2.size()) return false;
const CGAL::Projection_traits_xy_3<Kernel> ptraits;
const CGAL::Projection_traits_xy_3<Kernel> ptraits;
std::vector<FT> weights_3;
weights_3.reserve(polygon_3.size());
wrapper.compute_on_polygon(
polygon_3, query_3, ptraits, std::back_inserter(weights_3));
wrapper.compute_on_polygon(polygon_3, query_3, ptraits, std::back_inserter(weights_3));
assert(weights_3.size() == polygon_3.size());
if (weights_3.size() != polygon_3.size()) return false;
if (!test_coordinates(query_3, polygon_3, weights_3)) return false;
return true;
test_coordinates(query_3, polygon_3, weights_3);
}
template<
typename Kernel,
typename Weight_wrapper>
bool test_barycentric_properties(
const Weight_wrapper& wrapper,
const typename Kernel::Point_2& query,
const std::vector<typename Kernel::Point_2>& polygon) {
template<typename Kernel,
typename Weight_wrapper>
void test_barycentric_properties(const Weight_wrapper& wrapper,
const typename Kernel::Point_2& query,
const std::vector<typename Kernel::Point_2>& polygon)
{
// Get weights.
using FT = typename Kernel::FT;
const std::size_t n = polygon.size();
assert(n >= 3);
if (n < 3) return false;
// Check properties.
std::vector<FT> weights;
weights.reserve(n);
for (std::size_t i = 0; i < n; ++i) {
for (std::size_t i = 0; i < n; ++i)
{
const std::size_t im = (i + n - 1) % n;
const std::size_t ip = (i + 1) % n;
const auto& t = polygon[im];
@ -419,23 +434,19 @@ bool test_barycentric_properties(
weights.push_back(weight);
}
assert(weights.size() == n);
if (weights.size() != n) return false;
if (!test_coordinates(query, polygon, weights)) return false;
return true;
test_coordinates(query, polygon, weights);
}
template<
typename Kernel,
typename Weight_wrapper_1,
typename Weight_wrapper_2>
bool test_analytic_weight(
const Weight_wrapper_1& weight,
const Weight_wrapper_2& alternative) {
template<typename Kernel,
typename Weight_wrapper_1,
typename Weight_wrapper_2>
void test_analytic_weight(const Weight_wrapper_1& weight,
const Weight_wrapper_2& alternative)
{
using FT = typename Kernel::FT;
using Point_2 = typename Kernel::Point_2;
// Data.
const FT q = FT(1) / FT(4);
const FT h = FT(1) / FT(2);
const FT t = FT(3) / FT(4);
@ -449,66 +460,59 @@ bool test_analytic_weight(
// Test query points.
auto configs = get_all_triangles<Kernel>();
for (const auto& config : configs) {
if (!test_query<Kernel>(weight, zero, config)) return false;
for (const auto& query : queries) {
if (!test_query<Kernel>(weight, query, config)) return false;
}
for (const auto& config : configs)
{
test_query<Kernel>(weight, zero, config);
for (const auto& query : queries)
test_query<Kernel>(weight, query, config);
}
// Test alternative formulations.
for (const auto& config : configs) {
if (!test_compare<Kernel>(weight, alternative, zero, config)) {
return false;
}
for (const auto& query : queries) {
if (!test_compare<Kernel>(weight, alternative, query, config)) {
return false;
}
}
for (const auto& config : configs)
{
test_compare<Kernel>(weight, alternative, zero, config);
for (const auto& query : queries)
test_compare<Kernel>(weight, alternative, query, config);
}
// Test symmetry along x axis.
configs = get_symmetric_triangles<Kernel>();
for (const auto& config : configs) {
if (!test_symmetry_x<Kernel>(weight, config, q)) return false;
if (!test_symmetry_x<Kernel>(weight, config, h)) return false;
if (!test_symmetry_x<Kernel>(weight, config, t)) return false;
for (const auto& config : configs)
{
test_symmetry_x<Kernel>(weight, config, q);
test_symmetry_x<Kernel>(weight, config, h);
test_symmetry_x<Kernel>(weight, config, t);
}
// Test barycentric properties.
if (weight.is_barycentric()) {
if (weight.is_barycentric())
{
const auto polygons = get_all_polygons<Kernel>();
for (const auto& polygon : polygons) {
if (!test_barycentric_properties<Kernel>(weight, zero, polygon)) {
return false;
}
for (const auto& query : queries) {
if (!test_barycentric_properties<Kernel>(weight, query, polygon)) {
return false;
}
}
for (const auto& polygon : polygons)
{
test_barycentric_properties<Kernel>(weight, zero, polygon);
for (const auto& query : queries)
test_barycentric_properties<Kernel>(weight, query, polygon);
}
}
return true;
return;
}
template<
typename Kernel,
typename Weight_wrapper_1,
typename Weight_wrapper_2>
bool test_barycentric_weight(
const Weight_wrapper_1& weight,
const Weight_wrapper_2& alternative) {
template<typename Kernel,
typename Weight_wrapper_1,
typename Weight_wrapper_2>
void test_barycentric_weight(const Weight_wrapper_1& weight,
const Weight_wrapper_2& alternative)
{
using FT = typename Kernel::FT;
using Point_2 = typename Kernel::Point_2;
// Data.
const FT q = FT(1) / FT(4);
const FT h = FT(1) / FT(2);
const Point_2 zero(0, 0);
const std::vector<Point_2> queries = {
const std::vector<Point_2> queries =
{
Point_2(-h, 0), Point_2(+h, 0), Point_2(-q, 0), Point_2(+q, 0),
Point_2( 0, -h), Point_2( 0, +h), Point_2( 0, -q), Point_2( 0, +q),
Point_2(-h, -h), Point_2(+h, +h), Point_2(-q, -q), Point_2(+q, +q),
@ -516,40 +520,29 @@ bool test_barycentric_weight(
};
// Test analytic formulations.
if (!test_analytic_weight<Kernel>(weight, alternative)) {
return false;
}
test_analytic_weight<Kernel>(weight, alternative);
// Test on polygons.
const auto polygons = get_all_polygons<Kernel>();
for (const auto& polygon : polygons) {
if (!test_on_polygon<Kernel>(weight, zero, polygon)) return false;
for (const auto& query : queries) {
if (!test_on_polygon<Kernel>(weight, query, polygon)) {
return false;
}
}
for (const auto& polygon : polygons)
{
test_on_polygon<Kernel>(weight, zero, polygon);
for (const auto& query : queries)
test_on_polygon<Kernel>(weight, query, polygon);
}
return true;
}
template<
typename Kernel,
typename Weight_wrapper>
bool test_region_weight(const Weight_wrapper& weight) {
// Test neighborhoods.
template<typename Kernel,
typename Weight_wrapper>
void test_region_weight(const Weight_wrapper& weight)
{
auto configs = get_all_triangles<Kernel>();
for (const auto& config : configs) {
if (!test_neighbors<Kernel>(weight, config)) return false;
}
for (const auto& config : configs)
test_neighbors<Kernel>(weight, config);
// Test areas.
configs = get_uniform_triangles<Kernel>();
for (const auto& config : configs) {
if (!test_area<Kernel>(weight, config)) return false;
}
return true;
for (const auto& config : configs)
test_area<Kernel>(weight, config);
}
} // namespace tests

View File

@ -1,261 +1,345 @@
#ifndef CGAL_WEIGHTS_TESTS_WRAPPERS_H
#define CGAL_WEIGHTS_TESTS_WRAPPERS_H
// STL includes.
#include "utils.h"
#include <CGAL/Weights.h>
#include <vector>
#include <string>
// CGAL includes.
#include <CGAL/Weights.h>
namespace wrappers {
template<typename Kernel>
struct Authalic_wrapper {
struct Authalic_wrapper
{
using FT = typename Kernel::FT;
template<typename Point>
FT weight_a(const Point& t, const Point& r, const Point& p, const Point& q) const {
return CGAL::Weights::authalic_weight(t, r, p, q);
FT weight_a(const Point& p0, const Point& p1, const Point& p2, const Point& q) const
{
return CGAL::Weights::authalic_weight(p0, p1, p2, q);
}
template<typename Point>
FT weight_b(const Point& t, const Point& r, const Point& p, const Point& q) const {
return
CGAL::Weights::half_authalic_weight(
CGAL::Weights::cotangent(t, r, q),
CGAL::Weights::squared_distance(q, r)) +
CGAL::Weights::half_authalic_weight(
CGAL::Weights::cotangent(q, r, p),
CGAL::Weights::squared_distance(q, r));
FT weight_b(const Point& p0, const Point& p1, const Point& p2, const Point& q) const
{
return CGAL::Weights::half_authalic_weight(CGAL::Weights::cotangent(p0, p1, q),
CGAL::Weights::internal::squared_distance(q, p1)) +
CGAL::Weights::half_authalic_weight(CGAL::Weights::cotangent(q, p1, p2),
CGAL::Weights::internal::squared_distance(q, p1));
}
bool supports_3d() const { return true; }
bool is_barycentric() const { return true; }
};
template<typename Kernel>
struct Cotangent_wrapper {
struct Cotangent_wrapper
{
using FT = typename Kernel::FT;
template<typename Point>
FT weight_a(const Point& t, const Point& r, const Point& p, const Point& q) const {
return CGAL::Weights::cotangent_weight(t, r, p, q);
FT weight_a(const Point& p0, const Point& p1, const Point& p2, const Point& q) const
{
return CGAL::Weights::cotangent_weight(p0, p1, p2, q);
}
template<typename Point>
FT weight_b(const Point& t, const Point& r, const Point& p, const Point& q) const {
return
CGAL::Weights::half_cotangent_weight(
CGAL::Weights::cotangent(q, t, r)) +
CGAL::Weights::half_cotangent_weight(
CGAL::Weights::cotangent(r, p, q));
FT weight_b(const Point& p0, const Point& p1, const Point& p2, const Point& q) const
{
return CGAL::Weights::half_cotangent_weight(CGAL::Weights::cotangent(q, p0, p1)) +
CGAL::Weights::half_cotangent_weight(CGAL::Weights::cotangent(p1, p2, q));
}
bool supports_3d() const { return true; }
bool is_barycentric() const { return true; }
};
template<typename Kernel>
struct Tangent_wrapper {
struct Tangent_wrapper
{
using FT = typename Kernel::FT;
template<typename Point>
FT weight_a(const Point& t, const Point& r, const Point& p, const Point& q) const {
FT weight_a(const Point& t, const Point& r, const Point& p, const Point& q) const
{
return CGAL::Weights::tangent_weight(t, r, p, q);
}
template<typename Point>
FT weight_b(const Point& t, const Point& r, const Point& p, const Point& q) const {
return
CGAL::Weights::half_tangent_weight(
CGAL::Weights::distance(r, q),
CGAL::Weights::distance(t, q),
CGAL::Weights::area(r, q, t),
CGAL::Weights::scalar_product(r, q, t)) +
CGAL::Weights::half_tangent_weight(
CGAL::Weights::distance(r, q),
CGAL::Weights::distance(p, q),
CGAL::Weights::area(p, q, r),
CGAL::Weights::scalar_product(p, q, r));
FT weight_b(const Point& t, const Point& r, const Point& p, const Point& q) const
{
return CGAL::Weights::half_tangent_weight(CGAL::Weights::internal::distance(r, q),
CGAL::Weights::internal::distance(t, q),
CGAL::Weights::internal::area(r, q, t),
CGAL::Weights::internal::scalar_product(r, q, t)) +
CGAL::Weights::half_tangent_weight(CGAL::Weights::internal::distance(r, q),
CGAL::Weights::internal::distance(p, q),
CGAL::Weights::internal::area(p, q, r),
CGAL::Weights::internal::scalar_product(p, q, r));
}
bool supports_3d() const { return true; }
bool is_barycentric() const { return true; }
};
template<typename Kernel>
struct Wachspress_wrapper {
struct Wachspress_wrapper
{
using FT = typename Kernel::FT;
using Point_2 = typename Kernel::Point_2;
using Point_3 = typename Kernel::Point_3;
FT weight_a(const Point_2& t, const Point_2& r, const Point_2& p, const Point_2& q) const {
return CGAL::Weights::wachspress_weight(t, r, p, q);
FT weight_a(const Point_2& p0, const Point_2& p1, const Point_2& p2, const Point_2& q) const
{
return CGAL::Weights::wachspress_weight(p0, p1, p2, q);
}
FT weight_a(const Point_3&, const Point_3&, const Point_3&, const Point_3&) const {
FT weight_a(const Point_3&, const Point_3&, const Point_3&, const Point_3&) const
{
return FT(-1);
}
template<typename Point>
FT weight_b(const Point& t, const Point& r, const Point& p, const Point& q) const {
return weight_a(t, r, p, q);
FT weight_b(const Point& p0, const Point& p1, const Point& p2, const Point& q) const
{
return weight_a(p0, p1, p2, q);
}
template<typename Polygon, typename Point, typename Traits, typename OutputIterator>
void compute_on_polygon(
const Polygon& polygon, const Point& query, const Traits& traits, OutputIterator out) const {
void compute_on_polygon(const Polygon& polygon,
const Point& query,
const Traits& traits,
OutputIterator out) const
{
CGAL::Weights::wachspress_weights_2(polygon, query, out, traits);
}
bool supports_3d() const { return false; }
bool is_barycentric() const { return true; }
};
template<typename Kernel>
struct Discrete_harmonic_wrapper {
struct Discrete_harmonic_wrapper
{
using FT = typename Kernel::FT;
using Point_2 = typename Kernel::Point_2;
using Point_3 = typename Kernel::Point_3;
FT weight_a(const Point_2& t, const Point_2& r, const Point_2& p, const Point_2& q) const {
return CGAL::Weights::discrete_harmonic_weight(t, r, p, q);
FT weight_a(const Point_2& p0, const Point_2& p1, const Point_2& p2, const Point_2& q) const
{
return CGAL::Weights::discrete_harmonic_weight(p0, p1, p2, q);
}
FT weight_a(const Point_3&, const Point_3&, const Point_3&, const Point_3&) const {
FT weight_a(const Point_3&, const Point_3&, const Point_3&, const Point_3&) const
{
return FT(-1);
}
template<typename Point>
FT weight_b(const Point& t, const Point& r, const Point& p, const Point& q) const {
return weight_a(t, r, p, q);
FT weight_b(const Point& p0, const Point& p1, const Point& p2, const Point& q) const
{
return weight_a(p0, p1, p2, q);
}
template<typename Polygon, typename Point, typename Traits, typename OutputIterator>
void compute_on_polygon(
const Polygon& polygon, const Point& query, const Traits& traits, OutputIterator out) const {
void compute_on_polygon(const Polygon& polygon,
const Point& query,
const Traits& traits,
OutputIterator out) const
{
CGAL::Weights::discrete_harmonic_weights_2(polygon, query, out, traits);
}
bool supports_3d() const { return false; }
bool is_barycentric() const { return true; }
};
template<typename Kernel>
struct Mean_value_wrapper {
struct Mean_value_wrapper
{
using FT = typename Kernel::FT;
using Point_2 = typename Kernel::Point_2;
using Point_3 = typename Kernel::Point_3;
FT weight_a(const Point_2& t, const Point_2& r, const Point_2& p, const Point_2& q) const {
FT weight_a(const Point_2& t, const Point_2& r, const Point_2& p, const Point_2& q) const
{
return CGAL::Weights::mean_value_weight(t, r, p, q);
}
FT weight_a(const Point_3&, const Point_3&, const Point_3&, const Point_3&) const {
FT weight_a(const Point_3&, const Point_3&, const Point_3&, const Point_3&) const
{
return FT(-1);
}
template<typename Point>
FT weight_b(const Point& t, const Point& r, const Point& p, const Point& q) const {
FT weight_b(const Point& t, const Point& r, const Point& p, const Point& q) const
{
return weight_a(t, r, p, q);
}
template<typename Polygon, typename Point, typename Traits, typename OutputIterator>
void compute_on_polygon(
const Polygon& polygon, const Point& query, const Traits& traits, OutputIterator out) const {
void compute_on_polygon(const Polygon& polygon,
const Point& query,
const Traits& traits,
OutputIterator out) const
{
CGAL::Weights::mean_value_weights_2(polygon, query, out, traits);
}
bool supports_3d() const { return false; }
bool is_barycentric() const { return true; }
};
template<typename Kernel>
struct Three_point_family_wrapper {
struct Three_point_family_wrapper
{
using FT = typename Kernel::FT;
using Point_2 = typename Kernel::Point_2;
using Point_3 = typename Kernel::Point_3;
const FT a;
Three_point_family_wrapper(const FT a) : a(a) { }
FT weight_a(const Point_2& t, const Point_2& r, const Point_2& p, const Point_2& q) const {
FT weight_a(const Point_2& t, const Point_2& r, const Point_2& p, const Point_2& q) const
{
return CGAL::Weights::three_point_family_weight(t, r, p, q, a);
}
FT weight_a(const Point_3&, const Point_3&, const Point_3&, const Point_3&) const {
FT weight_a(const Point_3&, const Point_3&, const Point_3&, const Point_3&) const
{
return FT(-1);
}
template<typename Point>
FT weight_b(const Point& t, const Point& r, const Point& p, const Point& q) const {
FT weight_b(const Point& t, const Point& r, const Point& p, const Point& q) const
{
return weight_a(t, r, p, q);
}
bool supports_3d() const { return false; }
bool is_barycentric() const { return true; }
};
template<typename Kernel>
struct Uniform_region_wrapper {
struct Uniform_region_wrapper
{
using FT = typename Kernel::FT;
template<typename Point>
FT weight(const Point& p, const Point& q, const Point& r) const {
FT weight(const Point& p, const Point& q, const Point& r) const
{
return CGAL::Weights::uniform_area(p, q, r);
}
};
template<typename Kernel>
struct Triangular_region_wrapper {
struct Triangular_region_wrapper
{
using FT = typename Kernel::FT;
template<typename Point>
FT weight(const Point& p, const Point& q, const Point& r) const {
FT weight(const Point& p, const Point& q, const Point& r) const
{
return CGAL::Weights::triangular_area(p, q, r);
}
};
template<typename Kernel>
struct Barycentric_region_wrapper {
struct Barycentric_region_wrapper
{
using FT = typename Kernel::FT;
template<typename Point>
FT weight(const Point& p, const Point& q, const Point& r) const {
FT weight(const Point& p, const Point& q, const Point& r) const
{
return CGAL::Weights::barycentric_area(p, q, r);
}
};
template<typename Kernel>
struct Voronoi_region_wrapper {
struct Voronoi_region_wrapper
{
using FT = typename Kernel::FT;
template<typename Point>
FT weight(const Point& p, const Point& q, const Point& r) const {
FT weight(const Point& p, const Point& q, const Point& r) const
{
return CGAL::Weights::voronoi_area(p, q, r);
}
};
template<typename Kernel>
struct Mixed_voronoi_region_wrapper {
struct Mixed_voronoi_region_wrapper
{
using FT = typename Kernel::FT;
template<typename Point>
FT weight(const Point& p, const Point& q, const Point& r) const {
FT weight(const Point& p, const Point& q, const Point& r) const
{
return CGAL::Weights::mixed_voronoi_area(p, q, r);
}
};
template<typename Kernel>
struct Uniform_wrapper {
struct Uniform_wrapper
{
using FT = typename Kernel::FT;
template<typename Point>
FT weight_a(const Point& t, const Point& r, const Point& p, const Point& q) const {
FT weight_a(const Point& t, const Point& r, const Point& p, const Point& q) const
{
return CGAL::Weights::uniform_weight(t, r, p, q);
}
template<typename Point>
FT weight_b(const Point& t, const Point& r, const Point& p, const Point& q) const {
FT weight_b(const Point& t, const Point& r, const Point& p, const Point& q) const
{
return weight_a(t, r, p, q);
}
bool supports_3d() const { return true; }
bool is_barycentric() const { return false; }
};
template<typename Kernel>
struct Inverse_distance_wrapper {
struct Inverse_distance_wrapper
{
using FT = typename Kernel::FT;
template<typename Point>
FT weight_a(const Point& t, const Point& r, const Point& p, const Point& q) const {
FT weight_a(const Point& t, const Point& r, const Point& p, const Point& q) const
{
return CGAL::Weights::inverse_distance_weight(t, r, p, q);
}
template<typename Point>
FT weight_b(const Point& t, const Point& r, const Point& p, const Point& q) const {
FT weight_b(const Point& t, const Point& r, const Point& p, const Point& q) const
{
return weight_a(t, r, p, q);
}
bool supports_3d() const { return true; }
bool is_barycentric() const { return false; }
};
template<typename Kernel>
struct Shepard_wrapper {
struct Shepard_wrapper
{
using FT = typename Kernel::FT;
const FT a;
Shepard_wrapper(const FT a) : a(a) { }
template<typename Point>
FT weight_a(const Point& t, const Point& r, const Point& p, const Point& q) const {
FT weight_a(const Point& t, const Point& r, const Point& p, const Point& q) const
{
return CGAL::Weights::shepard_weight(t, r, p, q, a);
}
template<typename Point>
FT weight_b(const Point& t, const Point& r, const Point& p, const Point& q) const {
FT weight_b(const Point& t, const Point& r, const Point& p, const Point& q) const
{
return weight_a(t, r, p, q);
}
bool supports_3d() const { return true; }
bool is_barycentric() const { return false; }
};

View File

@ -1,26 +1,29 @@
#include "include/utils.h"
#include "include/wrappers.h"
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include "include/utils.h"
#include "include/wrappers.h"
// Typedefs.
using SCKER = CGAL::Simple_cartesian<double>;
using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel;
using EPECK = CGAL::Exact_predicates_exact_constructions_kernel;
template<typename Kernel>
bool test_kernel() {
void test_kernel()
{
const wrappers::Authalic_wrapper<Kernel> aut;
const wrappers::Wachspress_wrapper<Kernel> whp;
return tests::test_analytic_weight<Kernel>(aut, whp);
const wrappers::Three_point_family_wrapper<Kernel> tpf(0);
tests::test_analytic_weight<Kernel>(aut, whp);
tests::test_analytic_weight<Kernel>(aut, tpf);
}
int main() {
assert(test_kernel<SCKER>());
assert(test_kernel<EPICK>());
assert(test_kernel<EPECK>());
int main(int, char**)
{
test_kernel<SCKER>();
test_kernel<EPICK>();
test_kernel<EPECK>();
std::cout << "* test_authalic_weights: SUCCESS" << std::endl;
return EXIT_SUCCESS;
}

View File

@ -1,25 +1,48 @@
#include "include/utils.h"
#include "include/wrappers.h"
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include "include/utils.h"
#include "include/wrappers.h"
// Typedefs.
using SCKER = CGAL::Simple_cartesian<double>;
using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel;
using EPECK = CGAL::Exact_predicates_exact_constructions_kernel;
template<typename Kernel>
bool test_kernel() {
void test_kernel()
{
using FT = typename Kernel::FT;
using Point_2 = typename Kernel::Point_2;
using Point_3 = typename Kernel::Point_3;
const Point_2 p( 1, 0);
const Point_2 q( 0, 6);
const Point_2 r(-1, 0);
const FT w1 = CGAL::Weights::barycentric_area(p, q, r);
const FT w2 = CGAL::Weights::barycentric_area(r, p, q);
const FT w3 = CGAL::Weights::barycentric_area(q, r, p);
assert(w1 == FT(2)); // medians subdivide a triangle into 6 triangles of equal areas
assert(w1 == w2 && w2 == w3);
const Point_3 s( 0, -1, 0);
const Point_3 t( 0, 0, 6);
const Point_3 u( 0, 1, 0);
const FT w4 = CGAL::Weights::barycentric_area(s, t, u);
const FT w5 = CGAL::Weights::barycentric_area(t, u, s);
const FT w6 = CGAL::Weights::barycentric_area(u, s, t);
assert(w4 == FT(2));
assert(w4 == w5 && w5 == w6);
const wrappers::Barycentric_region_wrapper<Kernel> bar;
return tests::test_region_weight<Kernel>(bar);
tests::test_region_weight<Kernel>(bar);
}
int main() {
assert(test_kernel<SCKER>());
assert(test_kernel<EPICK>());
assert(test_kernel<EPECK>());
int main(int, char**)
{
test_kernel<SCKER>();
test_kernel<EPICK>();
test_kernel<EPECK>();
std::cout << "* test_barycentric_region_weights: SUCCESS" << std::endl;
return EXIT_SUCCESS;
}

View File

@ -1,26 +1,39 @@
#include "include/utils.h"
#include "include/wrappers.h"
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include "include/utils.h"
#include "include/wrappers.h"
// Typedefs.
using SCKER = CGAL::Simple_cartesian<double>;
using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel;
using EPECK = CGAL::Exact_predicates_exact_constructions_kernel;
template<typename Kernel>
bool test_kernel() {
void test_kernel()
{
using FT = typename Kernel::FT;
using Point_2 = typename Kernel::Point_2;
const Point_2 p0(-2, 1);
const Point_2 p1( 0, 1);
const Point_2 p2( 0, 3);
const Point_2 q( -2, 3);
const FT w = CGAL::Weights::cotangent_weight(p0, p1, p2, q);
assert(w == FT(0));
const wrappers::Cotangent_wrapper<Kernel> cot;
const wrappers::Discrete_harmonic_wrapper<Kernel> dhw;
return tests::test_analytic_weight<Kernel>(cot, dhw);
const wrappers::Three_point_family_wrapper<Kernel> tpf(2);
tests::test_analytic_weight<Kernel>(cot, dhw);
tests::test_analytic_weight<Kernel>(cot, tpf);
}
int main() {
assert(test_kernel<SCKER>());
assert(test_kernel<EPICK>());
assert(test_kernel<EPECK>());
int main(int, char**)
{
test_kernel<SCKER>();
test_kernel<EPICK>();
test_kernel<EPECK>();
std::cout << "* test_cotangent_weights: SUCCESS" << std::endl;
return EXIT_SUCCESS;
}

View File

@ -1,17 +1,17 @@
#include "include/utils.h"
#include "include/wrappers.h"
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include "include/utils.h"
#include "include/wrappers.h"
// Typedefs.
using SCKER = CGAL::Simple_cartesian<double>;
using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel;
using EPECK = CGAL::Exact_predicates_exact_constructions_kernel;
template<typename Kernel>
void test_overloads() {
void test_overloads()
{
using FT = typename Kernel::FT;
using Point_2 = typename Kernel::Point_2;
using Point_3 = typename Kernel::Point_3;
@ -19,36 +19,42 @@ void test_overloads() {
const Point_2 r1( 0, -1);
const Point_2 p1( 1, 0);
const Point_2 q1( 0, 0);
const Point_3 t2(-1, 0, 1);
const Point_3 r2( 0, -1, 1);
const Point_3 p2( 1, 0, 1);
const Point_3 q2( 0, 0, 1);
const FT a2 = CGAL::Weights::discrete_harmonic_weight(t1, r1, p1, q1);
const FT a3 = CGAL::Weights::internal::discrete_harmonic_weight(t2, r2, p2, q2);
assert(a2 >= FT(0));
assert(a3 >= FT(0));
const FT a3 = CGAL::Weights::discrete_harmonic_weight(t2, r2, p2, q2);
assert(a2 == FT(4));
assert(a3 == FT(4));
assert(a2 == a3);
struct Traits : public Kernel { };
assert(CGAL::Weights::discrete_harmonic_weight(t1, r1, p1, q1, Traits()) == a2);
assert(CGAL::Weights::internal::discrete_harmonic_weight(t2, r2, p2, q2, Traits()) == a3);
assert(CGAL::Weights::discrete_harmonic_weight(t2, r2, p2, q2, Traits()) == a3);
CGAL::Projection_traits_xy_3<Kernel> ptraits;
const FT a23 = CGAL::Weights::discrete_harmonic_weight(t2, r2, p2, q2, ptraits);
assert(a23 >= FT(0));
assert(a23 == a2 && a23 == a3);
assert(a23 == FT(4));
assert(a23 == a2);
}
template<typename Kernel>
bool test_kernel() {
void test_kernel()
{
test_overloads<Kernel>();
const wrappers::Discrete_harmonic_wrapper<Kernel> dhw;
const wrappers::Cotangent_wrapper<Kernel> cot;
return tests::test_barycentric_weight<Kernel>(dhw, cot);
tests::test_barycentric_weight<Kernel>(dhw, cot);
}
int main() {
assert(test_kernel<SCKER>());
assert(test_kernel<EPICK>());
assert(test_kernel<EPECK>());
int main(int, char**)
{
test_kernel<SCKER>();
test_kernel<EPICK>();
test_kernel<EPECK>();
std::cout << "* test_discrete_harmonic_weights: SUCCESS" << std::endl;
return EXIT_SUCCESS;
}

View File

@ -1,47 +1,53 @@
#include "include/utils.h"
#include "include/wrappers.h"
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include "include/utils.h"
#include "include/wrappers.h"
// Typedefs.
using SCKER = CGAL::Simple_cartesian<double>;
using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel;
using EPECK = CGAL::Exact_predicates_exact_constructions_kernel;
template<typename Kernel>
void test_overloads() {
void test_overloads()
{
using FT = typename Kernel::FT;
using Point_2 = typename Kernel::Point_2;
using Point_3 = typename Kernel::Point_3;
const Point_2 p1(0, 0);
const Point_2 q1(1, 0);
const Point_2 q1(2, 0);
const Point_3 p2(0, 0, 1);
const Point_3 q2(1, 0, 1);
const FT a2 = CGAL::Weights::inverse_distance_weight(p1, q1);
const FT a3 = CGAL::Weights::inverse_distance_weight(p2, q2);
assert(a2 == FT(1));
assert(a3 == FT(1));
assert(CGAL::Weights::inverse_distance_weight(p1, p1, q1, q1) == a2);
assert(CGAL::Weights::inverse_distance_weight(p2, p2, q2, q2) == a3);
const Point_3 q2(2, 0, 1);
const FT w1 = CGAL::Weights::inverse_distance_weight(p1, q1);
const FT w2 = CGAL::Weights::inverse_distance_weight(p2, q2);
assert(w1 == FT(1) / FT(2));
assert(w2 == FT(1) / FT(2));
assert(CGAL::Weights::inverse_distance_weight(p1, p1, q1, q1) == w1);
assert(CGAL::Weights::inverse_distance_weight(p2, p2, q2, q2) == w2);
struct Traits : public Kernel { };
assert(CGAL::Weights::inverse_distance_weight(p1, p1, q1, q1, Traits()) == a2);
assert(CGAL::Weights::inverse_distance_weight(p2, p2, q2, q2, Traits()) == a3);
assert(CGAL::Weights::inverse_distance_weight(p1, q1, Traits()) == w1);
assert(CGAL::Weights::inverse_distance_weight(p2, q2, Traits()) == w2);
assert(CGAL::Weights::inverse_distance_weight(p1, p1, q1, q1, Traits()) == w1);
assert(CGAL::Weights::inverse_distance_weight(p2, p2, q2, q2, Traits()) == w2);
}
template<typename Kernel>
bool test_kernel() {
void test_kernel()
{
test_overloads<Kernel>();
const wrappers::Inverse_distance_wrapper<Kernel> idw;
const wrappers::Shepard_wrapper<Kernel> spw(1);
return tests::test_analytic_weight<Kernel>(idw, spw);
tests::test_analytic_weight<Kernel>(idw, spw);
}
int main() {
assert(test_kernel<SCKER>());
assert(test_kernel<EPICK>());
assert(test_kernel<EPECK>());
int main(int, char**)
{
test_kernel<SCKER>();
test_kernel<EPICK>();
test_kernel<EPECK>();
std::cout << "* test_inverse_distance_weights: SUCCESS" << std::endl;
return EXIT_SUCCESS;
}

View File

@ -1,17 +1,17 @@
#include "include/utils.h"
#include "include/wrappers.h"
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include "include/utils.h"
#include "include/wrappers.h"
// Typedefs.
using SCKER = CGAL::Simple_cartesian<double>;
using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel;
using EPECK = CGAL::Exact_predicates_exact_constructions_kernel;
template<typename Kernel>
void test_overloads() {
void test_overloads()
{
using FT = typename Kernel::FT;
using Point_2 = typename Kernel::Point_2;
using Point_3 = typename Kernel::Point_3;
@ -23,14 +23,16 @@ void test_overloads() {
const Point_3 r2( 0, -1, 1);
const Point_3 p2( 1, 0, 1);
const Point_3 q2( 0, 0, 1);
const FT a2 = CGAL::Weights::mean_value_weight(t1, r1, p1, q1);
const FT a3 = CGAL::Weights::internal::mean_value_weight(t2, r2, p2, q2);
const FT a3 = CGAL::Weights::mean_value_weight(t2, r2, p2, q2);
assert(a2 >= FT(0));
assert(a3 >= FT(0));
assert(a2 == a3);
struct Traits : public Kernel { };
assert(CGAL::Weights::mean_value_weight(t1, r1, p1, q1, Traits()) == a2);
assert(CGAL::Weights::internal::mean_value_weight(t2, r2, p2, q2, Traits()) == a3);
assert(CGAL::Weights::mean_value_weight(t2, r2, p2, q2, Traits()) == a3);
CGAL::Projection_traits_xy_3<Kernel> ptraits;
const FT a23 = CGAL::Weights::mean_value_weight(t2, r2, p2, q2, ptraits);
assert(a23 >= FT(0));
@ -38,17 +40,21 @@ void test_overloads() {
}
template<typename Kernel>
bool test_kernel() {
void test_kernel()
{
test_overloads<Kernel>();
const wrappers::Mean_value_wrapper<Kernel> mvw;
const wrappers::Tangent_wrapper<Kernel> tan;
return tests::test_barycentric_weight<Kernel>(mvw, tan);
const wrappers::Three_point_family_wrapper<Kernel> tpf(1);
tests::test_barycentric_weight<Kernel>(mvw, tan);
tests::test_barycentric_weight<Kernel>(mvw, tpf);
}
int main() {
assert(test_kernel<SCKER>());
assert(test_kernel<EPICK>());
assert(test_kernel<EPECK>());
int main(int, char**)
{
test_kernel<SCKER>();
test_kernel<EPICK>();
test_kernel<EPECK>();
std::cout << "* test_mean_value_weights: SUCCESS" << std::endl;
return EXIT_SUCCESS;
}

View File

@ -1,25 +1,26 @@
#include "include/utils.h"
#include "include/wrappers.h"
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include "include/utils.h"
#include "include/wrappers.h"
// Typedefs.
using SCKER = CGAL::Simple_cartesian<double>;
using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel;
using EPECK = CGAL::Exact_predicates_exact_constructions_kernel;
template<typename Kernel>
bool test_kernel() {
void test_kernel()
{
const wrappers::Mixed_voronoi_region_wrapper<Kernel> mix;
return tests::test_region_weight<Kernel>(mix);
tests::test_region_weight<Kernel>(mix);
}
int main() {
assert(test_kernel<SCKER>());
assert(test_kernel<EPICK>());
assert(test_kernel<EPECK>());
int main(int, char**)
{
test_kernel<SCKER>();
test_kernel<EPICK>();
test_kernel<EPECK>();
std::cout << "* test_mixed_voronoi_region_weights: SUCCESS" << std::endl;
return EXIT_SUCCESS;
}

View File

@ -1,18 +1,19 @@
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Projection_traits_xy_3.h>
#include <CGAL/Projection_traits_xz_3.h>
#include <CGAL/Projection_traits_yz_3.h>
#include <CGAL/Weights.h>
// Typedefs.
using SCKER = CGAL::Simple_cartesian<double>;
using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel;
using EPECK = CGAL::Exact_predicates_exact_constructions_kernel;
template<typename Kernel>
void test_kernel() {
void test_kernel()
{
using FT = typename Kernel::FT;
using Point_2 = typename Kernel::Point_2;
using Point_3 = typename Kernel::Point_3;
@ -110,7 +111,8 @@ void test_kernel() {
assert(CGAL::Weights::three_point_family_weight(t3, r3, p3, q3, 1, yz_traits) == ref_value);
}
int main() {
int main(int, char**)
{
test_kernel<SCKER>();
test_kernel<EPICK>();
test_kernel<EPECK>();

View File

@ -1,49 +1,55 @@
#include "include/utils.h"
#include "include/wrappers.h"
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include "include/utils.h"
#include "include/wrappers.h"
// Typedefs.
using SCKER = CGAL::Simple_cartesian<double>;
using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel;
using EPECK = CGAL::Exact_predicates_exact_constructions_kernel;
template<typename Kernel>
void test_overloads() {
void test_overloads()
{
using FT = typename Kernel::FT;
using Point_2 = typename Kernel::Point_2;
using Point_3 = typename Kernel::Point_3;
const Point_2 p1(0, 0);
const Point_2 q1(1, 0);
const Point_2 q1(2, 0);
const Point_3 p2(0, 0, 1);
const Point_3 q2(1, 0, 1);
const FT a2 = CGAL::Weights::shepard_weight(p1, q1);
const FT a3 = CGAL::Weights::shepard_weight(p2, q2);
assert(a2 == FT(1));
assert(a3 == FT(1));
assert(CGAL::Weights::shepard_weight(p1, p1, q1, q1) == a2);
assert(CGAL::Weights::shepard_weight(p2, p2, q2, q2) == a3);
const Point_3 q2(2, 0, 1);
const FT a2 = CGAL::Weights::shepard_weight(p1, q1, 3);
const FT a3 = CGAL::Weights::shepard_weight(p2, q2, 3);
assert(a2 == FT(1)/FT(8));
assert(a3 == FT(1)/FT(8));
assert(CGAL::Weights::shepard_weight(p1, p1, q1, q1, 3) == a2);
assert(CGAL::Weights::shepard_weight(p2, p2, q2, q2, 3) == a3);
struct Traits : public Kernel { };
assert(CGAL::Weights::shepard_weight(p1, p1, q1, q1, 1, Traits()) == a2);
assert(CGAL::Weights::shepard_weight(p2, p2, q2, q2, 1, Traits()) == a3);
assert(CGAL::Weights::shepard_weight(p1, p1, q1, q1, 3, Traits()) == a2);
assert(CGAL::Weights::shepard_weight(p2, p2, q2, q2, 3, Traits()) == a3);
}
template<typename Kernel>
bool test_kernel() {
void test_kernel()
{
test_overloads<Kernel>();
const wrappers::Shepard_wrapper<Kernel> spwa(1);
const wrappers::Shepard_wrapper<Kernel> spwb(2);
const wrappers::Inverse_distance_wrapper<Kernel> idw;
assert(tests::test_analytic_weight<Kernel>(spwa, idw));
return tests::test_analytic_weight<Kernel>(spwb, spwb);
tests::test_analytic_weight<Kernel>(spwa, idw);
tests::test_analytic_weight<Kernel>(spwb, spwb);
}
int main() {
assert(test_kernel<SCKER>());
assert(test_kernel<EPICK>());
assert(test_kernel<EPECK>());
int main(int, char**)
{
test_kernel<SCKER>();
test_kernel<EPICK>();
test_kernel<EPECK>();
std::cout << "* test_shepard_weights: SUCCESS" << std::endl;
return EXIT_SUCCESS;
}

View File

@ -1,26 +1,29 @@
#include "include/utils.h"
#include "include/wrappers.h"
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include "include/utils.h"
#include "include/wrappers.h"
// Typedefs.
using SCKER = CGAL::Simple_cartesian<double>;
using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel;
using EPECK = CGAL::Exact_predicates_exact_constructions_kernel;
template<typename Kernel>
bool test_kernel() {
void test_kernel()
{
const wrappers::Tangent_wrapper<Kernel> tan;
const wrappers::Mean_value_wrapper<Kernel> mvw;
return tests::test_analytic_weight<Kernel>(tan, mvw);
const wrappers::Three_point_family_wrapper<Kernel> tpf(1);
tests::test_analytic_weight<Kernel>(tan, mvw);
tests::test_analytic_weight<Kernel>(tan, tpf);
}
int main() {
assert(test_kernel<SCKER>());
assert(test_kernel<EPICK>());
assert(test_kernel<EPECK>());
int main(int, char**)
{
test_kernel<SCKER>();
test_kernel<EPICK>();
test_kernel<EPECK>();
std::cout << "* test_tangent_weights: SUCCESS" << std::endl;
return EXIT_SUCCESS;
}

View File

@ -1,17 +1,17 @@
#include "include/utils.h"
#include "include/wrappers.h"
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include "include/utils.h"
#include "include/wrappers.h"
// Typedefs.
using SCKER = CGAL::Simple_cartesian<double>;
using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel;
using EPECK = CGAL::Exact_predicates_exact_constructions_kernel;
template<typename Kernel>
void test_overloads() {
void test_overloads()
{
using FT = typename Kernel::FT;
using Point_2 = typename Kernel::Point_2;
using Point_3 = typename Kernel::Point_3;
@ -23,14 +23,16 @@ void test_overloads() {
const Point_3 r2( 0, -1, 1);
const Point_3 p2( 1, 0, 1);
const Point_3 q2( 0, 0, 1);
const FT a2 = CGAL::Weights::three_point_family_weight(t1, r1, p1, q1);
const FT a3 = CGAL::Weights::internal::three_point_family_weight(t2, r2, p2, q2);
const FT a3 = CGAL::Weights::three_point_family_weight(t2, r2, p2, q2);
assert(a2 >= FT(0));
assert(a3 >= FT(0));
assert(a2 == a3);
struct Traits : public Kernel { };
assert(CGAL::Weights::three_point_family_weight(t1, r1, p1, q1, 1, Traits()) == a2);
assert(CGAL::Weights::internal::three_point_family_weight(t2, r2, p2, q2, 1, Traits()) == a3);
assert(CGAL::Weights::three_point_family_weight(t2, r2, p2, q2, 1, Traits()) == a3);
CGAL::Projection_traits_xy_3<Kernel> ptraits;
const FT a23 = CGAL::Weights::three_point_family_weight(t2, r2, p2, q2, 0, ptraits);
assert(a23 >= FT(0));
@ -38,7 +40,8 @@ void test_overloads() {
}
template<typename Kernel>
bool test_kernel() {
void test_kernel()
{
test_overloads<Kernel>();
using FT = typename Kernel::FT;
const FT h = FT(1) / FT(2);
@ -49,16 +52,18 @@ bool test_kernel() {
const wrappers::Wachspress_wrapper<Kernel> whp;
const wrappers::Mean_value_wrapper<Kernel> mvw;
const wrappers::Discrete_harmonic_wrapper<Kernel> dhw;
assert(tests::test_analytic_weight<Kernel>(tpfa, whp));
assert(tests::test_analytic_weight<Kernel>(tpfb, mvw));
assert(tests::test_analytic_weight<Kernel>(tpfc, dhw));
return tests::test_analytic_weight<Kernel>(tpfd, tpfd);
tests::test_analytic_weight<Kernel>(tpfa, whp);
tests::test_analytic_weight<Kernel>(tpfb, mvw);
tests::test_analytic_weight<Kernel>(tpfc, dhw);
tests::test_analytic_weight<Kernel>(tpfd, tpfd);
}
int main() {
assert(test_kernel<SCKER>());
assert(test_kernel<EPICK>());
assert(test_kernel<EPECK>());
int main(int, char**)
{
test_kernel<SCKER>();
test_kernel<EPICK>();
test_kernel<EPECK>();
std::cout << "* test_three_point_family_weights: SUCCESS" << std::endl;
return EXIT_SUCCESS;
}

View File

@ -1,25 +1,26 @@
#include "include/utils.h"
#include "include/wrappers.h"
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include "include/utils.h"
#include "include/wrappers.h"
// Typedefs.
using SCKER = CGAL::Simple_cartesian<double>;
using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel;
using EPECK = CGAL::Exact_predicates_exact_constructions_kernel;
template<typename Kernel>
bool test_kernel() {
void test_kernel()
{
const wrappers::Triangular_region_wrapper<Kernel> tri;
return tests::test_region_weight<Kernel>(tri);
tests::test_region_weight<Kernel>(tri);
}
int main() {
assert(test_kernel<SCKER>());
assert(test_kernel<EPICK>());
assert(test_kernel<EPECK>());
int main(int, char**)
{
test_kernel<SCKER>();
test_kernel<EPICK>();
test_kernel<EPECK>();
std::cout << "* test_triangular_region_weights: SUCCESS" << std::endl;
return EXIT_SUCCESS;
}

View File

@ -1,17 +1,17 @@
#include "include/utils.h"
#include "include/wrappers.h"
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include "include/utils.h"
#include "include/wrappers.h"
// Typedefs.
using SCKER = CGAL::Simple_cartesian<double>;
using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel;
using EPECK = CGAL::Exact_predicates_exact_constructions_kernel;
template<typename Kernel>
void test_overloads() {
void test_overloads()
{
using FT = typename Kernel::FT;
using Point_2 = typename Kernel::Point_2;
using Point_3 = typename Kernel::Point_3;
@ -20,22 +20,25 @@ void test_overloads() {
const Point_3 q(0, 0, 0);
assert(CGAL::Weights::uniform_area(p, p, p) == a);
assert(CGAL::Weights::uniform_area(q, q, q) == a);
struct Traits : public Kernel { };
assert(CGAL::Weights::uniform_area(p, p, p, Traits()) == a);
assert(CGAL::Weights::uniform_area(q, q, q, Traits()) == a);
}
template<typename Kernel>
bool test_kernel() {
void test_kernel()
{
test_overloads<Kernel>();
const wrappers::Uniform_region_wrapper<Kernel> uni;
return tests::test_region_weight<Kernel>(uni);
tests::test_region_weight<Kernel>(uni);
}
int main() {
assert(test_kernel<SCKER>());
assert(test_kernel<EPICK>());
assert(test_kernel<EPECK>());
int main(int, char**)
{
test_kernel<SCKER>();
test_kernel<EPICK>();
test_kernel<EPECK>();
std::cout << "* test_uniform_region_weights: SUCCESS" << std::endl;
return EXIT_SUCCESS;
}

View File

@ -1,41 +1,46 @@
#include "include/utils.h"
#include "include/wrappers.h"
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include "include/utils.h"
#include "include/wrappers.h"
// Typedefs.
using SCKER = CGAL::Simple_cartesian<double>;
using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel;
using EPECK = CGAL::Exact_predicates_exact_constructions_kernel;
template<typename Kernel>
void test_overloads() {
void test_overloads()
{
using FT = typename Kernel::FT;
using Point_2 = typename Kernel::Point_2;
using Point_3 = typename Kernel::Point_3;
const FT a = FT(1);
const Point_2 p(0, 0);
const Point_3 q(0, 0, 0);
assert(CGAL::Weights::uniform_weight(p, p, p, p) == a);
assert(CGAL::Weights::uniform_weight(q, q, q, q) == a);
struct Traits : public Kernel { };
assert(CGAL::Weights::uniform_weight(p, p, p, p, Traits()) == a);
assert(CGAL::Weights::uniform_weight(q, q, q, q, Traits()) == a);
}
template<typename Kernel>
bool test_kernel() {
void test_kernel()
{
test_overloads<Kernel>();
const wrappers::Uniform_wrapper<Kernel> uni;
return tests::test_analytic_weight<Kernel>(uni, uni);
tests::test_analytic_weight<Kernel>(uni, uni);
}
int main() {
assert(test_kernel<SCKER>());
assert(test_kernel<EPICK>());
assert(test_kernel<EPECK>());
int main(int, char**)
{
test_kernel<SCKER>();
test_kernel<EPICK>();
test_kernel<EPECK>();
std::cout << "* test_uniform_weights: SUCCESS" << std::endl;
return EXIT_SUCCESS;
}

View File

@ -1,25 +1,42 @@
#include "include/utils.h"
#include "include/wrappers.h"
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include "include/utils.h"
#include "include/wrappers.h"
// Typedefs.
using SCKER = CGAL::Simple_cartesian<double>;
using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel;
using EPECK = CGAL::Exact_predicates_exact_constructions_kernel;
template<typename Kernel>
bool test_kernel() {
void test_kernel()
{
using FT = typename Kernel::FT;
using Point_2 = typename Kernel::Point_2;
using Point_3 = typename Kernel::Point_3;
const Point_2 p( 2, 0);
const Point_2 q( 0, 2);
const Point_2 r(-2, 0);
const FT w1 = CGAL::Weights::voronoi_area(p, q, r);
assert(w1 == FT(2));
const Point_3 s( 0, -2, 0);
const Point_3 t( 0, 0, 2);
const Point_3 u( 0, 2, 0);
const FT w4 = CGAL::Weights::voronoi_area(s, t, u);
assert(w4 == FT(2));
const wrappers::Voronoi_region_wrapper<Kernel> vor;
return tests::test_region_weight<Kernel>(vor);
tests::test_region_weight<Kernel>(vor);
}
int main() {
assert(test_kernel<SCKER>());
assert(test_kernel<EPICK>());
assert(test_kernel<EPECK>());
int main(int, char**)
{
test_kernel<SCKER>();
test_kernel<EPICK>();
test_kernel<EPECK>();
std::cout << "* test_voronoi_region_weights: SUCCESS" << std::endl;
return EXIT_SUCCESS;
}

View File

@ -1,17 +1,17 @@
#include "include/utils.h"
#include "include/wrappers.h"
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include "include/utils.h"
#include "include/wrappers.h"
// Typedefs.
using SCKER = CGAL::Simple_cartesian<double>;
using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel;
using EPECK = CGAL::Exact_predicates_exact_constructions_kernel;
template<typename Kernel>
void test_overloads() {
void test_overloads()
{
using FT = typename Kernel::FT;
using Point_2 = typename Kernel::Point_2;
using Point_3 = typename Kernel::Point_3;
@ -23,32 +23,38 @@ void test_overloads() {
const Point_3 r2( 0, -1, 1);
const Point_3 p2( 1, 0, 1);
const Point_3 q2( 0, 0, 1);
const FT a2 = CGAL::Weights::wachspress_weight(t1, r1, p1, q1);
const FT a3 = CGAL::Weights::internal::wachspress_weight(t2, r2, p2, q2);
assert(a2 >= FT(0));
assert(a3 >= FT(0));
const FT a3 = CGAL::Weights::wachspress_weight(t2, r2, p2, q2);
assert(a2 == FT(4));
assert(a3 == FT(4));
assert(a2 == a3);
struct Traits : public Kernel { };
assert(CGAL::Weights::wachspress_weight(t1, r1, p1, q1, Traits()) == a2);
assert(CGAL::Weights::internal::wachspress_weight(t2, r2, p2, q2, Traits()) == a3);
assert(CGAL::Weights::wachspress_weight(t2, r2, p2, q2, Traits()) == a3);
CGAL::Projection_traits_xy_3<Kernel> ptraits;
const FT a23 = CGAL::Weights::wachspress_weight(t2, r2, p2, q2, ptraits);
assert(a23 >= FT(0));
assert(a23 == a2 && a23 == a3);
assert(a23 == FT(4));
assert(a23 == a2);
}
template<typename Kernel>
bool test_kernel() {
void test_kernel()
{
test_overloads<Kernel>();
const wrappers::Wachspress_wrapper<Kernel> whp;
const wrappers::Authalic_wrapper<Kernel> aut;
return tests::test_barycentric_weight<Kernel>(whp, aut);
const wrappers::Three_point_family_wrapper<Kernel> tpf(0);
tests::test_barycentric_weight<Kernel>(whp, aut);
tests::test_barycentric_weight<Kernel>(whp, tpf);
}
int main() {
assert(test_kernel<SCKER>());
assert(test_kernel<EPICK>());
assert(test_kernel<EPECK>());
int main(int, char**)
{
test_kernel<SCKER>();
test_kernel<EPICK>();
test_kernel<EPECK>();
std::cout << "* test_wachspress_weights: SUCCESS" << std::endl;
return EXIT_SUCCESS;
}