mirror of https://github.com/CGAL/cgal
Merge remote-tracking branch 'cgal/releases/CGAL-5.0-branch'
This commit is contained in:
commit
a8f9858099
|
|
@ -35,17 +35,17 @@ int main(int argc, char* argv[])
|
|||
|
||||
std::ifstream in((argc>1)?argv[1]:"cube.off");
|
||||
in >> S;
|
||||
assert( CGAL::is_valid_polygon_mesh(S) );
|
||||
|
||||
// Note that the vertex_point property of the Source and Target1
|
||||
// come from different kernels.
|
||||
typedef CGAL::Surface_mesh<Point> Target1;
|
||||
Target1 T1;
|
||||
{
|
||||
CGAL::copy_face_graph(S, T1);
|
||||
std::ofstream out("sm.off");
|
||||
out.precision(17);
|
||||
out << T1;
|
||||
}
|
||||
CGAL::copy_face_graph(S, T1);
|
||||
assert( CGAL::is_valid_polygon_mesh(T1) );
|
||||
assert( vertices(S).size()==vertices(T1).size() );
|
||||
assert( halfedges(S).size()==halfedges(T1).size() );
|
||||
assert( faces(S).size()==faces(T1).size() );
|
||||
|
||||
#if defined(CGAL_USE_OPENMESH)
|
||||
typedef OpenMesh::PolyMesh_ArrayKernelT</* MyTraits*/> Target2;
|
||||
|
|
@ -63,7 +63,13 @@ int main(int argc, char* argv[])
|
|||
CGAL::copy_face_graph(S, T2, CGAL::parameters::vertex_to_vertex_output_iterator(std::inserter(v2v, v2v.end()))
|
||||
.halfedge_to_halfedge_output_iterator(std::inserter(h2h, h2h.end()))
|
||||
.face_to_face_output_iterator(std::inserter(f2f, f2f.end())));
|
||||
OpenMesh::IO::write_mesh(T2, "om.off");
|
||||
assert( CGAL::is_valid_polygon_mesh(T2) );
|
||||
assert( v2v.size()==vertices(T2).size() );
|
||||
assert( h2h.size()==halfedges(T2).size() );
|
||||
assert( f2f.size()==faces(T2).size() );
|
||||
assert( vertices(S).size()==vertices(T2).size() );
|
||||
assert( halfedges(S).size()==halfedges(T2).size() );
|
||||
assert( faces(S).size()==faces(T2).size() );
|
||||
}
|
||||
#endif
|
||||
S.clear();
|
||||
|
|
@ -80,13 +86,14 @@ int main(int argc, char* argv[])
|
|||
boost::unordered_map<source_vertex_descriptor, tm_vertex_descriptor> v2v;
|
||||
boost::unordered_map<source_halfedge_descriptor, tm_halfedge_descriptor> h2h;
|
||||
boost::unordered_map<source_face_descriptor, tm_face_descriptor> f2f;
|
||||
CGAL::copy_face_graph(T1, S, std::inserter(v2v, v2v.end()), std::inserter(h2h, h2h.end()));
|
||||
std::ofstream out("reverse.off");
|
||||
out.precision(17);
|
||||
out << S;
|
||||
CGAL::copy_face_graph(T1, S, CGAL::parameters::vertex_to_vertex_map(boost::make_assoc_property_map(v2v))
|
||||
.halfedge_to_halfedge_output_iterator(std::inserter(h2h, h2h.end()))
|
||||
.face_to_face_map(boost::make_assoc_property_map(f2f)));
|
||||
|
||||
assert( CGAL::is_valid_polygon_mesh(S) );
|
||||
assert( vertices(S).size()==vertices(T1).size() );
|
||||
assert( halfedges(S).size()==halfedges(T1).size() );
|
||||
assert( faces(S).size()==faces(T1).size() );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -158,19 +158,15 @@ struct Dual_face_index_pmap{
|
|||
}
|
||||
};
|
||||
|
||||
template<typename P, typename Property,
|
||||
bool is_edge = boost::is_same<boost::edge_property_tag,
|
||||
typename boost::property_kind<Property>::type>::value>
|
||||
struct Dual_property_maps : boost::property_map<P, Property> {};
|
||||
|
||||
template< typename P, typename Property>
|
||||
struct Dual_property_maps<P, Property, false> {};
|
||||
|
||||
} //end of namespace internal
|
||||
|
||||
template <typename P, typename Property>
|
||||
struct property_map<CGAL::Dual<P>, Property>
|
||||
: internal::Dual_property_maps<P, Property> {};
|
||||
template <typename P>
|
||||
struct property_map<CGAL::Dual<P>, halfedge_index_t>
|
||||
: boost::property_map<P, halfedge_index_t> {};
|
||||
|
||||
template <typename P>
|
||||
struct property_map<CGAL::Dual<P>, edge_index_t>
|
||||
: boost::property_map<P, edge_index_t> {};
|
||||
|
||||
template <typename P>
|
||||
struct property_map<CGAL::Dual<P>, boost::vertex_index_t>
|
||||
|
|
@ -190,53 +186,49 @@ struct property_map<CGAL::Dual<P>, boost::face_index_t>
|
|||
|
||||
namespace CGAL {
|
||||
|
||||
template <typename P, typename Property>
|
||||
typename boost::property_map<P, Property>::type
|
||||
get(Property p, Dual<P>& dual)
|
||||
{
|
||||
return get(p, dual.primal());
|
||||
#define CGAL_GET_OVERLOADS(Property) \
|
||||
\
|
||||
template <typename P> \
|
||||
typename boost::property_map<P, Property>::type \
|
||||
get(Property p, Dual<P>& dual) \
|
||||
{ \
|
||||
return get(p, dual.primal()); \
|
||||
} \
|
||||
\
|
||||
template <typename P> \
|
||||
typename boost::property_map<P, Property>::const_type \
|
||||
get(Property p, const Dual<P>& dual) \
|
||||
{ \
|
||||
return get(p, dual.primal()); \
|
||||
} \
|
||||
\
|
||||
template <typename P, typename Key > \
|
||||
typename boost::property_map_value<P, Property>::type \
|
||||
get(Property p, const Dual<P>& dual, const Key& k) \
|
||||
{ \
|
||||
return get(p, dual.primal(), k); \
|
||||
}
|
||||
|
||||
template <typename P, typename Property>
|
||||
typename boost::property_map<P, Property>::const_type
|
||||
get(Property p, const Dual<P>& dual)
|
||||
{
|
||||
return get(p, dual.primal());
|
||||
}
|
||||
CGAL_GET_OVERLOADS(boost::edge_index_t)
|
||||
CGAL_GET_OVERLOADS(boost::halfedge_index_t)
|
||||
CGAL_GET_OVERLOADS(boost::vertex_point_t)
|
||||
|
||||
template <typename P, typename Property, typename Key >
|
||||
typename boost::property_map_value<P, Property>::type
|
||||
get(Property p, const Dual<P>& dual, const Key& k)
|
||||
{
|
||||
return get(p, dual.primal(), k);
|
||||
}
|
||||
|
||||
template<typename G, typename P, typename>
|
||||
struct Property_map_value_dummy {
|
||||
typedef typename boost::property_map_value<G, P>::type type;
|
||||
};
|
||||
#undef CGAL_GET_OVERLOADS
|
||||
|
||||
template <typename P, typename Key>
|
||||
typename Property_map_value_dummy<Dual<P>, boost::vertex_index_t, Key>::type
|
||||
typename boost::property_map_value<Dual<P>, boost::vertex_index_t>::type
|
||||
get(boost::vertex_index_t, const Dual<P>& dual, const Key& k)
|
||||
{
|
||||
return get(typename boost::internal::Dual_vertex_index_pmap<P>(dual.primal()), k);
|
||||
}
|
||||
|
||||
template <typename P, typename Key>
|
||||
typename Property_map_value_dummy<Dual<P>, boost::face_index_t, Key>::type
|
||||
typename boost::property_map_value<Dual<P>, boost::face_index_t>::type
|
||||
get(boost::face_index_t, const Dual<P>& dual, const Key& k)
|
||||
{
|
||||
return get(typename boost::internal::Dual_face_index_pmap<P>(dual.primal()), k);
|
||||
}
|
||||
|
||||
template <typename P, typename Property, typename Key, typename Value>
|
||||
void
|
||||
put(Property p, const Dual<P>& dual, const Key& k, const Value& val)
|
||||
{
|
||||
put(p, dual.primal(), k, val);
|
||||
}
|
||||
|
||||
template <typename P>
|
||||
typename boost::internal::Dual_vertex_index_pmap<P>
|
||||
get(boost::vertex_index_t, const Dual<P>& dual)
|
||||
|
|
|
|||
|
|
@ -1048,6 +1048,36 @@ get(PropertyTag ptag, Face_filtered_graph<Graph, FIMap, VIMap, HIMap>& w)
|
|||
return get(ptag, w.graph());
|
||||
}
|
||||
|
||||
#define CGAL_FFG_DYNAMIC_PMAP_SPEC(TAG) \
|
||||
template <class Graph, \
|
||||
typename FIMap, \
|
||||
typename VIMap, \
|
||||
typename HIMap, \
|
||||
class T> \
|
||||
typename boost::property_map<Graph, TAG<T> >::const_type \
|
||||
get(TAG<T> ptag, const Face_filtered_graph<Graph, FIMap, VIMap, HIMap>& w) \
|
||||
{ \
|
||||
return get(ptag, w.graph()); \
|
||||
} \
|
||||
\
|
||||
template <class Graph, \
|
||||
typename FIMap, \
|
||||
typename VIMap, \
|
||||
typename HIMap, \
|
||||
class T> \
|
||||
typename boost::property_map<Graph, TAG<T> >::type \
|
||||
get(TAG<T> ptag, Face_filtered_graph<Graph, FIMap, VIMap, HIMap>& w) \
|
||||
{ \
|
||||
return get(ptag, w.graph()); \
|
||||
}
|
||||
|
||||
CGAL_FFG_DYNAMIC_PMAP_SPEC(dynamic_vertex_property_t)
|
||||
CGAL_FFG_DYNAMIC_PMAP_SPEC(dynamic_halfedge_property_t)
|
||||
CGAL_FFG_DYNAMIC_PMAP_SPEC(dynamic_edge_property_t)
|
||||
CGAL_FFG_DYNAMIC_PMAP_SPEC(dynamic_face_property_t)
|
||||
|
||||
#undef CGAL_FFG_DYNAMIC_PMAP_SPEC
|
||||
|
||||
//specializations for indices
|
||||
template <class Graph,
|
||||
typename FIMap,
|
||||
|
|
|
|||
|
|
@ -32,17 +32,15 @@ namespace CGAL {
|
|||
namespace internal {
|
||||
|
||||
template <typename SourceMesh, typename TargetMesh,
|
||||
typename Hmap,
|
||||
typename V2V, typename H2H, typename F2F,
|
||||
typename Src_vpm, typename Tgt_vpm>
|
||||
void copy_face_graph_impl(const SourceMesh& sm, TargetMesh& tm,
|
||||
Hmap hmap,
|
||||
V2V v2v, H2H h2h, F2F f2f,
|
||||
Src_vpm sm_vpm, Tgt_vpm tm_vpm )
|
||||
{
|
||||
typedef typename boost::graph_traits<SourceMesh>::vertex_descriptor sm_vertex_descriptor;
|
||||
typedef typename boost::graph_traits<TargetMesh>::vertex_descriptor tm_vertex_descriptor;
|
||||
typedef typename boost::graph_traits<TargetMesh>::vertex_iterator tm_vertex_iterator;
|
||||
typedef typename boost::graph_traits<TargetMesh>::halfedge_iterator tm_halfedge_iterator;
|
||||
|
||||
typedef typename boost::graph_traits<SourceMesh>::face_descriptor sm_face_descriptor;
|
||||
typedef typename boost::graph_traits<TargetMesh>::face_descriptor tm_face_descriptor;
|
||||
|
|
@ -57,10 +55,14 @@ void copy_face_graph_impl(const SourceMesh& sm, TargetMesh& tm,
|
|||
typename Kernel_traits<typename boost::property_traits<Tgt_vpm>::value_type>::type >
|
||||
conv;
|
||||
|
||||
typedef CGAL::dynamic_halfedge_property_t<tm_halfedge_descriptor> Dyn_h_tag;
|
||||
typename boost::property_map<SourceMesh, Dyn_h_tag >::const_type hs_to_ht = get(Dyn_h_tag(), sm);
|
||||
|
||||
std::vector<tm_halfedge_descriptor> tm_border_halfedges;
|
||||
std::vector<sm_halfedge_descriptor> sm_border_halfedges;
|
||||
|
||||
tm_face_descriptor tm_null_face = boost::graph_traits<TargetMesh>::null_face();
|
||||
const tm_face_descriptor tm_null_face = boost::graph_traits<TargetMesh>::null_face();
|
||||
const tm_vertex_descriptor tm_null_vertex = boost::graph_traits<TargetMesh>::null_vertex();
|
||||
|
||||
reserve(tm, static_cast<typename boost::graph_traits<TargetMesh>::vertices_size_type>(vertices(sm).size()),
|
||||
static_cast<typename boost::graph_traits<TargetMesh>::edges_size_type>(edges(sm).size()),
|
||||
|
|
@ -77,8 +79,8 @@ void copy_face_graph_impl(const SourceMesh& sm, TargetMesh& tm,
|
|||
set_next( tm_h, tm_h, tm );
|
||||
set_next( tm_h_opp, tm_h_opp, tm );
|
||||
|
||||
put(hmap, sm_h, tm_h);
|
||||
put(hmap, sm_h_opp, tm_h_opp);
|
||||
put(hs_to_ht, sm_h, tm_h);
|
||||
put(hs_to_ht, sm_h_opp, tm_h_opp);
|
||||
*h2h++=std::make_pair(sm_h, tm_h);
|
||||
*h2h++=std::make_pair(sm_h_opp, tm_h_opp);
|
||||
|
||||
|
|
@ -106,6 +108,8 @@ void copy_face_graph_impl(const SourceMesh& sm, TargetMesh& tm,
|
|||
set_target(tm_h, tm_h_tgt, tm);
|
||||
put(tm_vpm, tm_h_tgt, conv(get(sm_vpm, sm_h_tgt)));
|
||||
}
|
||||
else
|
||||
set_target(tm_h, tm_null_vertex, tm);
|
||||
if ( halfedge(sm_h_src,sm)==sm_h_opp )
|
||||
{
|
||||
tm_vertex_descriptor tm_h_src = add_vertex(tm);
|
||||
|
|
@ -114,6 +118,8 @@ void copy_face_graph_impl(const SourceMesh& sm, TargetMesh& tm,
|
|||
set_target(tm_h_opp, tm_h_src, tm);
|
||||
put(tm_vpm, tm_h_src, conv(get(sm_vpm, sm_h_src)));
|
||||
}
|
||||
else
|
||||
set_target(tm_h_opp, tm_null_vertex, tm);
|
||||
}
|
||||
//create faces and connect halfedges
|
||||
for(sm_face_descriptor sm_f : faces(sm))
|
||||
|
|
@ -122,13 +128,13 @@ void copy_face_graph_impl(const SourceMesh& sm, TargetMesh& tm,
|
|||
*f2f++=std::make_pair(sm_f, tm_f);
|
||||
|
||||
sm_halfedge_descriptor sm_h_i=halfedge(sm_f, sm);
|
||||
tm_halfedge_descriptor tm_h_prev = get(hmap, prev(sm_h_i, sm));
|
||||
tm_halfedge_descriptor tm_h_prev = get(hs_to_ht, prev(sm_h_i, sm));
|
||||
set_halfedge(tm_f, tm_h_prev, tm);
|
||||
|
||||
CGAL_precondition(*halfedges_around_face(sm_h_i, sm).first == sm_h_i);
|
||||
for(sm_halfedge_descriptor sm_h : halfedges_around_face(sm_h_i, sm))
|
||||
{
|
||||
tm_halfedge_descriptor tm_h = get(hmap, sm_h);
|
||||
tm_halfedge_descriptor tm_h = get(hs_to_ht, sm_h);
|
||||
set_next(tm_h_prev, tm_h, tm);
|
||||
set_face(tm_h, tm_f, tm);
|
||||
tm_h_prev=tm_h;
|
||||
|
|
@ -150,16 +156,14 @@ void copy_face_graph_impl(const SourceMesh& sm, TargetMesh& tm,
|
|||
halfedges_around_face(next(sm_border_halfedges[i], sm), sm))
|
||||
{
|
||||
CGAL_assertion(next(tm_h_prev, tm) == tm_h_prev);
|
||||
tm_h = get(hmap, sm_h);
|
||||
tm_h = get(hs_to_ht, sm_h);
|
||||
set_next(tm_h_prev, tm_h, tm);
|
||||
tm_h_prev=tm_h;
|
||||
}
|
||||
}
|
||||
// update halfedge vertex of all but the vertex halfedge
|
||||
for(tm_vertex_iterator vit = vertices(tm).first;
|
||||
vit != vertices(tm).second; ++vit)
|
||||
for(tm_vertex_descriptor v : vertices(tm))
|
||||
{
|
||||
tm_vertex_descriptor v = *vit;
|
||||
tm_halfedge_descriptor h = halfedge(v, tm);
|
||||
tm_halfedge_descriptor next_around_vertex=h;
|
||||
do{
|
||||
|
|
@ -167,47 +171,36 @@ void copy_face_graph_impl(const SourceMesh& sm, TargetMesh& tm,
|
|||
set_target(next_around_vertex, v, tm);
|
||||
}while(h != next_around_vertex);
|
||||
}
|
||||
|
||||
// detect if there are some non-manifold umbrellas and fix missing halfedge target pointers
|
||||
for (tm_halfedge_iterator it=halfedges(tm).first; it!=halfedges(tm).second; ++it)
|
||||
{
|
||||
if (target(*it, tm) == tm_null_vertex)
|
||||
{
|
||||
// create and fill a map from target halfedge to source halfedge
|
||||
typedef CGAL::dynamic_halfedge_property_t<sm_halfedge_descriptor> Dyn_th_tag;
|
||||
typename boost::property_map<TargetMesh, Dyn_th_tag >::type ht_to_hs = get(Dyn_th_tag(), tm);
|
||||
for (sm_halfedge_descriptor hs : halfedges(sm))
|
||||
put(ht_to_hs, get(hs_to_ht, hs), hs);
|
||||
|
||||
for(; it!=halfedges(tm).second; ++it)
|
||||
{
|
||||
if (target(*it, tm) == tm_null_vertex)
|
||||
{
|
||||
// we recover tm_v using the halfedge associated to the target vertex of
|
||||
// the halfedge in sm corresponding to *it. This is working because we
|
||||
// set the vertex halfedge pointer to the "same" halfedges.
|
||||
tm_vertex_descriptor tm_v =
|
||||
target( get(hs_to_ht, halfedge(target(get(ht_to_hs, *it), sm), sm)), tm);
|
||||
for(tm_halfedge_descriptor ht : halfedges_around_target(*it, tm))
|
||||
set_target(ht, tm_v, tm);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename SourceMesh, typename TargetMesh,
|
||||
typename V2V, typename H2H, typename F2F,
|
||||
typename Src_vpm, typename Tgt_vpm>
|
||||
void copy_face_graph(const SourceMesh& sm, TargetMesh& tm,
|
||||
Tag_false,
|
||||
V2V v2v, H2H h2h, F2F f2f,
|
||||
Src_vpm sm_vpm, Tgt_vpm tm_vpm )
|
||||
{
|
||||
typedef typename boost::graph_traits<SourceMesh>::halfedge_descriptor sm_halfedge_descriptor;
|
||||
typedef typename boost::graph_traits<TargetMesh>::halfedge_descriptor tm_halfedge_descriptor;
|
||||
|
||||
boost::unordered_map<sm_halfedge_descriptor,
|
||||
tm_halfedge_descriptor> hash_map(num_halfedges(sm));
|
||||
copy_face_graph_impl(sm, tm,
|
||||
boost::make_assoc_property_map(hash_map),
|
||||
v2v, h2h, f2f,
|
||||
sm_vpm, tm_vpm);
|
||||
}
|
||||
|
||||
template <typename SourceMesh, typename TargetMesh,
|
||||
typename V2V, typename H2H, typename F2F,
|
||||
typename Src_vpm, typename Tgt_vpm>
|
||||
void copy_face_graph(const SourceMesh& sm, TargetMesh& tm,
|
||||
Tag_true,
|
||||
V2V v2v, H2H h2h, F2F f2f,
|
||||
Src_vpm sm_vpm, Tgt_vpm tm_vpm )
|
||||
{
|
||||
typedef typename boost::graph_traits<TargetMesh>::halfedge_descriptor tm_halfedge_descriptor;
|
||||
std::vector<tm_halfedge_descriptor> hedges(num_halfedges(sm));
|
||||
|
||||
copy_face_graph_impl(sm, tm,
|
||||
bind_property_maps(get_initialized_halfedge_index_map(sm),
|
||||
make_property_map(hedges)),
|
||||
v2v, h2h, f2f,
|
||||
sm_vpm, tm_vpm);
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // end of namespace internal
|
||||
namespace impl
|
||||
{
|
||||
|
|
@ -329,8 +322,7 @@ void copy_face_graph(const SourceMesh& sm, TargetMesh& tm,
|
|||
using parameters::choose_parameter;
|
||||
using parameters::get_parameter;
|
||||
|
||||
internal::copy_face_graph(sm, tm,
|
||||
CGAL::graph_has_property<SourceMesh,boost::halfedge_index_t>(),
|
||||
internal::copy_face_graph_impl(sm, tm,
|
||||
choose_parameter(get_parameter(np1, internal_np::vertex_to_vertex_output_iterator),
|
||||
impl::make_functor(get_parameter(np1, internal_np::vertex_to_vertex_map))),
|
||||
choose_parameter(get_parameter(np1, internal_np::halfedge_to_halfedge_output_iterator),
|
||||
|
|
@ -357,7 +349,7 @@ void copy_face_graph(const SourceMesh& sm, TargetMesh& tm,
|
|||
copy_face_graph(sm, tm, np, parameters::all_default());
|
||||
}
|
||||
|
||||
#if !defined(DOXYGEN_RUNNING)
|
||||
#if !defined(DOXYGEN_RUNNING) && !defined(CGAL_NO_DEPRECATED_CODE)
|
||||
template <typename SourceMesh, typename TargetMesh,
|
||||
typename V2V, typename H2H, typename F2F,
|
||||
typename Src_vpm, typename Tgt_vpm>
|
||||
|
|
@ -365,10 +357,9 @@ void copy_face_graph(const SourceMesh& sm, TargetMesh& tm,
|
|||
V2V v2v, H2H h2h, F2F f2f,
|
||||
Src_vpm sm_vpm, Tgt_vpm tm_vpm )
|
||||
{
|
||||
internal::copy_face_graph(sm, tm,
|
||||
CGAL::graph_has_property<SourceMesh,boost::halfedge_index_t>(),
|
||||
v2v, h2h, f2f,
|
||||
sm_vpm, tm_vpm);
|
||||
internal::copy_face_graph_impl(sm, tm,
|
||||
v2v, h2h, f2f,
|
||||
sm_vpm, tm_vpm);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@ void concept_check_dual() {
|
|||
Graph, face_descriptor, boost::face_index_t> >();
|
||||
|
||||
// edge properties should be forwarded
|
||||
boost::function_requires< boost::concepts::ReadablePropertyGraph<
|
||||
Graph, edge_descriptor, boost::edge_weight_t> >();
|
||||
//boost::function_requires< boost::concepts::ReadablePropertyGraph<
|
||||
// Graph, edge_descriptor, boost::edge_weight_t> >();
|
||||
|
||||
// boost::function_requires< boost::concepts::PropertyGraph<
|
||||
// Graph, halfedge_descriptor, CGAL::halfedge_index_t> >();
|
||||
|
|
|
|||
|
|
@ -2,8 +2,38 @@
|
|||
#include "test_Prefix.h"
|
||||
#include <boost/range/distance.hpp>
|
||||
#include <CGAL/boost/graph/Euler_operations.h>
|
||||
|
||||
#include <CGAL/IO/OFF_reader.h>
|
||||
#include <CGAL/Polygon_mesh_processing/border.h>
|
||||
#include <CGAL/boost/graph/copy_face_graph.h>
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
test_copy_face_graph_nm_umbrella()
|
||||
{
|
||||
CGAL_GRAPH_TRAITS_MEMBERS(T);
|
||||
|
||||
T g;
|
||||
Kernel::Point_3 p;
|
||||
|
||||
CGAL::make_tetrahedron(p, p, p, p, g);
|
||||
CGAL::make_tetrahedron(p, p, p, p, g);
|
||||
|
||||
std::vector<vertex_descriptor> verts(vertices(g).begin(), vertices(g).end());
|
||||
|
||||
//merge verts[0] and verts[4]
|
||||
for (halfedge_descriptor h : CGAL::halfedges_around_target(verts[4], g))
|
||||
set_target(h, verts[0], g);
|
||||
remove_vertex(verts[4], g);
|
||||
|
||||
T g_copy;
|
||||
CGAL::copy_face_graph(g, g_copy);
|
||||
|
||||
for (halfedge_descriptor h : halfedges(g_copy))
|
||||
{
|
||||
assert( target(h, g_copy) != Traits::null_vertex() );
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
|
|
@ -588,6 +618,7 @@ template <typename Graph>
|
|||
void
|
||||
test_Euler_operations()
|
||||
{
|
||||
test_copy_face_graph_nm_umbrella<Graph>();
|
||||
join_face_test<Graph>();
|
||||
add_vertex_and_face_to_border_test<Graph>();
|
||||
add_face_to_border_test<Graph>();
|
||||
|
|
|
|||
|
|
@ -14,34 +14,34 @@ to do whatever they want with the software. Using the software means to accept
|
|||
the license, which has the status of a contract between the user and the owner
|
||||
of the \cgal software.
|
||||
|
||||
\section licensesGPL GPL
|
||||
\section licensesGPL GNU GPL
|
||||
|
||||
The \sc{Gpl} is an Open Source license that, if you distribute your software
|
||||
based on \sc{Gpl}ed \cgal data structures, obliges you to distribute the
|
||||
source code of your software under the \sc{Gpl}.
|
||||
The GNU GPL is an Open Source license that, if you distribute your software
|
||||
based on GPLed \cgal data structures, obliges you to distribute the
|
||||
source code of your software under the GPL.
|
||||
|
||||
The exact license terms can be found at the Free Software Foundation
|
||||
web site: http://www.gnu.org/copyleft/gpl.html.
|
||||
|
||||
\section licensesLGPL LGPL
|
||||
\section licensesLGPL GNU LGPL
|
||||
|
||||
The \sc{Lgpl} is an Open Source license that obliges you to distribute
|
||||
The GNU LGPL is an Open Source license that obliges you to distribute
|
||||
modifications you make on \cgal software accessible to the users.
|
||||
In contrast to the \sc{Gpl}, there is no obligation to make the source
|
||||
code of software you build on top of \sc{Lgpl}ed \cgal data structures.
|
||||
In contrast to the GPL, there is no obligation to make the source
|
||||
code of software you build on top of LGPLed \cgal data structures.
|
||||
|
||||
The exact license terms can be found at the Free Software Foundation web site:
|
||||
http://www.gnu.org/copyleft/lesser.html.
|
||||
|
||||
\section licensesRationale Rationale of the License Choice
|
||||
|
||||
We have chosen the \sc{Gpl} and the \sc{Lgpl} as they are well-known
|
||||
We have chosen the GPL and the LGPL as they are well-known
|
||||
and well-understood open source licenses. The former restricts
|
||||
commercial use, and the latter allows to promote software as de facto standard
|
||||
so that people can build new higher level data structures on top.
|
||||
|
||||
Therefore, the packages forming a foundation layer are distributed under
|
||||
the \sc{Lgpl}, and the higher level packages under the \sc{Gpl}.
|
||||
the LGPL, and the higher level packages under the GPL.
|
||||
The package overview states for each package under which license it is distributed.
|
||||
|
||||
\section licensesCommercial Commercial Licenses
|
||||
|
|
@ -57,7 +57,7 @@ as well as in evolutive maintenance.
|
|||
Users who have a commercial license for specific packages can check that
|
||||
they do not accidentally use packages for which they do not have a commercial
|
||||
license. The same holds for users who want to be sure that they only
|
||||
use packages of \cgal released under the \sc{Lgpl}.
|
||||
use packages of \cgal released under the LGPL.
|
||||
|
||||
To enable checking, users have to define one of the following macros:
|
||||
|
||||
|
|
|
|||
|
|
@ -355,7 +355,7 @@ removes some unneeded files, and performs minor repair on some glitches.''')
|
|||
class_and_struct_files=list(package_glob('./*/class*.html'))
|
||||
class_and_struct_files.extend(package_glob('./*/struct*.html'))
|
||||
for fn in class_and_struct_files:
|
||||
re_replace_first_in_file(r'<p>Inherits\s*(.*)</p>', r'<a name="details" id="details"></a><h2 class="groupheader">Inherits from</h2><p>\1</p>', fn)
|
||||
re_replace_first_in_file(r'<p>Inherits\s*(.*)</p>', r'<h2 class="groupheader">Inherits from</h2><p>\1</p>', fn)
|
||||
|
||||
# remove class name in Definition section if there is no default template
|
||||
# parameter documented
|
||||
|
|
|
|||
|
|
@ -475,25 +475,19 @@ void reserve(HalfedgeDS_default<T,I,A>& p,
|
|||
}// namespace CGAL
|
||||
namespace boost {
|
||||
|
||||
// property_map dispatcher into Polyhedron
|
||||
template<class T, class I, class A, class Tag>
|
||||
struct property_map<CGAL::HalfedgeDS_default<T,I,A>, Tag>
|
||||
{
|
||||
typedef typename CGAL::HDS_property_map<Tag>::
|
||||
template bind_<T,I,A> map_gen;
|
||||
typedef typename map_gen::type type;
|
||||
typedef typename map_gen::const_type const_type;
|
||||
#define CGAL_PM_SPECIALIZATION(TAG) \
|
||||
template<class T, class I, class A> \
|
||||
struct property_map<CGAL::HalfedgeDS_default<T,I,A>, TAG> \
|
||||
{\
|
||||
typedef typename CGAL::HDS_property_map<TAG>:: \
|
||||
template bind_<T,I,A> map_gen; \
|
||||
typedef typename map_gen::type type; \
|
||||
typedef typename map_gen::const_type const_type; \
|
||||
};
|
||||
|
||||
// property_map dispatcher into const Polyhedron
|
||||
template<class T, class I, class A, class Tag>
|
||||
struct property_map<const CGAL::HalfedgeDS_default<T,I,A>, Tag>
|
||||
{
|
||||
typedef typename CGAL::HDS_property_map<Tag>::
|
||||
template bind_<T,I,A> map_gen;
|
||||
typedef typename map_gen::type type;
|
||||
typedef typename map_gen::const_type const_type;
|
||||
};
|
||||
CGAL_PM_SPECIALIZATION(vertex_point_t)
|
||||
|
||||
#undef CGAL_PM_SPECIALIZATION
|
||||
|
||||
} // namespace boost
|
||||
|
||||
|
|
|
|||
|
|
@ -3333,8 +3333,7 @@ namespace CommonKernelFunctors {
|
|||
CGAL_kernel_assertion_msg(bool(optional) == true,
|
||||
"the segment does not intersect the supporting"
|
||||
" plane");
|
||||
using boost::get;
|
||||
const Point_3* p = get<Point_3>(&*optional);
|
||||
const Point_3* p = boost::get<Point_3>(&*optional);
|
||||
CGAL_kernel_assertion_msg(p != 0,
|
||||
"the segment intersection with the plane is "
|
||||
"not a point");
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include <CGAL/boost/graph/properties.h>
|
||||
#include <CGAL/Linear_cell_complex_for_combinatorial_map.h>
|
||||
#include <CGAL/Unique_hash_map.h>
|
||||
#include <CGAL/Dynamic_property_map.h>
|
||||
|
||||
|
||||
#define CGAL_LCC_ARGS unsigned int d_, unsigned int ambient_dim, \
|
||||
|
|
@ -402,6 +403,76 @@ struct property_map<const CGAL_LCC_TYPE, Tag>
|
|||
|
||||
} // namespace boost
|
||||
|
||||
// dynamic property map ambiguity resolution
|
||||
#define CGAL_LCC_DYNAMIC_PMAP_SPEC(TAG, DESC) \
|
||||
namespace boost { \
|
||||
template<unsigned int d_, unsigned int ambient_dim, \
|
||||
class Traits_, \
|
||||
class Items_, \
|
||||
class Alloc_, \
|
||||
template<unsigned int,class,class,class,class> \
|
||||
class CMap, \
|
||||
class Storage_,\
|
||||
class T>\
|
||||
struct property_map< \
|
||||
CGAL::Linear_cell_complex_for_combinatorial_map<d_, ambient_dim, Traits_, Items_, Alloc_, CMap , Storage_>,\
|
||||
TAG<T> > \
|
||||
{\
|
||||
typedef CGAL::Linear_cell_complex_for_combinatorial_map\
|
||||
<d_, ambient_dim, Traits_, Items_, Alloc_, CMap , Storage_> LCC;\
|
||||
typedef typename boost::graph_traits<LCC>::DESC DESC;\
|
||||
typedef CGAL::internal::Dynamic_property_map<DESC,T> type;\
|
||||
typedef type const_type;\
|
||||
};\
|
||||
} \
|
||||
\
|
||||
namespace CGAL { \
|
||||
template <unsigned int d_, unsigned int ambient_dim, \
|
||||
class Traits_, \
|
||||
class Items_, \
|
||||
class Alloc_, \
|
||||
template<unsigned int,class,class,class,class> \
|
||||
class CMap, \
|
||||
class Storage_,\
|
||||
class T> \
|
||||
typename boost::property_map< \
|
||||
Linear_cell_complex_for_combinatorial_map<d_, ambient_dim, Traits_, Items_, Alloc_, CMap , Storage_>, \
|
||||
TAG<T> >::const_type \
|
||||
get(TAG<T>, const Linear_cell_complex_for_combinatorial_map<d_, ambient_dim, Traits_, Items_, Alloc_, CMap , Storage_>&) \
|
||||
{ \
|
||||
typedef Linear_cell_complex_for_combinatorial_map<d_, ambient_dim, Traits_, Items_, Alloc_, CMap , Storage_> LCC;\
|
||||
typedef typename boost::graph_traits<LCC>::DESC DESC; \
|
||||
return internal::Dynamic_property_map<DESC,T>();\
|
||||
} \
|
||||
\
|
||||
template <unsigned int d_, unsigned int ambient_dim, \
|
||||
class Traits_, \
|
||||
class Items_, \
|
||||
class Alloc_, \
|
||||
template<unsigned int,class,class,class,class> \
|
||||
class CMap, \
|
||||
class Storage_,\
|
||||
class T> \
|
||||
typename boost::property_map< \
|
||||
Linear_cell_complex_for_combinatorial_map<d_, ambient_dim, Traits_, Items_, Alloc_, CMap , Storage_>, \
|
||||
TAG<T> >::type \
|
||||
get(TAG<T>, Linear_cell_complex_for_combinatorial_map<d_, ambient_dim, Traits_, Items_, Alloc_, CMap , Storage_>&) \
|
||||
{ \
|
||||
typedef Linear_cell_complex_for_combinatorial_map<d_, ambient_dim, Traits_, Items_, Alloc_, CMap , Storage_> LCC;\
|
||||
typedef typename boost::graph_traits<LCC>::DESC DESC; \
|
||||
return internal::Dynamic_property_map<DESC,T>();\
|
||||
} \
|
||||
}
|
||||
|
||||
CGAL_LCC_DYNAMIC_PMAP_SPEC(CGAL::dynamic_vertex_property_t, vertex_descriptor)
|
||||
CGAL_LCC_DYNAMIC_PMAP_SPEC(CGAL::dynamic_halfedge_property_t, halfedge_descriptor)
|
||||
CGAL_LCC_DYNAMIC_PMAP_SPEC(CGAL::dynamic_edge_property_t, edge_descriptor)
|
||||
CGAL_LCC_DYNAMIC_PMAP_SPEC(CGAL::dynamic_face_property_t, face_descriptor)
|
||||
|
||||
#undef CGAL_LCC_DYNAMIC_PMAP_SPEC
|
||||
|
||||
|
||||
#undef CGAL_NAME_LCC_ARGS
|
||||
#undef CGAL_LCC_ARGS
|
||||
#undef CGAL_LCC_TYPE
|
||||
|
||||
|
|
|
|||
|
|
@ -184,9 +184,10 @@ void extrude_mesh(const InputMesh& input,
|
|||
|
||||
std::vector<std::pair<input_vertex_descriptor, output_vertex_descriptor> > bottom_v2v;
|
||||
std::vector<std::pair<input_halfedge_descriptor, output_halfedge_descriptor> > bottom_h2h;
|
||||
copy_face_graph(input, output, std::back_inserter(bottom_v2v),
|
||||
std::back_inserter(bottom_h2h), Emptyset_iterator(),
|
||||
input_vpm, output_vpm);
|
||||
copy_face_graph(input, output, parameters::vertex_point_map(input_vpm)
|
||||
.vertex_to_vertex_output_iterator(std::back_inserter(bottom_v2v))
|
||||
.halfedge_to_halfedge_output_iterator(std::back_inserter(bottom_h2h)),
|
||||
parameters::vertex_point_map(output_vpm));
|
||||
|
||||
// create the offset for the other side
|
||||
for(std::size_t i = 0; i< bottom_v2v.size(); ++i)
|
||||
|
|
@ -198,9 +199,11 @@ void extrude_mesh(const InputMesh& input,
|
|||
// collect border halfedges for the creation of the triangle strip
|
||||
std::vector<std::pair<input_vertex_descriptor, output_vertex_descriptor> > top_v2v;
|
||||
std::vector<std::pair<input_halfedge_descriptor, output_halfedge_descriptor> > top_h2h;
|
||||
copy_face_graph(input, output, std::inserter(top_v2v, top_v2v.end()),
|
||||
std::inserter(top_h2h, top_h2h.end()), Emptyset_iterator(),
|
||||
input_vpm, output_vpm);
|
||||
copy_face_graph(input, output, parameters::vertex_point_map(input_vpm)
|
||||
.vertex_to_vertex_output_iterator(std::inserter(top_v2v, top_v2v.end()))
|
||||
.halfedge_to_halfedge_output_iterator(std::inserter(top_h2h, top_h2h.end())),
|
||||
parameters::vertex_point_map(output_vpm));
|
||||
|
||||
for(std::size_t i = 0; i< top_v2v.size(); ++i)
|
||||
{
|
||||
top(top_v2v[i].first, top_v2v[i].second);
|
||||
|
|
|
|||
|
|
@ -230,10 +230,8 @@ test_dual_with_various_faces()
|
|||
// copy dual to a sm
|
||||
Surface_mesh sm_dual;
|
||||
CGAL::copy_face_graph(dual, sm_dual,
|
||||
CGAL::Emptyset_iterator(),
|
||||
CGAL::Emptyset_iterator(),
|
||||
CGAL::Emptyset_iterator(),
|
||||
Dual_vpm<Surface_mesh, Point, Pmap>(mesh, vpmap));
|
||||
CGAL::parameters::vertex_point_map(
|
||||
Dual_vpm<Surface_mesh, Point, Pmap>(mesh, vpmap)));
|
||||
|
||||
for(typename boost::graph_traits<Surface_mesh>::face_descriptor fit : faces(sm_dual))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1574,7 +1574,7 @@ void MainWindow::showSceneContextMenu(const QPoint& p) {
|
|||
}
|
||||
else if(action->text() == QString("Line Width"))
|
||||
{
|
||||
menu_actions["line width"] = action->menu()->actions().last();
|
||||
menu_actions["Line width"] = action->menu()->actions().last();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1719,7 +1719,7 @@ void MainWindow::showSceneContextMenu(const QPoint& p) {
|
|||
slider->setValue(
|
||||
qobject_cast<QSlider*>(
|
||||
qobject_cast<QWidgetAction*>
|
||||
(menu_actions["line width"])->defaultWidget()
|
||||
(menu_actions["Line width"])->defaultWidget()
|
||||
)->value());
|
||||
slider->setOrientation(Qt::Horizontal);
|
||||
sliderAction->setDefaultWidget(slider);
|
||||
|
|
|
|||
|
|
@ -27,12 +27,7 @@
|
|||
#include <CGAL/Segment_Delaunay_graph_2/Filtered_traits_concept_check_tags.h>
|
||||
|
||||
// includes for the default parameters of the filtered traits
|
||||
#ifdef CGAL_USE_GMP
|
||||
#include <CGAL/Gmpq.h>
|
||||
#else
|
||||
#include <CGAL/Quotient.h>
|
||||
#include <CGAL/MP_Float.h>
|
||||
#endif
|
||||
#include <CGAL/internal/Exact_type_selector.h>
|
||||
|
||||
#include <CGAL/Simple_cartesian.h>
|
||||
#include <CGAL/Interval_arithmetic.h>
|
||||
|
|
@ -56,11 +51,7 @@ namespace CGAL {
|
|||
// this traits class does support intersecting segments
|
||||
template<class CK,
|
||||
class CK_MTag = Field_with_sqrt_tag,
|
||||
#ifdef CGAL_USE_GMP
|
||||
class EK = Simple_cartesian< Gmpq >,
|
||||
#else
|
||||
class EK = Simple_cartesian< Quotient<MP_Float> >,
|
||||
#endif
|
||||
class EK = Simple_cartesian< internal::Exact_field_selector<double>::Type >,
|
||||
class EK_MTag = Field_tag,
|
||||
class FK = Simple_cartesian< Interval_nt<false> >,
|
||||
class FK_MTag = Field_with_sqrt_tag,
|
||||
|
|
|
|||
|
|
@ -24,12 +24,8 @@
|
|||
#include <CGAL/Segment_Delaunay_graph_2/Filtered_traits_concept_check_tags.h>
|
||||
|
||||
// includes for the default parameters of the filtered traits
|
||||
#ifdef CGAL_USE_GMP
|
||||
#include <CGAL/Gmpq.h>
|
||||
#else
|
||||
#include <CGAL/Quotient.h>
|
||||
#include <CGAL/MP_Float.h>
|
||||
#endif
|
||||
#include <CGAL/internal/Exact_type_selector.h>
|
||||
|
||||
|
||||
#include <CGAL/Simple_cartesian.h>
|
||||
#include <CGAL/Interval_arithmetic.h>
|
||||
|
|
@ -53,11 +49,7 @@ namespace CGAL {
|
|||
// this traits class does support intersecting segments
|
||||
template<class CK,
|
||||
class CK_MTag = Field_with_sqrt_tag,
|
||||
#ifdef CGAL_USE_GMP
|
||||
class EK = Simple_cartesian< Gmpq >,
|
||||
#else
|
||||
class EK = Simple_cartesian< Quotient<MP_Float> >,
|
||||
#endif
|
||||
class EK = Simple_cartesian< internal::Exact_field_selector<double>::Type >,
|
||||
class EK_MTag = Field_tag,
|
||||
class FK = Simple_cartesian< Interval_nt<false> >,
|
||||
class FK_MTag = Field_with_sqrt_tag,
|
||||
|
|
|
|||
|
|
@ -235,23 +235,31 @@ struct T2_property_map<Tr, boost::face_index_t>
|
|||
// overloads and specializations in the boost namespace
|
||||
namespace boost {
|
||||
|
||||
// g++ 'enumeral_type' in template unification not implemented workaround
|
||||
template <CGAL_2D_TRIANGULATION_TEMPLATE_PARAMETERS, class Tag>
|
||||
struct property_map<CGAL_2D_TRIANGULATION, Tag>
|
||||
{
|
||||
typedef typename CGAL::internal::T2_property_map<CGAL_2D_TRIANGULATION, Tag> map_gen;
|
||||
typedef typename map_gen::type type;
|
||||
typedef typename map_gen::const_type const_type;
|
||||
#define CGAL_PM_SPECIALIZATION(TAG) \
|
||||
template <CGAL_2D_TRIANGULATION_TEMPLATE_PARAMETERS> \
|
||||
struct property_map<CGAL_2D_TRIANGULATION, TAG> \
|
||||
{ \
|
||||
typedef typename CGAL::internal::T2_property_map<CGAL_2D_TRIANGULATION, TAG> map_gen; \
|
||||
typedef typename map_gen::type type; \
|
||||
typedef typename map_gen::const_type const_type; \
|
||||
}; \
|
||||
\
|
||||
template <CGAL_2D_TRIANGULATION_TEMPLATE_PARAMETERS> \
|
||||
struct property_map<const CGAL_2D_TRIANGULATION, TAG> \
|
||||
{ \
|
||||
typedef typename CGAL::internal::T2_property_map<CGAL_2D_TRIANGULATION, TAG> map_gen; \
|
||||
typedef typename map_gen::type type; \
|
||||
typedef typename map_gen::const_type const_type; \
|
||||
};
|
||||
|
||||
// see struct property_map in Polyehdron for an explanation
|
||||
template <CGAL_2D_TRIANGULATION_TEMPLATE_PARAMETERS, class Tag>
|
||||
struct property_map<const CGAL_2D_TRIANGULATION, Tag>
|
||||
{
|
||||
typedef typename CGAL::internal::T2_property_map<CGAL_2D_TRIANGULATION, Tag> map_gen;
|
||||
typedef typename map_gen::type type;
|
||||
typedef typename map_gen::const_type const_type;
|
||||
};
|
||||
CGAL_PM_SPECIALIZATION(vertex_point_t)
|
||||
CGAL_PM_SPECIALIZATION(edge_weight_t)
|
||||
CGAL_PM_SPECIALIZATION(vertex_index_t)
|
||||
CGAL_PM_SPECIALIZATION(halfedge_index_t)
|
||||
CGAL_PM_SPECIALIZATION(edge_index_t)
|
||||
CGAL_PM_SPECIALIZATION(face_index_t)
|
||||
|
||||
#undef CGAL_PM_SPECIALIZATION
|
||||
|
||||
} // end namespace boost
|
||||
|
||||
|
|
|
|||
|
|
@ -284,23 +284,31 @@ get(boost::face_index_t, const Triangulation_data_structure_2<VB,FB>&)
|
|||
|
||||
namespace boost {
|
||||
|
||||
// g++ 'enumeral_type' in template unification not implemented workaround
|
||||
template <class VB, class FB, class Tag>
|
||||
struct property_map<CGAL::Triangulation_data_structure_2<VB,FB>, Tag>
|
||||
{
|
||||
typedef typename CGAL::internal::TDS2_property_map<VB, FB, Tag> map_gen;
|
||||
typedef typename map_gen::type type;
|
||||
typedef typename map_gen::const_type const_type;
|
||||
#define CGAL_PM_SPECIALIZATION(TAG) \
|
||||
template <class VB, class FB> \
|
||||
struct property_map<CGAL::Triangulation_data_structure_2<VB,FB>, TAG> \
|
||||
{ \
|
||||
typedef typename CGAL::internal::TDS2_property_map<VB, FB, TAG> map_gen; \
|
||||
typedef typename map_gen::type type; \
|
||||
typedef typename map_gen::const_type const_type; \
|
||||
}; \
|
||||
\
|
||||
template <class VB, class FB> \
|
||||
struct property_map<const CGAL::Triangulation_data_structure_2<VB,FB>, TAG> \
|
||||
{ \
|
||||
typedef typename CGAL::internal::TDS2_property_map<VB, FB, TAG> map_gen; \
|
||||
typedef typename map_gen::type type; \
|
||||
typedef typename map_gen::const_type const_type; \
|
||||
};
|
||||
|
||||
// see struct property_map in Polyehdron for an explanation
|
||||
template <class VB, class FB, class Tag>
|
||||
struct property_map<const CGAL::Triangulation_data_structure_2<VB,FB>, Tag>
|
||||
{
|
||||
typedef typename CGAL::internal::TDS2_property_map<VB, FB, Tag> map_gen;
|
||||
typedef typename map_gen::type type;
|
||||
typedef typename map_gen::const_type const_type;
|
||||
};
|
||||
CGAL_PM_SPECIALIZATION(vertex_point_t)
|
||||
CGAL_PM_SPECIALIZATION(edge_weight_t)
|
||||
CGAL_PM_SPECIALIZATION(vertex_index_t)
|
||||
CGAL_PM_SPECIALIZATION(halfedge_index_t)
|
||||
CGAL_PM_SPECIALIZATION(edge_index_t)
|
||||
CGAL_PM_SPECIALIZATION(face_index_t)
|
||||
|
||||
#undef CGAL_PM_SPECIALIZATION
|
||||
|
||||
} // namespace boost
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue