mirror of https://github.com/CGAL/cgal
Merge pull request #3636 from sgiraudot/Surface_mesh-PLY_IO-GF
[Small Feature] Surface Mesh PLY IO
This commit is contained in:
commit
008a75d439
|
|
@ -35,87 +35,12 @@
|
|||
#include <CGAL/IO/read_las_points.h>
|
||||
#include <CGAL/IO/write_las_points.h>
|
||||
#endif // LAS
|
||||
#include <CGAL/IO/read_ply_points.h>
|
||||
#include <CGAL/IO/write_ply_points.h>
|
||||
#include <CGAL/IO/PLY.h>
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
namespace internal
|
||||
{
|
||||
|
||||
template <typename Point, typename Vector>
|
||||
class Abstract_property_printer
|
||||
{
|
||||
public:
|
||||
virtual ~Abstract_property_printer() { }
|
||||
virtual void print (std::ostream& stream, const typename CGAL::Point_set_3<Point,Vector>::Index& index) = 0;
|
||||
};
|
||||
|
||||
template <typename Point, typename Vector, typename Type>
|
||||
class Property_printer : public Abstract_property_printer<Point, Vector>
|
||||
{
|
||||
typedef typename CGAL::Point_set_3<Point, Vector> Point_set;
|
||||
typedef typename Point_set::template Property_map<Type> Pmap;
|
||||
Pmap m_pmap;
|
||||
public:
|
||||
Property_printer (const Pmap& pmap) : m_pmap (pmap)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual void print(std::ostream& stream, const typename CGAL::Point_set_3<Point,Vector>::Index& index)
|
||||
{
|
||||
stream << get(m_pmap, index);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Point, typename Vector, typename Type>
|
||||
class Simple_property_printer : public Abstract_property_printer<Point, Vector>
|
||||
{
|
||||
typedef typename CGAL::Point_set_3<Point, Vector> Point_set;
|
||||
typedef typename Point_set::template Property_map<Type> Pmap;
|
||||
Pmap m_pmap;
|
||||
public:
|
||||
Simple_property_printer (const Pmap& pmap) : m_pmap (pmap)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual void print(std::ostream& stream, const typename CGAL::Point_set_3<Point,Vector>::Index& index)
|
||||
{
|
||||
if (get_mode(stream) == IO::ASCII)
|
||||
stream << get(m_pmap, index);
|
||||
else
|
||||
{
|
||||
Type t = get (m_pmap, index);
|
||||
stream.write (reinterpret_cast<char*>(&t), sizeof(t));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Point, typename Vector, typename Type>
|
||||
class Char_property_printer : public Abstract_property_printer<Point, Vector>
|
||||
{
|
||||
typedef typename CGAL::Point_set_3<Point, Vector> Point_set;
|
||||
typedef typename Point_set::template Property_map<Type> Pmap;
|
||||
Pmap m_pmap;
|
||||
public:
|
||||
Char_property_printer (const Pmap& pmap) : m_pmap (pmap)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual void print(std::ostream& stream, const typename CGAL::Point_set_3<Point,Vector>::Index& index)
|
||||
{
|
||||
if (get_mode(stream) == IO::ASCII)
|
||||
stream << int(get(m_pmap, index));
|
||||
else
|
||||
{
|
||||
Type t = get (m_pmap, index);
|
||||
stream.write (reinterpret_cast<char*>(&t), sizeof(t));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
namespace PLY
|
||||
{
|
||||
|
|
@ -365,19 +290,42 @@ read_off_point_set(
|
|||
}
|
||||
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
template <typename Point, typename Vector>
|
||||
bool
|
||||
read_ply_point_set(
|
||||
std::istream& stream, ///< input stream.
|
||||
CGAL::Point_set_3<Point, Vector>& point_set) ///< point set
|
||||
{
|
||||
std::string dummy;
|
||||
return read_ply_point_set (stream, point_set, dummy);
|
||||
}
|
||||
|
||||
/// \endcond
|
||||
|
||||
/*!
|
||||
\ingroup PkgPointSet3IO
|
||||
|
||||
Reads a point set with properties from an input stream in Ascii or
|
||||
Binary PLY format.
|
||||
|
||||
- the operator reads the vertex `point` property;
|
||||
- if three PLY properties `nx`, `ny` and `nz` with type `float`
|
||||
or `double` are found, the normal map is added;
|
||||
- if any other PLY property is found, a "[name]" property map is
|
||||
added, where `[name]` is the name of the PLY property.
|
||||
|
||||
The `comments` parameter can be omitted. If provided, it will be
|
||||
used to store the potential comments found in the PLY
|
||||
header. Each line starting by "comment " in the header is
|
||||
appended to the `comments` string (without the "comment " word).
|
||||
*/
|
||||
template <typename Point, typename Vector>
|
||||
bool
|
||||
read_ply_point_set(
|
||||
std::istream& stream, ///< input stream.
|
||||
#ifdef DOXYGEN_RUNNING
|
||||
CGAL::Point_set_3<Point, Vector>& point_set) ///< point set
|
||||
#else
|
||||
CGAL::Point_set_3<Point, Vector>& point_set, ///< point set
|
||||
std::string* comments = NULL) ///< recover PLY comments
|
||||
#endif
|
||||
std::string& comments) ///< PLY comments.
|
||||
{
|
||||
if(!stream)
|
||||
{
|
||||
|
|
@ -394,14 +342,14 @@ read_ply_point_set(
|
|||
return false;
|
||||
}
|
||||
|
||||
if (comments != NULL)
|
||||
*comments = reader.comments();
|
||||
comments = reader.comments();
|
||||
|
||||
for (std::size_t i = 0; i < reader.number_of_elements(); ++ i)
|
||||
{
|
||||
internal::PLY::PLY_element& element = reader.element(i);
|
||||
|
||||
if (element.name() == "vertex" || element.name() == "vertices")
|
||||
bool is_vertex = (element.name() == "vertex" || element.name() == "vertices");
|
||||
if (is_vertex)
|
||||
{
|
||||
point_set.reserve (element.number_of_items());
|
||||
filler.instantiate_properties (element);
|
||||
|
|
@ -417,7 +365,7 @@ read_ply_point_set(
|
|||
return false;
|
||||
}
|
||||
|
||||
if (element.name() == "vertex" || element.name() == "vertices")
|
||||
if (is_vertex)
|
||||
filler.process_line (element);
|
||||
}
|
||||
}
|
||||
|
|
@ -427,27 +375,46 @@ read_ply_point_set(
|
|||
|
||||
/*!
|
||||
\ingroup PkgPointSet3IO
|
||||
|
||||
Writes a point set with properties in an output stream in PLY
|
||||
format.
|
||||
|
||||
If found, the normal map is inserted to the stream. All other
|
||||
properties with simple types are inserted in the stream.
|
||||
|
||||
If provided, the `comments` string is included line by line in
|
||||
the header of the PLY stream (each line will be precedeed by
|
||||
"comment ").
|
||||
*/
|
||||
template <typename Point, typename Vector>
|
||||
bool
|
||||
write_ply_point_set(
|
||||
std::ostream& stream, ///< output stream.
|
||||
#ifdef DOXYGEN_RUNNING
|
||||
const CGAL::Point_set_3<Point, Vector>& point_set) ///< point set
|
||||
#else
|
||||
const CGAL::Point_set_3<Point, Vector>& point_set, ///< point set
|
||||
std::string* comments = NULL) ///< write PLY comments
|
||||
#endif
|
||||
const CGAL::Point_set_3<Point, Vector>& point_set, ///< point set.
|
||||
const std::string& comments = std::string()) ///< PLY comments.
|
||||
{
|
||||
typedef CGAL::Point_set_3<Point, Vector> Point_set;
|
||||
typedef typename Point_set::Index Index;
|
||||
typedef typename Point_set::Point_map Point_map;
|
||||
typedef typename Point_set::Vector_map Vector_map;
|
||||
typedef typename Point_set::template Property_map<boost::int8_t> Int8_map;
|
||||
typedef typename Point_set::template Property_map<boost::uint8_t> Uint8_map;
|
||||
typedef typename Point_set::template Property_map<boost::int16_t> Int16_map;
|
||||
typedef typename Point_set::template Property_map<boost::uint16_t> Uint16_map;
|
||||
typedef typename Point_set::template Property_map<boost::int32_t> Int32_map;
|
||||
typedef typename Point_set::template Property_map<boost::uint32_t> Uint32_map;
|
||||
typedef typename Point_set::template Property_map<boost::int64_t> Int64_map;
|
||||
typedef typename Point_set::template Property_map<boost::uint64_t> Uint64_map;
|
||||
typedef typename Point_set::template Property_map<float> Float_map;
|
||||
typedef typename Point_set::template Property_map<double> Double_map;
|
||||
|
||||
stream << "ply" << std::endl
|
||||
<< ((get_mode(stream) == IO::BINARY) ? "format binary_little_endian 1.0" : "format ascii 1.0") << std::endl
|
||||
<< "comment Generated by the CGAL library" << std::endl;
|
||||
|
||||
if (comments != NULL)
|
||||
if (comments != std::string())
|
||||
{
|
||||
std::istringstream iss (*comments);
|
||||
std::istringstream iss (comments);
|
||||
std::string line;
|
||||
while (getline(iss, line))
|
||||
{
|
||||
|
|
@ -459,7 +426,7 @@ write_ply_point_set(
|
|||
stream << "element vertex " << point_set.number_of_points() << std::endl;
|
||||
|
||||
std::vector<std::string> prop = point_set.base().properties();
|
||||
std::vector<internal::Abstract_property_printer<Point, Vector>*> printers;
|
||||
std::vector<internal::PLY::Abstract_property_printer<Index>*> printers;
|
||||
|
||||
for (std::size_t i = 0; i < prop.size(); ++ i)
|
||||
{
|
||||
|
|
@ -468,7 +435,7 @@ write_ply_point_set(
|
|||
|
||||
if (prop[i] == "point")
|
||||
{
|
||||
if (boost::is_same<typename GetFTFromMap<typename Point_set::Point_map>::type, float>::value)
|
||||
if (boost::is_same<typename Get_FT_from_map<typename Point_set::Point_map>::type, float>::value)
|
||||
{
|
||||
stream << "property float x" << std::endl
|
||||
<< "property float y" << std::endl
|
||||
|
|
@ -480,86 +447,125 @@ write_ply_point_set(
|
|||
<< "property double y" << std::endl
|
||||
<< "property double z" << std::endl;
|
||||
}
|
||||
printers.push_back (new internal::Property_printer<Point,Vector,Point>(point_set.point_map()));
|
||||
printers.push_back (new internal::PLY::Property_printer<Index,Point_map>(point_set.point_map()));
|
||||
continue;
|
||||
}
|
||||
if (prop[i] == "normal")
|
||||
{
|
||||
stream << "property double nx" << std::endl
|
||||
<< "property double ny" << std::endl
|
||||
<< "property double nz" << std::endl;
|
||||
printers.push_back (new internal::Property_printer<Point,Vector,Vector>(point_set.normal_map()));
|
||||
if (boost::is_same<typename Get_FT_from_map<typename Point_set::Vector_map>::type, float>::value)
|
||||
{
|
||||
stream << "property float nx" << std::endl
|
||||
<< "property float ny" << std::endl
|
||||
<< "property float nz" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
stream << "property double nx" << std::endl
|
||||
<< "property double ny" << std::endl
|
||||
<< "property double nz" << std::endl;
|
||||
}
|
||||
printers.push_back (new internal::PLY::Property_printer<Index,Vector_map>(point_set.normal_map()));
|
||||
continue;
|
||||
}
|
||||
|
||||
bool okay = false;
|
||||
{
|
||||
typename Point_set::template Property_map<boost::int8_t> pmap;
|
||||
Int8_map pmap;
|
||||
boost::tie (pmap, okay) = point_set.template property_map<boost::int8_t>(prop[i]);
|
||||
if (okay)
|
||||
{
|
||||
stream << "property char " << prop[i] << std::endl;
|
||||
printers.push_back (new internal::Char_property_printer<Point,Vector,boost::int8_t>(pmap));
|
||||
printers.push_back (new internal::PLY::Char_property_printer<Index,Int8_map>(pmap));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
{
|
||||
typename Point_set::template Property_map<boost::uint8_t> pmap;
|
||||
Uint8_map pmap;
|
||||
boost::tie (pmap, okay) = point_set.template property_map<boost::uint8_t>(prop[i]);
|
||||
if (okay)
|
||||
{
|
||||
stream << "property uchar " << prop[i] << std::endl;
|
||||
printers.push_back (new internal::Char_property_printer<Point,Vector,boost::uint8_t>(pmap));
|
||||
printers.push_back (new internal::PLY::Char_property_printer<Index,Uint8_map>(pmap));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
{
|
||||
typename Point_set::template Property_map<boost::int16_t> pmap;
|
||||
Int16_map pmap;
|
||||
boost::tie (pmap, okay) = point_set.template property_map<boost::int16_t>(prop[i]);
|
||||
if (okay)
|
||||
{
|
||||
stream << "property short " << prop[i] << std::endl;
|
||||
printers.push_back (new internal::Simple_property_printer<Point,Vector,boost::int16_t>(pmap));
|
||||
printers.push_back (new internal::PLY::Simple_property_printer<Index,Int16_map>(pmap));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
{
|
||||
typename Point_set::template Property_map<boost::uint16_t> pmap;
|
||||
Uint16_map pmap;
|
||||
boost::tie (pmap, okay) = point_set.template property_map<boost::uint16_t>(prop[i]);
|
||||
if (okay)
|
||||
{
|
||||
stream << "property ushort " << prop[i] << std::endl;
|
||||
printers.push_back (new internal::Simple_property_printer<Point,Vector,boost::uint16_t>(pmap));
|
||||
printers.push_back (new internal::PLY::Simple_property_printer<Index,Uint16_map>(pmap));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
{
|
||||
typename Point_set::template Property_map<boost::int32_t> pmap;
|
||||
Int32_map pmap;
|
||||
boost::tie (pmap, okay) = point_set.template property_map<boost::int32_t>(prop[i]);
|
||||
if (okay)
|
||||
{
|
||||
stream << "property int " << prop[i] << std::endl;
|
||||
printers.push_back (new internal::Simple_property_printer<Point,Vector,boost::int32_t>(pmap));
|
||||
printers.push_back (new internal::PLY::Simple_property_printer<Index,Int32_map>(pmap));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
{
|
||||
typename Point_set::template Property_map<float> pmap;
|
||||
Uint32_map pmap;
|
||||
boost::tie (pmap, okay) = point_set.template property_map<boost::uint32_t>(prop[i]);
|
||||
if (okay)
|
||||
{
|
||||
stream << "property uint " << prop[i] << std::endl;
|
||||
printers.push_back (new internal::PLY::Simple_property_printer<Index,Uint32_map>(pmap));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
{
|
||||
Int64_map pmap;
|
||||
boost::tie (pmap, okay) = point_set.template property_map<boost::int64_t>(prop[i]);
|
||||
if (okay)
|
||||
{
|
||||
stream << "property int " << prop[i] << std::endl;
|
||||
printers.push_back (new internal::PLY::Simple_property_printer<Index,Int64_map,boost::int32_t>(pmap));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
{
|
||||
Uint64_map pmap;
|
||||
boost::tie (pmap, okay) = point_set.template property_map<boost::uint64_t>(prop[i]);
|
||||
if (okay)
|
||||
{
|
||||
stream << "property uint " << prop[i] << std::endl;
|
||||
printers.push_back (new internal::PLY::Simple_property_printer<Index,Uint64_map,boost::uint32_t>(pmap));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
{
|
||||
Float_map pmap;
|
||||
boost::tie (pmap, okay) = point_set.template property_map<float>(prop[i]);
|
||||
if (okay)
|
||||
{
|
||||
stream << "property float " << prop[i] << std::endl;
|
||||
printers.push_back (new internal::Simple_property_printer<Point,Vector,float>(pmap));
|
||||
printers.push_back (new internal::PLY::Simple_property_printer<Index,Float_map>(pmap));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
{
|
||||
typename Point_set::template Property_map<double> pmap;
|
||||
Double_map pmap;
|
||||
boost::tie (pmap, okay) = point_set.template property_map<double>(prop[i]);
|
||||
if (okay)
|
||||
{
|
||||
stream << "property double " << prop[i] << std::endl;
|
||||
printers.push_back (new internal::Simple_property_printer<Point,Vector,double>(pmap));
|
||||
printers.push_back (new internal::PLY::Simple_property_printer<Index,Double_map>(pmap));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
#include <tuple>
|
||||
|
||||
#include <CGAL/IO/PLY.h>
|
||||
#include <CGAL/property_map.h>
|
||||
#include <CGAL/value_type_traits.h>
|
||||
#include <CGAL/point_set_processing_assertions.h>
|
||||
|
|
@ -43,36 +44,9 @@
|
|||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#define TRY_TO_GENERATE_PROPERTY(STD_TYPE, T_TYPE, TYPE) \
|
||||
if (type == STD_TYPE || type == T_TYPE) \
|
||||
m_elements.back().add_property (new PLY_read_typed_number< TYPE > (name, format))
|
||||
|
||||
#define TRY_TO_GENERATE_SIZED_LIST_PROPERTY(STD_SIZE_TYPE, T_SIZE_TYPE, SIZE_TYPE, STD_INDEX_TYPE, T_INDEX_TYPE, INDEX_TYPE) \
|
||||
if ((size_type == STD_SIZE_TYPE || size_type == T_SIZE_TYPE) && \
|
||||
(index_type == STD_INDEX_TYPE || index_type == T_INDEX_TYPE)) \
|
||||
m_elements.back().add_property (new PLY_read_typed_list_with_typed_size< SIZE_TYPE , INDEX_TYPE > (name, format))
|
||||
|
||||
#define TRY_TO_GENERATE_LIST_PROPERTY(STD_INDEX_TYPE, T_INDEX_TYPE, INDEX_TYPE) \
|
||||
TRY_TO_GENERATE_SIZED_LIST_PROPERTY("uchar", "uint8", boost::uint8_t, STD_INDEX_TYPE, T_INDEX_TYPE, INDEX_TYPE); \
|
||||
else TRY_TO_GENERATE_SIZED_LIST_PROPERTY("ushort", "uint16", boost::uint16_t, STD_INDEX_TYPE, T_INDEX_TYPE, INDEX_TYPE); \
|
||||
else TRY_TO_GENERATE_SIZED_LIST_PROPERTY("uint", "uint32", boost::uint32_t, STD_INDEX_TYPE, T_INDEX_TYPE, INDEX_TYPE)
|
||||
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
|
||||
// PLY types:
|
||||
// name type number of bytes
|
||||
// ---------------------------------------
|
||||
// char character 1
|
||||
// uchar unsigned character 1
|
||||
// short short integer 2
|
||||
// ushort unsigned short integer 2
|
||||
// int integer 4
|
||||
// uint unsigned integer 4
|
||||
// float single-precision float 4
|
||||
// double double-precision float 8
|
||||
|
||||
#ifdef DOXYGEN_RUNNING // Document some parts from Stream_support here for convenience
|
||||
/**
|
||||
\ingroup PkgPointSetProcessing3IOPly
|
||||
|
||||
|
|
@ -88,23 +62,6 @@ namespace CGAL {
|
|||
PLY_property (const char* name) : name (name) { }
|
||||
};
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
|
||||
// Use a double property for all kernels...
|
||||
template <typename FT> struct Convert_FT { typedef double type; };
|
||||
// ...except if kernel uses type float
|
||||
template <> struct Convert_FT<float> { typedef float type; };
|
||||
|
||||
template <typename PointOrVectorMap>
|
||||
struct GetFTFromMap
|
||||
{
|
||||
typedef typename Convert_FT
|
||||
<typename Kernel_traits
|
||||
<typename boost::property_traits
|
||||
<PointOrVectorMap>::value_type>::Kernel::FT>::type type;
|
||||
};
|
||||
/// \endcond
|
||||
|
||||
/**
|
||||
\ingroup PkgPointSetProcessing3IOPly
|
||||
|
||||
|
|
@ -118,24 +75,10 @@ namespace CGAL {
|
|||
\tparam PointMap the property map used to store points.
|
||||
*/
|
||||
template <typename PointMap>
|
||||
#ifdef DOXYGEN_RUNNING
|
||||
std::tuple<PointMap,
|
||||
typename Kernel_traits<typename PointMap::value_type>::Kernel::Construct_point_3,
|
||||
PLY_property<FT>, PLY_property<FT>, PLY_property<FT> >
|
||||
#else
|
||||
std::tuple<PointMap,
|
||||
typename Kernel_traits<typename PointMap::value_type>::Kernel::Construct_point_3,
|
||||
PLY_property<typename GetFTFromMap<PointMap>::type>,
|
||||
PLY_property<typename GetFTFromMap<PointMap>::type>,
|
||||
PLY_property<typename GetFTFromMap<PointMap>::type> >
|
||||
#endif
|
||||
make_ply_point_reader(PointMap point_map)
|
||||
{
|
||||
return std::make_tuple (point_map, typename Kernel_traits<typename PointMap::value_type>::Kernel::Construct_point_3(),
|
||||
PLY_property<typename GetFTFromMap<PointMap>::type>("x"),
|
||||
PLY_property<typename GetFTFromMap<PointMap>::type>("y"),
|
||||
PLY_property<typename GetFTFromMap<PointMap>::type>("z"));
|
||||
}
|
||||
make_ply_point_reader(PointMap point_map);
|
||||
|
||||
/**
|
||||
\ingroup PkgPointSetProcessing3IOPly
|
||||
|
|
@ -151,573 +94,11 @@ namespace CGAL {
|
|||
\tparam VectorMap the property map used to store vectors.
|
||||
*/
|
||||
template <typename VectorMap>
|
||||
#ifdef DOXYGEN_RUNNING
|
||||
std::tuple<VectorMap,
|
||||
typename Kernel_traits<typename VectorMap::value_type>::Kernel::Construct_vector_3,
|
||||
PLY_property<FT>, PLY_property<FT>, PLY_property<FT> >
|
||||
#else
|
||||
std::tuple<VectorMap,
|
||||
typename Kernel_traits<typename VectorMap::value_type>::Kernel::Construct_vector_3,
|
||||
PLY_property<typename GetFTFromMap<VectorMap>::type>,
|
||||
PLY_property<typename GetFTFromMap<VectorMap>::type>,
|
||||
PLY_property<typename GetFTFromMap<VectorMap>::type> >
|
||||
#endif
|
||||
make_ply_normal_reader(VectorMap normal_map)
|
||||
{
|
||||
return std::make_tuple (normal_map, typename Kernel_traits<typename VectorMap::value_type>::Kernel::Construct_vector_3(),
|
||||
PLY_property<typename GetFTFromMap<VectorMap>::type>("nx"),
|
||||
PLY_property<typename GetFTFromMap<VectorMap>::type>("ny"),
|
||||
PLY_property<typename GetFTFromMap<VectorMap>::type>("nz"));
|
||||
}
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
|
||||
namespace internal {
|
||||
|
||||
namespace PLY {
|
||||
|
||||
class PLY_read_number
|
||||
{
|
||||
protected:
|
||||
std::string m_name;
|
||||
std::size_t m_format;
|
||||
|
||||
public:
|
||||
PLY_read_number (std::string name, std::size_t format)
|
||||
: m_name (name), m_format (format) { }
|
||||
virtual ~PLY_read_number() { }
|
||||
|
||||
const std::string& name () const { return m_name; }
|
||||
|
||||
virtual void get (std::istream& stream) const = 0;
|
||||
|
||||
// The two following functions prevent the stream to only extract
|
||||
// ONE character (= what the types char imply) by requiring
|
||||
// explicitely an integer object when reading the stream
|
||||
void read_ascii (std::istream& stream, char& c) const
|
||||
{
|
||||
short s;
|
||||
stream >> s;
|
||||
c = static_cast<char>(s);
|
||||
}
|
||||
void read_ascii (std::istream& stream, signed char& c) const
|
||||
{
|
||||
short s;
|
||||
stream >> s;
|
||||
c = static_cast<signed char>(s);
|
||||
}
|
||||
void read_ascii (std::istream& stream, unsigned char& c) const
|
||||
{
|
||||
unsigned short s;
|
||||
stream >> s;
|
||||
c = static_cast<unsigned char>(s);
|
||||
}
|
||||
|
||||
void read_ascii (std::istream& stream, float& t) const
|
||||
{
|
||||
stream >> iformat(t);
|
||||
}
|
||||
|
||||
void read_ascii (std::istream& stream, double& t) const
|
||||
{
|
||||
stream >> iformat(t);
|
||||
}
|
||||
|
||||
// Default template when Type is not a char type
|
||||
template <typename Type>
|
||||
void read_ascii (std::istream& stream, Type& t) const
|
||||
{
|
||||
stream >> t;
|
||||
}
|
||||
|
||||
|
||||
template <typename Type>
|
||||
Type read (std::istream& stream) const
|
||||
{
|
||||
if (m_format == 0) // Ascii
|
||||
{
|
||||
Type t;
|
||||
read_ascii (stream, t);
|
||||
return t;
|
||||
}
|
||||
else // Binary (2 = little endian)
|
||||
{
|
||||
union
|
||||
{
|
||||
char uChar[sizeof (Type)];
|
||||
Type type;
|
||||
} buffer;
|
||||
|
||||
std::size_t size = sizeof (Type);
|
||||
|
||||
stream.read(buffer.uChar, size);
|
||||
|
||||
if (m_format == 2) // Big endian
|
||||
{
|
||||
for (std::size_t i = 0; i < size / 2; ++ i)
|
||||
{
|
||||
unsigned char tmp = buffer.uChar[i];
|
||||
buffer.uChar[i] = buffer.uChar[size - 1 - i];
|
||||
buffer.uChar[size - 1 - i] = tmp;
|
||||
}
|
||||
}
|
||||
return buffer.type;
|
||||
}
|
||||
return Type();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
class PLY_read_typed_number : public PLY_read_number
|
||||
{
|
||||
mutable Type m_buffer;
|
||||
public:
|
||||
PLY_read_typed_number (std::string name, std::size_t format)
|
||||
: PLY_read_number (name, format)
|
||||
{
|
||||
}
|
||||
void get (std::istream& stream) const
|
||||
{
|
||||
m_buffer = (this->read<Type> (stream));
|
||||
}
|
||||
const Type& buffer() const
|
||||
{
|
||||
return m_buffer;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
class PLY_read_typed_list : public PLY_read_number
|
||||
{
|
||||
protected:
|
||||
mutable std::vector<Type> m_buffer;
|
||||
public:
|
||||
PLY_read_typed_list (std::string name, std::size_t format)
|
||||
: PLY_read_number (name, format)
|
||||
{
|
||||
}
|
||||
virtual void get (std::istream& stream) const = 0;
|
||||
|
||||
const std::vector<Type>& buffer() const
|
||||
{
|
||||
return m_buffer;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename SizeType, typename IndexType>
|
||||
class PLY_read_typed_list_with_typed_size
|
||||
: public PLY_read_typed_list<IndexType>
|
||||
{
|
||||
|
||||
public:
|
||||
PLY_read_typed_list_with_typed_size (std::string name, std::size_t format)
|
||||
: PLY_read_typed_list<IndexType> (name, format)
|
||||
{
|
||||
}
|
||||
void get (std::istream& stream) const
|
||||
{
|
||||
std::size_t size = static_cast<std::size_t>(this->template read<SizeType>(stream));
|
||||
this->m_buffer.resize (size);
|
||||
for (std::size_t i = 0; i < size; ++ i)
|
||||
this->m_buffer[i] = this->template read<IndexType> (stream);
|
||||
}
|
||||
};
|
||||
|
||||
class PLY_element
|
||||
{
|
||||
std::string m_name;
|
||||
std::size_t m_number;
|
||||
|
||||
std::vector<PLY_read_number*> m_properties;
|
||||
public:
|
||||
|
||||
PLY_element (const std::string& name, std::size_t number)
|
||||
: m_name (name), m_number (number)
|
||||
{ }
|
||||
|
||||
PLY_element (const PLY_element& other)
|
||||
: m_name (other.m_name), m_number (other.m_number), m_properties (other.m_properties)
|
||||
{
|
||||
const_cast<PLY_element&>(other).m_properties.clear();
|
||||
}
|
||||
|
||||
PLY_element& operator= (const PLY_element& other)
|
||||
{
|
||||
m_name = other.m_name;
|
||||
m_number = other.m_number;
|
||||
m_properties = other.m_properties;
|
||||
const_cast<PLY_element&>(other).m_properties.clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
~PLY_element()
|
||||
{
|
||||
for (std::size_t i = 0; i < m_properties.size(); ++ i)
|
||||
delete m_properties[i];
|
||||
}
|
||||
|
||||
const std::string& name() const { return m_name; }
|
||||
std::size_t number_of_items() const { return m_number; }
|
||||
std::size_t number_of_properties() const { return m_properties.size(); }
|
||||
|
||||
PLY_read_number* property (std::size_t idx) { return m_properties[idx]; }
|
||||
|
||||
void add_property (PLY_read_number* read_number)
|
||||
{
|
||||
m_properties.push_back (read_number);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
bool has_property (const char* tag)
|
||||
{
|
||||
return has_property (tag, Type());
|
||||
}
|
||||
template <typename Type>
|
||||
bool has_property (const char* tag, const std::vector<Type>&)
|
||||
{
|
||||
for (std::size_t i = 0; i < number_of_properties(); ++ i)
|
||||
if (m_properties[i]->name () == tag)
|
||||
return (dynamic_cast<PLY_read_typed_list<Type>*>(m_properties[i]) != NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
bool has_property (const char* tag, Type)
|
||||
{
|
||||
for (std::size_t i = 0; i < number_of_properties(); ++ i)
|
||||
if (m_properties[i]->name () == tag)
|
||||
return (dynamic_cast<PLY_read_typed_number<Type>*>(m_properties[i]) != NULL);
|
||||
return false;
|
||||
}
|
||||
bool has_property (const char* tag, double)
|
||||
{
|
||||
for (std::size_t i = 0; i < number_of_properties(); ++ i)
|
||||
if (m_properties[i]->name () == tag)
|
||||
return (dynamic_cast<PLY_read_typed_number<double>*>(m_properties[i]) != NULL
|
||||
|| dynamic_cast<PLY_read_typed_number<float>*>(m_properties[i]) != NULL);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void assign (Type& t, const char* tag)
|
||||
{
|
||||
for (std::size_t i = 0; i < number_of_properties (); ++ i)
|
||||
if (m_properties[i]->name () == tag)
|
||||
{
|
||||
PLY_read_typed_number<Type>*
|
||||
property = dynamic_cast<PLY_read_typed_number<Type>*>(m_properties[i]);
|
||||
CGAL_assertion (property != NULL);
|
||||
t = property->buffer();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void assign (std::vector<Type>& t, const char* tag)
|
||||
{
|
||||
for (std::size_t i = 0; i < number_of_properties (); ++ i)
|
||||
if (m_properties[i]->name () == tag)
|
||||
{
|
||||
PLY_read_typed_list<Type>*
|
||||
property = dynamic_cast<PLY_read_typed_list<Type>*>(m_properties[i]);
|
||||
CGAL_assertion (property != NULL);
|
||||
t = property->buffer();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void assign (double& t, const char* tag)
|
||||
{
|
||||
for (std::size_t i = 0; i < number_of_properties (); ++ i)
|
||||
if (m_properties[i]->name () == tag)
|
||||
{
|
||||
PLY_read_typed_number<double>*
|
||||
property_double = dynamic_cast<PLY_read_typed_number<double>*>(m_properties[i]);
|
||||
if (property_double == NULL)
|
||||
{
|
||||
PLY_read_typed_number<float>*
|
||||
property_float = dynamic_cast<PLY_read_typed_number<float>*>(m_properties[i]);
|
||||
CGAL_assertion (property_float != NULL);
|
||||
t = property_float->buffer();
|
||||
}
|
||||
else
|
||||
t = property_double->buffer();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class PLY_reader
|
||||
{
|
||||
std::vector<PLY_element> m_elements;
|
||||
std::string m_comments;
|
||||
|
||||
public:
|
||||
PLY_reader () { }
|
||||
|
||||
std::size_t number_of_elements() const { return m_elements.size(); }
|
||||
PLY_element& element (std::size_t idx)
|
||||
{
|
||||
return m_elements[idx];
|
||||
}
|
||||
|
||||
const std::string& comments() const { return m_comments; }
|
||||
|
||||
template <typename Stream>
|
||||
bool init (Stream& stream)
|
||||
{
|
||||
std::size_t lineNumber = 0; // current line number
|
||||
enum Format { ASCII = 0, BINARY_LITTLE_ENDIAN = 1, BINARY_BIG_ENDIAN = 2};
|
||||
Format format = ASCII;
|
||||
|
||||
std::string line;
|
||||
std::istringstream iss;
|
||||
|
||||
while (getline (stream,line))
|
||||
{
|
||||
iss.clear();
|
||||
iss.str (line);
|
||||
++ lineNumber;
|
||||
|
||||
// Reads file signature on first line
|
||||
if (lineNumber == 1)
|
||||
{
|
||||
std::string signature;
|
||||
if (!(iss >> signature) || (signature != "ply"))
|
||||
{
|
||||
// if wrong file format
|
||||
std::cerr << "Error: incorrect file format line " << lineNumber << " of file" << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Reads format on 2nd line
|
||||
else if (lineNumber == 2)
|
||||
{
|
||||
std::string tag, format_string, version;
|
||||
if ( !(iss >> tag >> format_string >> version) )
|
||||
{
|
||||
std::cerr << "Error line " << lineNumber << " of file" << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (format_string == "ascii") format = ASCII;
|
||||
else if (format_string == "binary_little_endian") format = BINARY_LITTLE_ENDIAN;
|
||||
else if (format_string == "binary_big_endian") format = BINARY_BIG_ENDIAN;
|
||||
else
|
||||
{
|
||||
std::cerr << "Error: unknown file format \"" << format_string << "\" line " << lineNumber << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Comments and vertex properties
|
||||
else
|
||||
{
|
||||
std::string keyword;
|
||||
if (!(iss >> keyword))
|
||||
{
|
||||
std::cerr << "Error line " << lineNumber << " of file" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (keyword == "property")
|
||||
{
|
||||
std::string type, name;
|
||||
if (!(iss >> type >> name))
|
||||
{
|
||||
std::cerr << "Error line " << lineNumber << " of file" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (type == "list") // Special case
|
||||
{
|
||||
std::string size_type = name;
|
||||
std::string index_type;
|
||||
name.clear();
|
||||
if (!(iss >> index_type >> name))
|
||||
{
|
||||
std::cerr << "Error line " << lineNumber << " of file" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
TRY_TO_GENERATE_LIST_PROPERTY ("char", "int8", boost::int8_t);
|
||||
else TRY_TO_GENERATE_LIST_PROPERTY ("uchar", "uint8", boost::uint8_t);
|
||||
else TRY_TO_GENERATE_LIST_PROPERTY ("short", "int16", boost::int16_t);
|
||||
else TRY_TO_GENERATE_LIST_PROPERTY ("ushort", "uint16", boost::uint16_t);
|
||||
else TRY_TO_GENERATE_LIST_PROPERTY ("int", "int32", boost::int32_t);
|
||||
else TRY_TO_GENERATE_LIST_PROPERTY ("uint", "uint32", boost::uint32_t);
|
||||
else TRY_TO_GENERATE_LIST_PROPERTY ("float", "float32", float);
|
||||
else TRY_TO_GENERATE_LIST_PROPERTY ("double", "float64", double);
|
||||
}
|
||||
else
|
||||
{
|
||||
TRY_TO_GENERATE_PROPERTY ("char", "int8", boost::int8_t);
|
||||
else TRY_TO_GENERATE_PROPERTY ("uchar", "uint8", boost::uint8_t);
|
||||
else TRY_TO_GENERATE_PROPERTY ("short", "int16", boost::int16_t);
|
||||
else TRY_TO_GENERATE_PROPERTY ("ushort", "uint16", boost::uint16_t);
|
||||
else TRY_TO_GENERATE_PROPERTY ("int", "int32", boost::int32_t);
|
||||
else TRY_TO_GENERATE_PROPERTY ("uint", "uint32", boost::uint32_t);
|
||||
else TRY_TO_GENERATE_PROPERTY ("float", "float32", float);
|
||||
else TRY_TO_GENERATE_PROPERTY ("double", "float64", double);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
else if (keyword == "comment")
|
||||
{
|
||||
std::string str = iss.str();
|
||||
if (str.size() > 8)
|
||||
{
|
||||
std::copy (str.begin() + 8, str.end(), std::back_inserter (m_comments));
|
||||
m_comments += "\n";
|
||||
}
|
||||
}
|
||||
else if (keyword == "element")
|
||||
{
|
||||
std::string type;
|
||||
std::size_t number;
|
||||
if (!(iss >> type >> number))
|
||||
{
|
||||
std::cerr << "Error line " << lineNumber << " of file" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_elements.push_back (PLY_element(type, number));
|
||||
}
|
||||
// When end_header is reached, stop loop and begin reading points
|
||||
else if (keyword == "end_header")
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
~PLY_reader ()
|
||||
{
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <class Reader, class T>
|
||||
void get_value(Reader& r, T& v, PLY_property<T>& wrapper)
|
||||
{
|
||||
return r.assign(v, wrapper.name);
|
||||
}
|
||||
|
||||
|
||||
template <std::size_t N>
|
||||
struct Filler
|
||||
{
|
||||
template <class Reader, class Value_tuple, class PLY_property_tuple>
|
||||
static void fill(Reader& r, Value_tuple& values, PLY_property_tuple wrappers)
|
||||
{
|
||||
get_value(r, std::get<N>(values), std::get<N+2>(wrappers));
|
||||
Filler<N-1>::fill(r, values, wrappers);
|
||||
}
|
||||
};
|
||||
|
||||
template<int ...>
|
||||
struct seq { };
|
||||
|
||||
template<int N, int ...S>
|
||||
struct gens : gens<N-1, N-1, S...> { };
|
||||
|
||||
template<int ...S>
|
||||
struct gens<0, S...> {
|
||||
typedef seq<S...> type;
|
||||
};
|
||||
|
||||
template<class ValueType, class Functor, class Tuple, int ...S>
|
||||
ValueType call_functor(Functor f, Tuple t, seq<S...>) {
|
||||
return f(std::get<S>(t) ...);
|
||||
}
|
||||
|
||||
template <class ValueType, class Functor, typename ... T>
|
||||
ValueType call_functor(Functor f, std::tuple<T...>& t)
|
||||
{
|
||||
return call_functor<ValueType>(f, t, typename gens<sizeof...(T)>::type());
|
||||
}
|
||||
|
||||
template<>
|
||||
struct Filler<0>
|
||||
{
|
||||
template <class Reader, class Value_tuple, class PLY_property_tuple>
|
||||
static void fill(Reader& r, Value_tuple& values, PLY_property_tuple wrappers)
|
||||
{
|
||||
get_value(r, std::get<0>(values), std::get<2>(wrappers));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename OutputValueType,
|
||||
typename PropertyMap,
|
||||
typename Constructor,
|
||||
typename ... T>
|
||||
void process_properties (PLY_element& element, OutputValueType& new_element,
|
||||
std::tuple<PropertyMap, Constructor, PLY_property<T>...>&& current)
|
||||
{
|
||||
typedef typename PropertyMap::value_type PmapValueType;
|
||||
std::tuple<T...> values;
|
||||
Filler<sizeof...(T)-1>::fill(element, values, current);
|
||||
PmapValueType new_value = call_functor<PmapValueType>(std::get<1>(current), values);
|
||||
put (std::get<0>(current), new_element, new_value);
|
||||
}
|
||||
|
||||
template <typename OutputValueType,
|
||||
typename PropertyMap,
|
||||
typename Constructor,
|
||||
typename ... T,
|
||||
typename NextPropertyBinder,
|
||||
typename ... PropertyMapBinders>
|
||||
void process_properties (PLY_element& element, OutputValueType& new_element,
|
||||
std::tuple<PropertyMap, Constructor, PLY_property<T>...>&& current,
|
||||
NextPropertyBinder&& next,
|
||||
PropertyMapBinders&& ... properties)
|
||||
{
|
||||
typedef typename PropertyMap::value_type PmapValueType;
|
||||
std::tuple<T...> values;
|
||||
Filler<sizeof...(T)-1>::fill(element, values, current);
|
||||
PmapValueType new_value = call_functor<PmapValueType>(std::get<1>(current), values);
|
||||
put (std::get<0>(current), new_element, new_value);
|
||||
|
||||
process_properties (element, new_element, std::forward<NextPropertyBinder>(next),
|
||||
std::forward<PropertyMapBinders>(properties)...);
|
||||
}
|
||||
|
||||
|
||||
template <typename OutputValueType, typename PropertyMap, typename T>
|
||||
void process_properties (PLY_element& element, OutputValueType& new_element,
|
||||
std::pair<PropertyMap, PLY_property<T> >&& current)
|
||||
{
|
||||
T new_value = T();
|
||||
element.assign (new_value, current.second.name);
|
||||
put (current.first, new_element, new_value);
|
||||
}
|
||||
|
||||
template <typename OutputValueType, typename PropertyMap, typename T,
|
||||
typename NextPropertyBinder, typename ... PropertyMapBinders>
|
||||
void process_properties (PLY_element& element, OutputValueType& new_element,
|
||||
std::pair<PropertyMap, PLY_property<T> >&& current,
|
||||
NextPropertyBinder&& next,
|
||||
PropertyMapBinders&& ... properties)
|
||||
{
|
||||
T new_value = T();
|
||||
element.assign (new_value, current.second.name);
|
||||
put (current.first, new_element, new_value);
|
||||
process_properties (element, new_element, std::forward<NextPropertyBinder>(next),
|
||||
std::forward<PropertyMapBinders>(properties)...);
|
||||
}
|
||||
|
||||
} // namespace PLY
|
||||
|
||||
} // namespace internal
|
||||
|
||||
|
||||
/// \endcond
|
||||
|
||||
make_ply_normal_reader(VectorMap normal_map);
|
||||
#endif // DOXYGEN_RUNNING
|
||||
|
||||
/*
|
||||
\ingroup PkgPointSetProcessing3IOPly
|
||||
|
|
|
|||
|
|
@ -27,9 +27,9 @@
|
|||
|
||||
#include <tuple>
|
||||
|
||||
#include <CGAL/IO/PLY.h>
|
||||
#include <CGAL/property_map.h>
|
||||
#include <CGAL/point_set_processing_assertions.h>
|
||||
#include <CGAL/IO/read_ply_points.h>
|
||||
#include <CGAL/Iterator_range.h>
|
||||
|
||||
#include <CGAL/boost/graph/named_function_params.h>
|
||||
|
|
@ -42,6 +42,7 @@
|
|||
|
||||
namespace CGAL {
|
||||
|
||||
#ifdef DOXYGEN_RUNNING // Document some parts from Stream_support here for convenience
|
||||
/**
|
||||
\ingroup PkgPointSetProcessing3IOPly
|
||||
|
||||
|
|
@ -55,21 +56,8 @@ namespace CGAL {
|
|||
\tparam PointMap the property map used to store points.
|
||||
*/
|
||||
template <typename PointMap>
|
||||
#ifdef DOXYGEN_RUNNING
|
||||
std::tuple<PointMap, PLY_property<FT>, PLY_property<FT>, PLY_property<FT> >
|
||||
#else
|
||||
std::tuple<PointMap,
|
||||
PLY_property<typename GetFTFromMap<PointMap>::type>,
|
||||
PLY_property<typename GetFTFromMap<PointMap>::type>,
|
||||
PLY_property<typename GetFTFromMap<PointMap>::type> >
|
||||
#endif
|
||||
make_ply_point_writer(PointMap point_map)
|
||||
{
|
||||
return std::make_tuple (point_map,
|
||||
PLY_property<typename GetFTFromMap<PointMap>::type>("x"),
|
||||
PLY_property<typename GetFTFromMap<PointMap>::type>("y"),
|
||||
PLY_property<typename GetFTFromMap<PointMap>::type>("z"));
|
||||
}
|
||||
make_ply_point_writer(PointMap point_map);
|
||||
|
||||
/**
|
||||
\ingroup PkgPointSetProcessing3IOPly
|
||||
|
|
@ -84,21 +72,9 @@ namespace CGAL {
|
|||
\tparam VectorMap the property map used to store vectors.
|
||||
*/
|
||||
template <typename VectorMap>
|
||||
#ifdef DOXYGEN_RUNNING
|
||||
std::tuple<VectorMap, PLY_property<FT>, PLY_property<FT>, PLY_property<FT> >
|
||||
#else
|
||||
std::tuple<VectorMap,
|
||||
PLY_property<typename GetFTFromMap<VectorMap>::type>,
|
||||
PLY_property<typename GetFTFromMap<VectorMap>::type>,
|
||||
PLY_property<typename GetFTFromMap<VectorMap>::type> >
|
||||
make_ply_normal_writer(VectorMap normal_map);
|
||||
#endif
|
||||
make_ply_normal_writer(VectorMap normal_map)
|
||||
{
|
||||
return std::make_tuple (normal_map,
|
||||
PLY_property<typename GetFTFromMap<VectorMap>::type>("nx"),
|
||||
PLY_property<typename GetFTFromMap<VectorMap>::type>("ny"),
|
||||
PLY_property<typename GetFTFromMap<VectorMap>::type>("nz"));
|
||||
}
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
|
||||
|
|
@ -106,219 +82,6 @@ namespace internal {
|
|||
|
||||
namespace PLY {
|
||||
|
||||
template <typename T> void property_header_type (std::ostream& stream)
|
||||
{
|
||||
CGAL_assertion_msg (false, "Unknown PLY type");
|
||||
stream << "undefined_type";
|
||||
}
|
||||
|
||||
template <> void property_header_type<char> (std::ostream& stream) { stream << "char"; }
|
||||
template <> void property_header_type<signed char> (std::ostream& stream) { stream << "char"; }
|
||||
template <> void property_header_type<unsigned char> (std::ostream& stream) { stream << "uchar"; }
|
||||
template <> void property_header_type<short> (std::ostream& stream) { stream << "short"; }
|
||||
template <> void property_header_type<unsigned short> (std::ostream& stream) { stream << "ushort"; }
|
||||
template <> void property_header_type<int> (std::ostream& stream) { stream << "int"; }
|
||||
template <> void property_header_type<unsigned int> (std::ostream& stream) { stream << "uint"; }
|
||||
template <> void property_header_type<float> (std::ostream& stream) { stream << "float"; }
|
||||
template <> void property_header_type<double> (std::ostream& stream) { stream << "double"; }
|
||||
|
||||
template <typename T>
|
||||
void property_header (std::ostream& stream, const PLY_property<T>& prop)
|
||||
{
|
||||
stream << "property ";
|
||||
property_header_type<T>(stream);
|
||||
stream << " " << prop.name << std::endl;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void property_header (std::ostream& stream, const PLY_property<std::vector<T> >& prop)
|
||||
{
|
||||
stream << "property list uchar ";
|
||||
property_header_type<T>(stream);
|
||||
stream << " " << prop.name << std::endl;
|
||||
}
|
||||
|
||||
|
||||
template <std::size_t N>
|
||||
struct Properties_header
|
||||
{
|
||||
template <class PLY_property_tuple>
|
||||
static void write(std::ostream& stream, PLY_property_tuple& wrappers)
|
||||
{
|
||||
Properties_header<N-1>::write(stream, wrappers);
|
||||
property_header (stream, std::get<N+1>(wrappers));
|
||||
}
|
||||
};
|
||||
template <>
|
||||
struct Properties_header<0>
|
||||
{
|
||||
template <class PLY_property_tuple>
|
||||
static void write(std::ostream& stream, PLY_property_tuple& wrappers)
|
||||
{
|
||||
property_header (stream, std::get<1>(wrappers));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename PropertyMap,
|
||||
typename ... T>
|
||||
void output_property_header (std::ostream& stream,
|
||||
std::tuple<PropertyMap, PLY_property<T>... >&& current)
|
||||
{
|
||||
Properties_header<sizeof...(T)-1>::write(stream, current);
|
||||
}
|
||||
|
||||
|
||||
template <typename PropertyMap,
|
||||
typename T>
|
||||
void output_property_header (std::ostream& stream,
|
||||
std::pair<PropertyMap, PLY_property<T> >&& current)
|
||||
{
|
||||
property_header (stream, current.second);
|
||||
}
|
||||
|
||||
template <typename PropertyMap,
|
||||
typename T,
|
||||
typename NextPropertyHandler,
|
||||
typename ... PropertyHandler>
|
||||
void output_property_header (std::ostream& stream,
|
||||
std::pair<PropertyMap, PLY_property<T> >&& current,
|
||||
NextPropertyHandler&& next,
|
||||
PropertyHandler&& ... properties)
|
||||
{
|
||||
property_header (stream, current.second);
|
||||
output_property_header (stream, std::forward<NextPropertyHandler>(next),
|
||||
std::forward<PropertyHandler>(properties)...);
|
||||
}
|
||||
template <typename PropertyMap,
|
||||
typename ... T,
|
||||
typename NextPropertyHandler,
|
||||
typename ... PropertyHandler>
|
||||
void output_property_header (std::ostream& stream,
|
||||
std::tuple<PropertyMap, PLY_property<T>... >&& current,
|
||||
NextPropertyHandler&& next,
|
||||
PropertyHandler&& ... properties)
|
||||
{
|
||||
Properties_header<sizeof...(T)-1>::write(stream, current);
|
||||
output_property_header (stream, std::forward<NextPropertyHandler>(next),
|
||||
std::forward<PropertyHandler>(properties)...);
|
||||
}
|
||||
|
||||
|
||||
template <typename ForwardIterator,
|
||||
typename PropertyMap>
|
||||
void property_write (std::ostream& stream, ForwardIterator it, PropertyMap map)
|
||||
{
|
||||
stream << CGAL::oformat(get (map, *it));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T no_char_character (const T& t) { return t; }
|
||||
int no_char_character (const char& t) { return int(t); }
|
||||
int no_char_character (const signed char& t) { return int(t); }
|
||||
int no_char_character (const unsigned char& t) { return int(t); }
|
||||
|
||||
template <typename ForwardIterator,
|
||||
typename PropertyMap,
|
||||
typename T>
|
||||
void simple_property_write (std::ostream& stream, ForwardIterator it,
|
||||
std::pair<PropertyMap, PLY_property<T> > map)
|
||||
{
|
||||
if (CGAL::get_mode(stream) == IO::ASCII)
|
||||
stream << no_char_character(get (map.first, *it));
|
||||
else
|
||||
{
|
||||
typename PropertyMap::value_type value = get(map.first, *it);
|
||||
stream.write (reinterpret_cast<char*>(&value), sizeof(value));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ForwardIterator,
|
||||
typename PropertyMap,
|
||||
typename T>
|
||||
void simple_property_write (std::ostream& stream, ForwardIterator it,
|
||||
std::pair<PropertyMap, PLY_property<std::vector<T> > > map)
|
||||
{
|
||||
const typename PropertyMap::reference value = get(map.first, *it);
|
||||
|
||||
if (CGAL::get_mode(stream) == IO::ASCII)
|
||||
{
|
||||
stream << value.size();
|
||||
for (std::size_t i = 0; i < value.size(); ++ i)
|
||||
stream << " " << no_char_character(value[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned char size = static_cast<unsigned char>(value.size());
|
||||
stream.write (reinterpret_cast<char*>(&size), sizeof(size));
|
||||
for (std::size_t i = 0; i < value.size(); ++ i)
|
||||
{
|
||||
T t = T(value[i]);
|
||||
stream.write (reinterpret_cast<char*>(&t), sizeof(t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename ForwardIterator,
|
||||
typename PropertyMap,
|
||||
typename ... T>
|
||||
void output_properties (std::ostream& stream,
|
||||
ForwardIterator it,
|
||||
std::tuple<PropertyMap, PLY_property<T>... >&& current)
|
||||
{
|
||||
property_write (stream, it, std::get<0>(current));
|
||||
if (get_mode(stream) == IO::ASCII)
|
||||
stream << std::endl;
|
||||
}
|
||||
|
||||
|
||||
template <typename ForwardIterator,
|
||||
typename PropertyMap,
|
||||
typename T>
|
||||
void output_properties (std::ostream& stream,
|
||||
ForwardIterator it,
|
||||
std::pair<PropertyMap, PLY_property<T> >&& current)
|
||||
{
|
||||
simple_property_write (stream, it, std::forward<std::pair<PropertyMap, PLY_property<T> > >(current));
|
||||
if (get_mode(stream) == IO::ASCII)
|
||||
stream << std::endl;
|
||||
}
|
||||
|
||||
template <typename ForwardIterator,
|
||||
typename PropertyMap,
|
||||
typename T,
|
||||
typename NextPropertyHandler,
|
||||
typename ... PropertyHandler>
|
||||
void output_properties (std::ostream& stream,
|
||||
ForwardIterator it,
|
||||
std::pair<PropertyMap, PLY_property<T> >&& current,
|
||||
NextPropertyHandler&& next,
|
||||
PropertyHandler&& ... properties)
|
||||
{
|
||||
simple_property_write (stream, it, current);
|
||||
if (get_mode(stream) == IO::ASCII)
|
||||
stream << " ";
|
||||
output_properties (stream, it, std::forward<NextPropertyHandler>(next),
|
||||
std::forward<PropertyHandler>(properties)...);
|
||||
}
|
||||
|
||||
template <typename ForwardIterator,
|
||||
typename PropertyMap,
|
||||
typename ... T,
|
||||
typename NextPropertyHandler,
|
||||
typename ... PropertyHandler>
|
||||
void output_properties (std::ostream& stream,
|
||||
ForwardIterator it,
|
||||
std::tuple<PropertyMap, PLY_property<T>... >&& current,
|
||||
NextPropertyHandler&& next,
|
||||
PropertyHandler&& ... properties)
|
||||
{
|
||||
property_write (stream, it, std::get<0>(current));
|
||||
if (get_mode(stream) == IO::ASCII)
|
||||
stream << " ";
|
||||
output_properties (stream, it, std::forward<NextPropertyHandler>(next),
|
||||
std::forward<PropertyHandler>(properties)...);
|
||||
}
|
||||
|
||||
} // namespace PLY
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@
|
|||
#include <CGAL/IO/PLY_reader.h>
|
||||
#include <CGAL/IO/PLY_writer.h>
|
||||
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
|
||||
#include <QTime>
|
||||
#include <QMessageBox>
|
||||
|
||||
class Polyhedron_demo_ply_plugin :
|
||||
|
|
@ -38,62 +37,6 @@ public:
|
|||
bool canSave(const CGAL::Three::Scene_item*);
|
||||
bool save(const CGAL::Three::Scene_item*, QFileInfo fileinfo);
|
||||
|
||||
private:
|
||||
void set_vcolors(SMesh* smesh, const std::vector<CGAL::Color>& colors)
|
||||
{
|
||||
typedef boost::graph_traits<SMesh>::vertex_descriptor vertex_descriptor;
|
||||
SMesh::Property_map<vertex_descriptor, CGAL::Color> vcolors =
|
||||
smesh->property_map<vertex_descriptor, CGAL::Color >("v:color").first;
|
||||
bool created;
|
||||
boost::tie(vcolors, created) = smesh->add_property_map<SMesh::Vertex_index,CGAL::Color>("v:color",CGAL::Color(0,0,0));
|
||||
assert(colors.size()==smesh->number_of_vertices());
|
||||
int color_id = 0;
|
||||
for(vertex_descriptor vd : vertices(*smesh))
|
||||
vcolors[vd] = colors[color_id++];
|
||||
}
|
||||
|
||||
void set_fcolors(SMesh* smesh, const std::vector<CGAL::Color>& colors)
|
||||
{
|
||||
typedef boost::graph_traits<SMesh>::face_descriptor face_descriptor;
|
||||
SMesh::Property_map<face_descriptor, CGAL::Color> fcolors =
|
||||
smesh->property_map<face_descriptor, CGAL::Color >("f:color").first;
|
||||
bool created;
|
||||
boost::tie(fcolors, created) = smesh->add_property_map<SMesh::Face_index,CGAL::Color>("f:color",CGAL::Color(0,0,0));
|
||||
assert(colors.size()==smesh->number_of_faces());
|
||||
int color_id = 0;
|
||||
for(face_descriptor fd : faces(*smesh))
|
||||
fcolors[fd] = colors[color_id++];
|
||||
}
|
||||
|
||||
bool set_huvs(SMesh* smesh,
|
||||
std::vector<std::pair<unsigned int, unsigned int> > & hedges,
|
||||
std::vector<std::pair<float, float> >& uvs)
|
||||
{
|
||||
QTime timer;
|
||||
timer.start();
|
||||
typedef boost::graph_traits<SMesh>::halfedge_descriptor halfedge_descriptor;
|
||||
typedef boost::graph_traits<SMesh>::vertex_descriptor vertex_descriptor;
|
||||
SMesh::Property_map<halfedge_descriptor,std::pair<float, float> > uv =
|
||||
smesh->property_map<halfedge_descriptor,std::pair<float, float> >("h:uv").first;
|
||||
bool created;
|
||||
boost::tie(uv, created) = smesh->add_property_map<halfedge_descriptor,
|
||||
std::pair<float, float> >("h:uv",std::make_pair(0.0f,0.0f));
|
||||
assert(hedges.size()==smesh->number_of_halfedges());
|
||||
assert(uvs.size()==smesh->number_of_halfedges());
|
||||
for(std::size_t id = 0; id < hedges.size(); ++id)
|
||||
{
|
||||
bool exists = false;
|
||||
halfedge_descriptor hd;
|
||||
boost::tie(hd, exists) = halfedge(
|
||||
vertex_descriptor(hedges[id].first),
|
||||
vertex_descriptor(hedges[id].second),
|
||||
*smesh);
|
||||
if(!exists)
|
||||
return false;
|
||||
uv[hd] = uvs[id];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
bool Polyhedron_demo_ply_plugin::canLoad() const {
|
||||
|
|
@ -146,57 +89,39 @@ Polyhedron_demo_ply_plugin::load(QFileInfo fileinfo) {
|
|||
|
||||
if (input_is_mesh) // Open mesh or polygon soup
|
||||
{
|
||||
// First try mesh
|
||||
SMesh *surface_mesh = new SMesh();
|
||||
std::string comments;
|
||||
|
||||
if (CGAL::read_ply (in, *surface_mesh, comments))
|
||||
{
|
||||
Scene_surface_mesh_item* sm_item = new Scene_surface_mesh_item(surface_mesh);
|
||||
sm_item->setName(fileinfo.completeBaseName());
|
||||
sm_item->comments() = comments;
|
||||
QApplication::restoreOverrideCursor();
|
||||
return sm_item;
|
||||
}
|
||||
|
||||
in.clear();
|
||||
in.seekg(0);
|
||||
|
||||
// else try polygon soup
|
||||
std::vector<Kernel::Point_3> points;
|
||||
std::vector<std::vector<std::size_t> > polygons;
|
||||
std::vector<std::pair<unsigned int, unsigned int> > hedges;
|
||||
std::vector<CGAL::Color> fcolors;
|
||||
std::vector<CGAL::Color> vcolors;
|
||||
std::vector<std::pair<float, float> > huvs;
|
||||
QTime timer;
|
||||
timer.start();
|
||||
if (!(CGAL::read_PLY (in, points, polygons, hedges, fcolors, vcolors, huvs)))
|
||||
|
||||
if (!(CGAL::read_PLY (in, points, polygons, fcolors, vcolors)))
|
||||
{
|
||||
QApplication::restoreOverrideCursor();
|
||||
return NULL;
|
||||
}
|
||||
if (CGAL::Polygon_mesh_processing::is_polygon_soup_a_polygon_mesh (polygons))
|
||||
{
|
||||
CGAL::Three::Scene_item* item = nullptr;
|
||||
SMesh *surface_mesh = new SMesh();
|
||||
CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh (points, polygons,
|
||||
*surface_mesh);
|
||||
if(!(vcolors.empty()))
|
||||
set_vcolors(surface_mesh, vcolors);
|
||||
if(!(fcolors.empty()))
|
||||
set_fcolors(surface_mesh, fcolors);
|
||||
if(!huvs.empty())
|
||||
{
|
||||
if(!set_huvs(surface_mesh, hedges, huvs))
|
||||
{
|
||||
std::cerr<<"halfedge not found."<<std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
item = new Scene_textured_surface_mesh_item(surface_mesh);
|
||||
item->invalidateOpenGLBuffers();
|
||||
item->itemChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
item = new Scene_surface_mesh_item(surface_mesh);
|
||||
}
|
||||
|
||||
item->setName(fileinfo.completeBaseName());
|
||||
QApplication::restoreOverrideCursor();
|
||||
return item;
|
||||
}
|
||||
else
|
||||
{
|
||||
Scene_polygon_soup_item* soup_item = new Scene_polygon_soup_item;
|
||||
soup_item->setName(fileinfo.completeBaseName());
|
||||
soup_item->load (points, polygons, fcolors, vcolors);
|
||||
QApplication::restoreOverrideCursor();
|
||||
return soup_item;
|
||||
}
|
||||
|
||||
Scene_polygon_soup_item* soup_item = new Scene_polygon_soup_item;
|
||||
soup_item->setName(fileinfo.completeBaseName());
|
||||
soup_item->load (points, polygons, fcolors, vcolors);
|
||||
QApplication::restoreOverrideCursor();
|
||||
return soup_item;
|
||||
}
|
||||
else // Open point set
|
||||
{
|
||||
|
|
@ -245,7 +170,10 @@ bool Polyhedron_demo_ply_plugin::save(const CGAL::Three::Scene_item* item, QFile
|
|||
return false;
|
||||
|
||||
std::ofstream out(fileinfo.filePath().toUtf8().data(), std::ios::binary);
|
||||
out.precision (std::numeric_limits<double>::digits10 + 2);
|
||||
if (choice == tr("Binary"))
|
||||
CGAL::set_binary_mode(out);
|
||||
else
|
||||
out.precision (std::numeric_limits<double>::digits10 + 2);
|
||||
|
||||
// This plugin supports point sets
|
||||
const Scene_points_with_normal_item* point_set_item =
|
||||
|
|
@ -263,13 +191,13 @@ bool Polyhedron_demo_ply_plugin::save(const CGAL::Three::Scene_item* item, QFile
|
|||
const Scene_surface_mesh_item* sm_item =
|
||||
qobject_cast<const Scene_surface_mesh_item*>(item);
|
||||
if (sm_item)
|
||||
return CGAL::write_PLY (out, *(sm_item->polyhedron()));
|
||||
return CGAL::write_ply (out, *(sm_item->polyhedron()), sm_item->comments());
|
||||
|
||||
// This plugin supports textured surface meshes
|
||||
const Scene_textured_surface_mesh_item* stm_item =
|
||||
qobject_cast<const Scene_textured_surface_mesh_item*>(item);
|
||||
if (stm_item)
|
||||
return CGAL::write_PLY (out, *(stm_item->textured_face_graph()));
|
||||
return CGAL::write_ply (out, *(stm_item->textured_face_graph()));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -150,9 +150,14 @@ public :
|
|||
pen.setWidth(0);
|
||||
painter->setPen(pen);
|
||||
painter->setBrush(brush);
|
||||
SMesh::Property_map<halfedge_descriptor,std::pair<float, float> > uv;
|
||||
uv = graph->add_property_map<halfedge_descriptor,std::pair<float, float> >
|
||||
("h:uv",std::make_pair(0.0f,0.0f)).first;
|
||||
SMesh::Property_map<halfedge_descriptor, float> u;
|
||||
SMesh::Property_map<halfedge_descriptor, float> v;
|
||||
|
||||
u = graph->add_property_map<halfedge_descriptor, float>
|
||||
("h:u", 0.0f).first;
|
||||
v = graph->add_property_map<halfedge_descriptor, float>
|
||||
("h:v", 0.0f).first;
|
||||
|
||||
for( Component::iterator
|
||||
fi = component->begin();
|
||||
fi != component->end();
|
||||
|
|
@ -161,11 +166,11 @@ public :
|
|||
boost::graph_traits<SMesh>::face_descriptor f(*fi);
|
||||
QPointF points[3];
|
||||
boost::graph_traits<SMesh>::halfedge_descriptor h = halfedge(f, *graph);;
|
||||
points[0] = QPointF(get(uv, h).first, -get(uv, h).second);
|
||||
points[0] = QPointF(get(u, h), -get(v, h));
|
||||
h = next(halfedge(f, *graph), *graph);
|
||||
points[1] = QPointF(get(uv, h).first, -get(uv, h).second);
|
||||
points[1] = QPointF(get(u, h), -get(v, h));
|
||||
h = next(next(halfedge(f, *graph), *graph), *graph);
|
||||
points[2] = QPointF(get(uv, h).first, -get(uv, h).second);
|
||||
points[2] = QPointF(get(u, h), -get(v, h));
|
||||
painter->drawPolygon(points,3);
|
||||
}
|
||||
|
||||
|
|
@ -605,9 +610,12 @@ public Q_SLOTS:
|
|||
{
|
||||
component->insert(*bfit);
|
||||
}
|
||||
SMesh::Property_map<halfedge_descriptor,std::pair<float, float> > uv;
|
||||
uv = sm->add_property_map<halfedge_descriptor,std::pair<float, float> >(
|
||||
"h:uv",std::make_pair(0.0f,0.0f)).first;
|
||||
SMesh::Property_map<halfedge_descriptor, float> umap;
|
||||
SMesh::Property_map<halfedge_descriptor, float> vmap;
|
||||
umap = sm->add_property_map<halfedge_descriptor, float>
|
||||
("h:u", 0.0f).first;
|
||||
vmap = sm->add_property_map<halfedge_descriptor, float>
|
||||
("h:v", 0.0f).first;
|
||||
SMesh::Halfedge_iterator it;
|
||||
SMesh::Property_map<SMesh::Vertex_index, EPICK::Point_2> uv_map =
|
||||
sm->property_map<SMesh::Vertex_index, EPICK::Point_2>("v:uv").first;
|
||||
|
|
@ -618,7 +626,8 @@ public Q_SLOTS:
|
|||
halfedge_descriptor hd(*it);
|
||||
EPICK::FT u = uv_map[target(hd, *sm)].x();
|
||||
EPICK::FT v = uv_map[target(hd, *sm)].y();
|
||||
put(uv, *it, std::make_pair(static_cast<float>(u),static_cast<float>(v)));
|
||||
put(umap, *it, static_cast<float>(u));
|
||||
put(vmap, *it, static_cast<float>(v));
|
||||
}
|
||||
|
||||
//ParamItem does not take ownership of text_mesh_bottom
|
||||
|
|
|
|||
|
|
@ -265,8 +265,11 @@ public :
|
|||
pen.setWidth(0);
|
||||
painter->setPen(pen);
|
||||
painter->setBrush(brush);
|
||||
SMesh::Property_map<halfedge_descriptor,std::pair<float, float> > uv;
|
||||
uv = graph->add_property_map<halfedge_descriptor,std::pair<float, float> >("h:uv",std::make_pair(0.0f,0.0f)).first;
|
||||
SMesh::Property_map<halfedge_descriptor,float> u,v;
|
||||
|
||||
u = graph->add_property_map<halfedge_descriptor,float>("h:u", 0.0f).first;
|
||||
v = graph->add_property_map<halfedge_descriptor,float>("h:v", 0.0f).first;
|
||||
|
||||
for( Component::iterator
|
||||
fi = components->at(m_current_component).begin();
|
||||
fi != components->at(m_current_component).end();
|
||||
|
|
@ -276,11 +279,11 @@ uv = graph->add_property_map<halfedge_descriptor,std::pair<float, float> >("h:uv
|
|||
|
||||
QPointF points[3];
|
||||
boost::graph_traits<Base_face_graph>::halfedge_descriptor h = halfedge(f, *graph);;
|
||||
points[0] = QPointF(get(uv, h).first, get(uv, h).second);
|
||||
points[0] = QPointF(get(u, h), get(v, h));
|
||||
h = next(halfedge(f, *graph), *graph);
|
||||
points[1] = QPointF(get(uv, h).first, get(uv, h).second);
|
||||
points[1] = QPointF(get(u, h), get(v, h));
|
||||
h = next(next(halfedge(f, *graph), *graph), *graph);
|
||||
points[2] = QPointF(get(uv, h).first, get(uv, h).second);
|
||||
points[2] = QPointF(get(u, h), get(v, h));
|
||||
painter->drawPolygon(points,3);
|
||||
}
|
||||
}
|
||||
|
|
@ -906,8 +909,13 @@ void Polyhedron_demo_parameterization_plugin::parameterize(const Parameterizatio
|
|||
QApplication::restoreOverrideCursor();
|
||||
QPointF min(FLT_MAX, FLT_MAX), max(-FLT_MAX, -FLT_MAX);
|
||||
|
||||
SMesh::Property_map<halfedge_descriptor,std::pair<float, float> > uv;
|
||||
uv = tMesh.add_property_map<halfedge_descriptor,std::pair<float, float> >("h:uv",std::make_pair(0.0f,0.0f)).first;
|
||||
SMesh::Property_map<halfedge_descriptor, float> umap;
|
||||
SMesh::Property_map<halfedge_descriptor, float> vmap;
|
||||
|
||||
umap = tMesh.add_property_map<halfedge_descriptor, float>("h:u", 0.0f).first;
|
||||
vmap = tMesh.add_property_map<halfedge_descriptor, float>("h:v", 0.0f).first;
|
||||
|
||||
tMesh.property_stats(std::cerr);
|
||||
Base_face_graph::Halfedge_iterator it;
|
||||
for(it = tMesh.halfedges_begin();
|
||||
it != tMesh.halfedges_end();
|
||||
|
|
@ -916,7 +924,8 @@ void Polyhedron_demo_parameterization_plugin::parameterize(const Parameterizatio
|
|||
Seam_mesh::halfedge_descriptor hd(*it);
|
||||
FT u = uv_pm[target(hd, sMesh)].x();
|
||||
FT v = uv_pm[target(hd, sMesh)].y();
|
||||
put(uv, *it, std::make_pair(static_cast<float>(u),static_cast<float>(v)));
|
||||
put(umap, *it, static_cast<float>(u));
|
||||
put(vmap, *it, static_cast<float>(v));
|
||||
if(u<min.x())
|
||||
min.setX(u);
|
||||
if(u>max.x())
|
||||
|
|
|
|||
|
|
@ -593,7 +593,7 @@ bool Scene_points_with_normal_item::read_ply_point_set(std::istream& stream)
|
|||
d->m_points->clear();
|
||||
|
||||
bool ok = stream &&
|
||||
CGAL::read_ply_point_set (stream, *(d->m_points), &(d->m_comments)) &&
|
||||
CGAL::read_ply_point_set (stream, *(d->m_points), d->m_comments) &&
|
||||
!isEmpty();
|
||||
d->point_Slider->setValue(CGAL::Three::Three::getDefaultPointSize());
|
||||
std::cerr << d->m_points->info();
|
||||
|
|
@ -627,7 +627,7 @@ bool Scene_points_with_normal_item::write_ply_point_set(std::ostream& stream, bo
|
|||
if (binary)
|
||||
CGAL::set_binary_mode (stream);
|
||||
|
||||
CGAL::write_ply_point_set (stream, *(d->m_points), &(d->m_comments));
|
||||
CGAL::write_ply_point_set (stream, *(d->m_points), d->m_comments);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -211,6 +211,8 @@ struct Scene_surface_mesh_item_priv{
|
|||
mutable QList<double> text_ids;
|
||||
mutable std::vector<TextItem*> targeted_id;
|
||||
|
||||
std::string comments;
|
||||
|
||||
mutable bool has_fpatch_id;
|
||||
mutable bool has_feature_edges;
|
||||
mutable bool floated;
|
||||
|
|
@ -1051,6 +1053,9 @@ Scene_surface_mesh_item::~Scene_surface_mesh_item()
|
|||
SMesh* Scene_surface_mesh_item::polyhedron() { return d->smesh_; }
|
||||
const SMesh* Scene_surface_mesh_item::polyhedron() const { return d->smesh_; }
|
||||
|
||||
std::string& Scene_surface_mesh_item::comments() { return d->comments; }
|
||||
const std::string& Scene_surface_mesh_item::comments() const { return d->comments; }
|
||||
|
||||
void Scene_surface_mesh_item::compute_bbox()const
|
||||
{
|
||||
SMesh::Property_map<vertex_descriptor, Point_3> pprop = d->smesh_->points();
|
||||
|
|
|
|||
|
|
@ -89,6 +89,10 @@ public:
|
|||
Face_graph* face_graph() { return polyhedron(); }
|
||||
const Face_graph* face_graph() const { return polyhedron(); }
|
||||
|
||||
// Gets PLY comments (empty if mesh not originated from PLY input)
|
||||
std::string& comments();
|
||||
const std::string& comments() const;
|
||||
|
||||
void invalidate_aabb_tree();
|
||||
void invalidateOpenGLBuffers()Q_DECL_OVERRIDE;
|
||||
void invalidate(Gl_data_names name);
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ struct Scene_textured_surface_mesh_item_priv
|
|||
{
|
||||
item = parent;
|
||||
texture.GenerateCheckerBoard(2048,2048,128,0,0,0,250,250,255);
|
||||
uv = sm->add_property_map<halfedge_descriptor,std::pair<float, float> >("h:uv",std::make_pair(0.0f,0.0f)).first;
|
||||
|
||||
umap = sm->add_property_map<halfedge_descriptor, float>("h:u", 0.0f).first;
|
||||
vmap = sm->add_property_map<halfedge_descriptor, float>("h:v", 0.0f).first;
|
||||
}
|
||||
Scene_textured_surface_mesh_item_priv(const SMesh& p, Scene_textured_surface_mesh_item* parent)
|
||||
: sm(new SMesh(p))
|
||||
|
|
@ -30,14 +30,16 @@ struct Scene_textured_surface_mesh_item_priv
|
|||
{
|
||||
item = parent;
|
||||
texture.GenerateCheckerBoard(2048,2048,128,0,0,0,250,250,255);
|
||||
uv = sm->add_property_map<halfedge_descriptor,std::pair<float, float> >("h:uv",std::make_pair(0.0f,0.0f)).first;
|
||||
umap = sm->add_property_map<halfedge_descriptor, float>("h:u", 0.0f).first;
|
||||
vmap = sm->add_property_map<halfedge_descriptor, float>("h:v", 0.0f).first;
|
||||
}
|
||||
Scene_textured_surface_mesh_item_priv(SMesh* const p,Scene_textured_surface_mesh_item* parent)
|
||||
:sm(p)
|
||||
{
|
||||
item = parent;
|
||||
texture.GenerateCheckerBoard(2048,2048,128,0,0,0,250,250,255);
|
||||
uv = sm->add_property_map<halfedge_descriptor,std::pair<float, float> >("h:uv",std::make_pair(0.0f,0.0f)).first;
|
||||
umap = sm->add_property_map<halfedge_descriptor, float>("h:u", 0.0f).first;
|
||||
vmap = sm->add_property_map<halfedge_descriptor, float>("h:v", 0.0f).first;
|
||||
}
|
||||
|
||||
~Scene_textured_surface_mesh_item_priv()
|
||||
|
|
@ -49,7 +51,8 @@ struct Scene_textured_surface_mesh_item_priv
|
|||
|
||||
SMesh* sm;
|
||||
::Texture texture;
|
||||
SMesh::Property_map<halfedge_descriptor,std::pair<float, float> > uv;
|
||||
SMesh::Property_map<halfedge_descriptor, float> umap;
|
||||
SMesh::Property_map<halfedge_descriptor, float> vmap;
|
||||
|
||||
//[Px][Py][Pz][Nx][Ny][Nz][u][v]
|
||||
mutable std::vector<float> faces_buffer;
|
||||
|
|
@ -102,8 +105,8 @@ Scene_textured_surface_mesh_item_priv::compute_normals_and_vertices(void) const
|
|||
faces_buffer.push_back(n[1]);
|
||||
faces_buffer.push_back(n[2]);
|
||||
//uvs [2]
|
||||
const float u = get(uv, *he).first;
|
||||
const float v = get(uv, *he).second;
|
||||
const float u = get(umap, *he);
|
||||
const float v = get(vmap, *he);
|
||||
faces_buffer.push_back(u);
|
||||
faces_buffer.push_back(v);
|
||||
}
|
||||
|
|
@ -127,8 +130,8 @@ Scene_textured_surface_mesh_item_priv::compute_normals_and_vertices(void) const
|
|||
edges_buffer.push_back(a.y() + offset.y);
|
||||
edges_buffer.push_back(a.z() + offset.z);
|
||||
//uvs [2]
|
||||
float u = get(uv, halfedge(*he, *sm)).first;
|
||||
float v = get(uv, halfedge(*he, *sm)).second;
|
||||
float u = get(umap, halfedge(*he, *sm));
|
||||
float v = get(vmap, halfedge(*he, *sm));
|
||||
|
||||
edges_buffer.push_back(u);
|
||||
edges_buffer.push_back(v);
|
||||
|
|
@ -138,8 +141,8 @@ Scene_textured_surface_mesh_item_priv::compute_normals_and_vertices(void) const
|
|||
edges_buffer.push_back(b.z() + offset.z);
|
||||
|
||||
//uvs [2]
|
||||
u = get(uv, opposite(halfedge(*he, *sm), *sm)).first;
|
||||
v = get(uv, opposite(halfedge(*he, *sm), *sm)).second;
|
||||
u = get(umap, opposite(halfedge(*he, *sm), *sm));
|
||||
v = get(vmap, opposite(halfedge(*he, *sm), *sm));
|
||||
|
||||
edges_buffer.push_back(u);
|
||||
edges_buffer.push_back(v);
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
#ifndef CGAL_IO_PLY_READER_H
|
||||
#define CGAL_IO_PLY_READER_H
|
||||
|
||||
#include <CGAL/IO/read_ply_points.h>
|
||||
#include <CGAL/IO/PLY.h>
|
||||
|
||||
namespace CGAL{
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
#ifndef CGAL_IO_PLY_WRITER_H
|
||||
#define CGAL_IO_PLY_WRITER_H
|
||||
|
||||
#include <CGAL/IO/write_ply_points.h>
|
||||
#include <CGAL/IO/PLY.h>
|
||||
|
||||
namespace CGAL{
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,973 @@
|
|||
// Copyright (c) 2015 Geometry Factory
|
||||
// All rights reserved.
|
||||
//
|
||||
// This file is part of CGAL (www.cgal.org); you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public License as
|
||||
// published by the Free Software Foundation; either version 3 of the License,
|
||||
// or (at your option) any later version.
|
||||
//
|
||||
// Licensees holding a valid commercial license may use this file in
|
||||
// accordance with the commercial license agreement provided with the software.
|
||||
//
|
||||
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// $URL$
|
||||
// $Id$
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
//
|
||||
// Author(s) : Simon Giraudot
|
||||
|
||||
#ifndef CGAL_IO_PLY_H
|
||||
#define CGAL_IO_PLY_H
|
||||
|
||||
#include <CGAL/Kernel_traits.h>
|
||||
#include <CGAL/IO/io.h>
|
||||
|
||||
#include <tuple>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#define TRY_TO_GENERATE_PROPERTY(STD_TYPE, T_TYPE, TYPE) \
|
||||
if (type == STD_TYPE || type == T_TYPE) \
|
||||
m_elements.back().add_property (new PLY_read_typed_number< TYPE > (name, format))
|
||||
|
||||
#define TRY_TO_GENERATE_SIZED_LIST_PROPERTY(STD_SIZE_TYPE, T_SIZE_TYPE, SIZE_TYPE, STD_INDEX_TYPE, T_INDEX_TYPE, INDEX_TYPE) \
|
||||
if ((size_type == STD_SIZE_TYPE || size_type == T_SIZE_TYPE) && \
|
||||
(index_type == STD_INDEX_TYPE || index_type == T_INDEX_TYPE)) \
|
||||
m_elements.back().add_property (new PLY_read_typed_list_with_typed_size< SIZE_TYPE , INDEX_TYPE > (name, format))
|
||||
|
||||
#define TRY_TO_GENERATE_LIST_PROPERTY(STD_INDEX_TYPE, T_INDEX_TYPE, INDEX_TYPE) \
|
||||
TRY_TO_GENERATE_SIZED_LIST_PROPERTY("uchar", "uint8", boost::uint8_t, STD_INDEX_TYPE, T_INDEX_TYPE, INDEX_TYPE); \
|
||||
else TRY_TO_GENERATE_SIZED_LIST_PROPERTY("ushort", "uint16", boost::uint16_t, STD_INDEX_TYPE, T_INDEX_TYPE, INDEX_TYPE); \
|
||||
else TRY_TO_GENERATE_SIZED_LIST_PROPERTY("uint", "uint32", boost::uint32_t, STD_INDEX_TYPE, T_INDEX_TYPE, INDEX_TYPE)
|
||||
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
// PLY types:
|
||||
// name type number of bytes
|
||||
// ---------------------------------------
|
||||
// char character 1
|
||||
// uchar unsigned character 1
|
||||
// short short integer 2
|
||||
// ushort unsigned short integer 2
|
||||
// int integer 4
|
||||
// uint unsigned integer 4
|
||||
// float single-precision float 4
|
||||
// double double-precision float 8
|
||||
|
||||
template <typename T>
|
||||
struct PLY_property
|
||||
{
|
||||
typedef T type;
|
||||
const char* name;
|
||||
PLY_property (const char* name) : name (name) { }
|
||||
};
|
||||
|
||||
// Use a double property for all kernels...
|
||||
template <typename FT> struct Convert_FT { typedef double type; };
|
||||
// ...except if kernel uses type float
|
||||
template <> struct Convert_FT<float> { typedef float type; };
|
||||
|
||||
template <typename PointOrVectorMap>
|
||||
struct Get_FT_from_map
|
||||
{
|
||||
typedef typename Convert_FT
|
||||
<typename Kernel_traits
|
||||
<typename boost::property_traits
|
||||
<PointOrVectorMap>::value_type>::Kernel::FT>::type type;
|
||||
};
|
||||
|
||||
template <typename PointMap>
|
||||
std::tuple<PointMap,
|
||||
typename Kernel_traits<typename PointMap::value_type>::Kernel::Construct_point_3,
|
||||
PLY_property<typename Get_FT_from_map<PointMap>::type>,
|
||||
PLY_property<typename Get_FT_from_map<PointMap>::type>,
|
||||
PLY_property<typename Get_FT_from_map<PointMap>::type> >
|
||||
make_ply_point_reader(PointMap point_map)
|
||||
{
|
||||
return std::make_tuple (point_map, typename Kernel_traits<typename PointMap::value_type>::Kernel::Construct_point_3(),
|
||||
PLY_property<typename Get_FT_from_map<PointMap>::type>("x"),
|
||||
PLY_property<typename Get_FT_from_map<PointMap>::type>("y"),
|
||||
PLY_property<typename Get_FT_from_map<PointMap>::type>("z"));
|
||||
}
|
||||
|
||||
template <typename VectorMap>
|
||||
std::tuple<VectorMap,
|
||||
typename Kernel_traits<typename VectorMap::value_type>::Kernel::Construct_vector_3,
|
||||
PLY_property<typename Get_FT_from_map<VectorMap>::type>,
|
||||
PLY_property<typename Get_FT_from_map<VectorMap>::type>,
|
||||
PLY_property<typename Get_FT_from_map<VectorMap>::type> >
|
||||
make_ply_normal_reader(VectorMap normal_map)
|
||||
{
|
||||
return std::make_tuple (normal_map, typename Kernel_traits<typename VectorMap::value_type>::Kernel::Construct_vector_3(),
|
||||
PLY_property<typename Get_FT_from_map<VectorMap>::type>("nx"),
|
||||
PLY_property<typename Get_FT_from_map<VectorMap>::type>("ny"),
|
||||
PLY_property<typename Get_FT_from_map<VectorMap>::type>("nz"));
|
||||
}
|
||||
|
||||
template <typename PointMap>
|
||||
std::tuple<PointMap,
|
||||
PLY_property<typename Get_FT_from_map<PointMap>::type>,
|
||||
PLY_property<typename Get_FT_from_map<PointMap>::type>,
|
||||
PLY_property<typename Get_FT_from_map<PointMap>::type> >
|
||||
make_ply_point_writer(PointMap point_map)
|
||||
{
|
||||
return std::make_tuple (point_map,
|
||||
PLY_property<typename Get_FT_from_map<PointMap>::type>("x"),
|
||||
PLY_property<typename Get_FT_from_map<PointMap>::type>("y"),
|
||||
PLY_property<typename Get_FT_from_map<PointMap>::type>("z"));
|
||||
}
|
||||
|
||||
template <typename VectorMap>
|
||||
std::tuple<VectorMap,
|
||||
PLY_property<typename Get_FT_from_map<VectorMap>::type>,
|
||||
PLY_property<typename Get_FT_from_map<VectorMap>::type>,
|
||||
PLY_property<typename Get_FT_from_map<VectorMap>::type> >
|
||||
make_ply_normal_writer(VectorMap normal_map)
|
||||
{
|
||||
return std::make_tuple (normal_map,
|
||||
PLY_property<typename Get_FT_from_map<VectorMap>::type>("nx"),
|
||||
PLY_property<typename Get_FT_from_map<VectorMap>::type>("ny"),
|
||||
PLY_property<typename Get_FT_from_map<VectorMap>::type>("nz"));
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
namespace PLY {
|
||||
|
||||
class PLY_read_number
|
||||
{
|
||||
protected:
|
||||
std::string m_name;
|
||||
std::size_t m_format;
|
||||
|
||||
public:
|
||||
PLY_read_number (std::string name, std::size_t format)
|
||||
: m_name (name), m_format (format) { }
|
||||
virtual ~PLY_read_number() { }
|
||||
|
||||
const std::string& name () const { return m_name; }
|
||||
|
||||
virtual void get (std::istream& stream) const = 0;
|
||||
|
||||
// The two following functions prevent the stream to only extract
|
||||
// ONE character (= what the types char imply) by requiring
|
||||
// explicitely an integer object when reading the stream
|
||||
void read_ascii (std::istream& stream, char& c) const
|
||||
{
|
||||
short s;
|
||||
stream >> s;
|
||||
c = static_cast<char>(s);
|
||||
}
|
||||
void read_ascii (std::istream& stream, signed char& c) const
|
||||
{
|
||||
short s;
|
||||
stream >> s;
|
||||
c = static_cast<signed char>(s);
|
||||
}
|
||||
void read_ascii (std::istream& stream, unsigned char& c) const
|
||||
{
|
||||
unsigned short s;
|
||||
stream >> s;
|
||||
c = static_cast<unsigned char>(s);
|
||||
}
|
||||
|
||||
void read_ascii (std::istream& stream, float& t) const
|
||||
{
|
||||
stream >> iformat(t);
|
||||
}
|
||||
|
||||
void read_ascii (std::istream& stream, double& t) const
|
||||
{
|
||||
stream >> iformat(t);
|
||||
}
|
||||
|
||||
// Default template when Type is not a char type
|
||||
template <typename Type>
|
||||
void read_ascii (std::istream& stream, Type& t) const
|
||||
{
|
||||
stream >> t;
|
||||
}
|
||||
|
||||
|
||||
template <typename Type>
|
||||
Type read (std::istream& stream) const
|
||||
{
|
||||
if (m_format == 0) // Ascii
|
||||
{
|
||||
Type t;
|
||||
read_ascii (stream, t);
|
||||
return t;
|
||||
}
|
||||
else // Binary (2 = little endian)
|
||||
{
|
||||
union
|
||||
{
|
||||
char uChar[sizeof (Type)];
|
||||
Type type;
|
||||
} buffer;
|
||||
|
||||
std::size_t size = sizeof (Type);
|
||||
|
||||
stream.read(buffer.uChar, size);
|
||||
|
||||
if (m_format == 2) // Big endian
|
||||
{
|
||||
for (std::size_t i = 0; i < size / 2; ++ i)
|
||||
{
|
||||
unsigned char tmp = buffer.uChar[i];
|
||||
buffer.uChar[i] = buffer.uChar[size - 1 - i];
|
||||
buffer.uChar[size - 1 - i] = tmp;
|
||||
}
|
||||
}
|
||||
return buffer.type;
|
||||
}
|
||||
return Type();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
class PLY_read_typed_number : public PLY_read_number
|
||||
{
|
||||
mutable Type m_buffer;
|
||||
public:
|
||||
PLY_read_typed_number (std::string name, std::size_t format)
|
||||
: PLY_read_number (name, format)
|
||||
{
|
||||
}
|
||||
void get (std::istream& stream) const
|
||||
{
|
||||
m_buffer = (this->read<Type> (stream));
|
||||
}
|
||||
const Type& buffer() const
|
||||
{
|
||||
return m_buffer;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
class PLY_read_typed_list : public PLY_read_number
|
||||
{
|
||||
protected:
|
||||
mutable std::vector<Type> m_buffer;
|
||||
public:
|
||||
PLY_read_typed_list (std::string name, std::size_t format)
|
||||
: PLY_read_number (name, format)
|
||||
{
|
||||
}
|
||||
virtual void get (std::istream& stream) const = 0;
|
||||
|
||||
const std::vector<Type>& buffer() const
|
||||
{
|
||||
return m_buffer;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename SizeType, typename IndexType>
|
||||
class PLY_read_typed_list_with_typed_size
|
||||
: public PLY_read_typed_list<IndexType>
|
||||
{
|
||||
|
||||
public:
|
||||
PLY_read_typed_list_with_typed_size (std::string name, std::size_t format)
|
||||
: PLY_read_typed_list<IndexType> (name, format)
|
||||
{
|
||||
}
|
||||
void get (std::istream& stream) const
|
||||
{
|
||||
std::size_t size = static_cast<std::size_t>(this->template read<SizeType>(stream));
|
||||
this->m_buffer.resize (size);
|
||||
for (std::size_t i = 0; i < size; ++ i)
|
||||
this->m_buffer[i] = this->template read<IndexType> (stream);
|
||||
}
|
||||
};
|
||||
|
||||
class PLY_element
|
||||
{
|
||||
std::string m_name;
|
||||
std::size_t m_number;
|
||||
|
||||
std::vector<PLY_read_number*> m_properties;
|
||||
public:
|
||||
|
||||
PLY_element (const std::string& name, std::size_t number)
|
||||
: m_name (name), m_number (number)
|
||||
{ }
|
||||
|
||||
PLY_element (const PLY_element& other)
|
||||
: m_name (other.m_name), m_number (other.m_number), m_properties (other.m_properties)
|
||||
{
|
||||
const_cast<PLY_element&>(other).m_properties.clear();
|
||||
}
|
||||
|
||||
PLY_element& operator= (const PLY_element& other)
|
||||
{
|
||||
m_name = other.m_name;
|
||||
m_number = other.m_number;
|
||||
m_properties = other.m_properties;
|
||||
const_cast<PLY_element&>(other).m_properties.clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
~PLY_element()
|
||||
{
|
||||
for (std::size_t i = 0; i < m_properties.size(); ++ i)
|
||||
delete m_properties[i];
|
||||
}
|
||||
|
||||
const std::string& name() const { return m_name; }
|
||||
std::size_t number_of_items() const { return m_number; }
|
||||
std::size_t number_of_properties() const { return m_properties.size(); }
|
||||
|
||||
PLY_read_number* property (std::size_t idx) { return m_properties[idx]; }
|
||||
|
||||
void add_property (PLY_read_number* read_number)
|
||||
{
|
||||
m_properties.push_back (read_number);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
bool has_property (const char* tag)
|
||||
{
|
||||
return has_property (tag, Type());
|
||||
}
|
||||
template <typename Type>
|
||||
bool has_property (const char* tag, const std::vector<Type>&)
|
||||
{
|
||||
for (std::size_t i = 0; i < number_of_properties(); ++ i)
|
||||
if (m_properties[i]->name () == tag)
|
||||
return (dynamic_cast<PLY_read_typed_list<Type>*>(m_properties[i]) != NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
bool has_property (const char* tag, Type)
|
||||
{
|
||||
for (std::size_t i = 0; i < number_of_properties(); ++ i)
|
||||
if (m_properties[i]->name () == tag)
|
||||
return (dynamic_cast<PLY_read_typed_number<Type>*>(m_properties[i]) != NULL);
|
||||
return false;
|
||||
}
|
||||
bool has_property (const char* tag, double)
|
||||
{
|
||||
for (std::size_t i = 0; i < number_of_properties(); ++ i)
|
||||
if (m_properties[i]->name () == tag)
|
||||
return (dynamic_cast<PLY_read_typed_number<double>*>(m_properties[i]) != NULL
|
||||
|| dynamic_cast<PLY_read_typed_number<float>*>(m_properties[i]) != NULL);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void assign (Type& t, const char* tag)
|
||||
{
|
||||
for (std::size_t i = 0; i < number_of_properties (); ++ i)
|
||||
if (m_properties[i]->name () == tag)
|
||||
{
|
||||
PLY_read_typed_number<Type>*
|
||||
property = dynamic_cast<PLY_read_typed_number<Type>*>(m_properties[i]);
|
||||
CGAL_assertion (property != NULL);
|
||||
t = property->buffer();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void assign (std::vector<Type>& t, const char* tag)
|
||||
{
|
||||
for (std::size_t i = 0; i < number_of_properties (); ++ i)
|
||||
if (m_properties[i]->name () == tag)
|
||||
{
|
||||
PLY_read_typed_list<Type>*
|
||||
property = dynamic_cast<PLY_read_typed_list<Type>*>(m_properties[i]);
|
||||
CGAL_assertion (property != NULL);
|
||||
t = property->buffer();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void assign (double& t, const char* tag)
|
||||
{
|
||||
for (std::size_t i = 0; i < number_of_properties (); ++ i)
|
||||
if (m_properties[i]->name () == tag)
|
||||
{
|
||||
PLY_read_typed_number<double>*
|
||||
property_double = dynamic_cast<PLY_read_typed_number<double>*>(m_properties[i]);
|
||||
if (property_double == NULL)
|
||||
{
|
||||
PLY_read_typed_number<float>*
|
||||
property_float = dynamic_cast<PLY_read_typed_number<float>*>(m_properties[i]);
|
||||
CGAL_assertion (property_float != NULL);
|
||||
t = property_float->buffer();
|
||||
}
|
||||
else
|
||||
t = property_double->buffer();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class PLY_reader
|
||||
{
|
||||
std::vector<PLY_element> m_elements;
|
||||
std::string m_comments;
|
||||
|
||||
public:
|
||||
PLY_reader () { }
|
||||
|
||||
std::size_t number_of_elements() const { return m_elements.size(); }
|
||||
PLY_element& element (std::size_t idx)
|
||||
{
|
||||
return m_elements[idx];
|
||||
}
|
||||
|
||||
const std::string& comments() const { return m_comments; }
|
||||
|
||||
template <typename Stream>
|
||||
bool init (Stream& stream)
|
||||
{
|
||||
std::size_t lineNumber = 0; // current line number
|
||||
enum Format { ASCII = 0, BINARY_LITTLE_ENDIAN = 1, BINARY_BIG_ENDIAN = 2};
|
||||
Format format = ASCII;
|
||||
|
||||
std::string line;
|
||||
std::istringstream iss;
|
||||
|
||||
while (getline (stream,line))
|
||||
{
|
||||
iss.clear();
|
||||
iss.str (line);
|
||||
++ lineNumber;
|
||||
|
||||
// Reads file signature on first line
|
||||
if (lineNumber == 1)
|
||||
{
|
||||
std::string signature;
|
||||
if (!(iss >> signature) || (signature != "ply"))
|
||||
{
|
||||
// if wrong file format
|
||||
std::cerr << "Error: incorrect file format line " << lineNumber << " of file" << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Reads format on 2nd line
|
||||
else if (lineNumber == 2)
|
||||
{
|
||||
std::string tag, format_string, version;
|
||||
if ( !(iss >> tag >> format_string >> version) )
|
||||
{
|
||||
std::cerr << "Error line " << lineNumber << " of file" << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (format_string == "ascii") format = ASCII;
|
||||
else if (format_string == "binary_little_endian") format = BINARY_LITTLE_ENDIAN;
|
||||
else if (format_string == "binary_big_endian") format = BINARY_BIG_ENDIAN;
|
||||
else
|
||||
{
|
||||
std::cerr << "Error: unknown file format \"" << format_string << "\" line " << lineNumber << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Comments and vertex properties
|
||||
else
|
||||
{
|
||||
std::string keyword;
|
||||
if (!(iss >> keyword))
|
||||
{
|
||||
std::cerr << "Error line " << lineNumber << " of file" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (keyword == "property")
|
||||
{
|
||||
std::string type, name;
|
||||
if (!(iss >> type >> name))
|
||||
{
|
||||
std::cerr << "Error line " << lineNumber << " of file" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (type == "list") // Special case
|
||||
{
|
||||
std::string size_type = name;
|
||||
std::string index_type;
|
||||
name.clear();
|
||||
if (!(iss >> index_type >> name))
|
||||
{
|
||||
std::cerr << "Error line " << lineNumber << " of file" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
TRY_TO_GENERATE_LIST_PROPERTY ("char", "int8", boost::int8_t);
|
||||
else TRY_TO_GENERATE_LIST_PROPERTY ("uchar", "uint8", boost::uint8_t);
|
||||
else TRY_TO_GENERATE_LIST_PROPERTY ("short", "int16", boost::int16_t);
|
||||
else TRY_TO_GENERATE_LIST_PROPERTY ("ushort", "uint16", boost::uint16_t);
|
||||
else TRY_TO_GENERATE_LIST_PROPERTY ("int", "int32", boost::int32_t);
|
||||
else TRY_TO_GENERATE_LIST_PROPERTY ("uint", "uint32", boost::uint32_t);
|
||||
else TRY_TO_GENERATE_LIST_PROPERTY ("float", "float32", float);
|
||||
else TRY_TO_GENERATE_LIST_PROPERTY ("double", "float64", double);
|
||||
}
|
||||
else
|
||||
{
|
||||
TRY_TO_GENERATE_PROPERTY ("char", "int8", boost::int8_t);
|
||||
else TRY_TO_GENERATE_PROPERTY ("uchar", "uint8", boost::uint8_t);
|
||||
else TRY_TO_GENERATE_PROPERTY ("short", "int16", boost::int16_t);
|
||||
else TRY_TO_GENERATE_PROPERTY ("ushort", "uint16", boost::uint16_t);
|
||||
else TRY_TO_GENERATE_PROPERTY ("int", "int32", boost::int32_t);
|
||||
else TRY_TO_GENERATE_PROPERTY ("uint", "uint32", boost::uint32_t);
|
||||
else TRY_TO_GENERATE_PROPERTY ("float", "float32", float);
|
||||
else TRY_TO_GENERATE_PROPERTY ("double", "float64", double);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
else if (keyword == "comment")
|
||||
{
|
||||
std::string str = iss.str();
|
||||
if (str.size() > 8)
|
||||
{
|
||||
std::copy (str.begin() + 8, str.end(), std::back_inserter (m_comments));
|
||||
m_comments += "\n";
|
||||
}
|
||||
}
|
||||
else if (keyword == "element")
|
||||
{
|
||||
std::string type;
|
||||
std::size_t number;
|
||||
if (!(iss >> type >> number))
|
||||
{
|
||||
std::cerr << "Error line " << lineNumber << " of file" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_elements.push_back (PLY_element(type, number));
|
||||
}
|
||||
// When end_header is reached, stop loop and begin reading points
|
||||
else if (keyword == "end_header")
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
~PLY_reader ()
|
||||
{
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <class Reader, class T>
|
||||
void get_value(Reader& r, T& v, PLY_property<T>& wrapper)
|
||||
{
|
||||
return r.assign(v, wrapper.name);
|
||||
}
|
||||
|
||||
|
||||
template <std::size_t N>
|
||||
struct Filler
|
||||
{
|
||||
template <class Reader, class Value_tuple, class PLY_property_tuple>
|
||||
static void fill(Reader& r, Value_tuple& values, PLY_property_tuple wrappers)
|
||||
{
|
||||
get_value(r, std::get<N>(values), std::get<N+2>(wrappers));
|
||||
Filler<N-1>::fill(r, values, wrappers);
|
||||
}
|
||||
};
|
||||
|
||||
template<int ...>
|
||||
struct seq { };
|
||||
|
||||
template<int N, int ...S>
|
||||
struct gens : gens<N-1, N-1, S...> { };
|
||||
|
||||
template<int ...S>
|
||||
struct gens<0, S...> {
|
||||
typedef seq<S...> type;
|
||||
};
|
||||
|
||||
template<class ValueType, class Functor, class Tuple, int ...S>
|
||||
ValueType call_functor(Functor f, Tuple t, seq<S...>) {
|
||||
return f(std::get<S>(t) ...);
|
||||
}
|
||||
|
||||
template <class ValueType, class Functor, typename ... T>
|
||||
ValueType call_functor(Functor f, std::tuple<T...>& t)
|
||||
{
|
||||
return call_functor<ValueType>(f, t, typename gens<sizeof...(T)>::type());
|
||||
}
|
||||
|
||||
template<>
|
||||
struct Filler<0>
|
||||
{
|
||||
template <class Reader, class Value_tuple, class PLY_property_tuple>
|
||||
static void fill(Reader& r, Value_tuple& values, PLY_property_tuple wrappers)
|
||||
{
|
||||
get_value(r, std::get<0>(values), std::get<2>(wrappers));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename OutputValueType,
|
||||
typename PropertyMap,
|
||||
typename Constructor,
|
||||
typename ... T>
|
||||
void process_properties (PLY_element& element, OutputValueType& new_element,
|
||||
std::tuple<PropertyMap, Constructor, PLY_property<T>...>&& current)
|
||||
{
|
||||
typedef typename PropertyMap::value_type PmapValueType;
|
||||
std::tuple<T...> values;
|
||||
Filler<sizeof...(T)-1>::fill(element, values, current);
|
||||
PmapValueType new_value = call_functor<PmapValueType>(std::get<1>(current), values);
|
||||
put (std::get<0>(current), new_element, new_value);
|
||||
}
|
||||
|
||||
template <typename OutputValueType,
|
||||
typename PropertyMap,
|
||||
typename Constructor,
|
||||
typename ... T,
|
||||
typename NextPropertyBinder,
|
||||
typename ... PropertyMapBinders>
|
||||
void process_properties (PLY_element& element, OutputValueType& new_element,
|
||||
std::tuple<PropertyMap, Constructor, PLY_property<T>...>&& current,
|
||||
NextPropertyBinder&& next,
|
||||
PropertyMapBinders&& ... properties)
|
||||
{
|
||||
typedef typename PropertyMap::value_type PmapValueType;
|
||||
std::tuple<T...> values;
|
||||
Filler<sizeof...(T)-1>::fill(element, values, current);
|
||||
PmapValueType new_value = call_functor<PmapValueType>(std::get<1>(current), values);
|
||||
put (std::get<0>(current), new_element, new_value);
|
||||
|
||||
process_properties (element, new_element, std::forward<NextPropertyBinder>(next),
|
||||
std::forward<PropertyMapBinders>(properties)...);
|
||||
}
|
||||
|
||||
|
||||
template <typename OutputValueType, typename PropertyMap, typename T>
|
||||
void process_properties (PLY_element& element, OutputValueType& new_element,
|
||||
std::pair<PropertyMap, PLY_property<T> >&& current)
|
||||
{
|
||||
T new_value = T();
|
||||
element.assign (new_value, current.second.name);
|
||||
put (current.first, new_element, new_value);
|
||||
}
|
||||
|
||||
template <typename OutputValueType, typename PropertyMap, typename T,
|
||||
typename NextPropertyBinder, typename ... PropertyMapBinders>
|
||||
void process_properties (PLY_element& element, OutputValueType& new_element,
|
||||
std::pair<PropertyMap, PLY_property<T> >&& current,
|
||||
NextPropertyBinder&& next,
|
||||
PropertyMapBinders&& ... properties)
|
||||
{
|
||||
T new_value = T();
|
||||
element.assign (new_value, current.second.name);
|
||||
put (current.first, new_element, new_value);
|
||||
process_properties (element, new_element, std::forward<NextPropertyBinder>(next),
|
||||
std::forward<PropertyMapBinders>(properties)...);
|
||||
}
|
||||
|
||||
template <typename T> inline void property_header_type (std::ostream& stream)
|
||||
{
|
||||
CGAL_assertion_msg (false, "Unknown PLY type");
|
||||
stream << "undefined_type";
|
||||
}
|
||||
|
||||
template <> inline void property_header_type<char> (std::ostream& stream) { stream << "char"; }
|
||||
template <> inline void property_header_type<signed char> (std::ostream& stream) { stream << "char"; }
|
||||
template <> inline void property_header_type<unsigned char> (std::ostream& stream) { stream << "uchar"; }
|
||||
template <> inline void property_header_type<short> (std::ostream& stream) { stream << "short"; }
|
||||
template <> inline void property_header_type<unsigned short> (std::ostream& stream) { stream << "ushort"; }
|
||||
template <> inline void property_header_type<int> (std::ostream& stream) { stream << "int"; }
|
||||
template <> inline void property_header_type<unsigned int> (std::ostream& stream) { stream << "uint"; }
|
||||
template <> inline void property_header_type<float> (std::ostream& stream) { stream << "float"; }
|
||||
template <> inline void property_header_type<double> (std::ostream& stream) { stream << "double"; }
|
||||
|
||||
template <typename T>
|
||||
void property_header (std::ostream& stream, const PLY_property<T>& prop)
|
||||
{
|
||||
stream << "property ";
|
||||
property_header_type<T>(stream);
|
||||
stream << " " << prop.name << std::endl;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void property_header (std::ostream& stream, const PLY_property<std::vector<T> >& prop)
|
||||
{
|
||||
stream << "property list uchar ";
|
||||
property_header_type<T>(stream);
|
||||
stream << " " << prop.name << std::endl;
|
||||
}
|
||||
|
||||
|
||||
template <std::size_t N>
|
||||
struct Properties_header
|
||||
{
|
||||
template <class PLY_property_tuple>
|
||||
static void write(std::ostream& stream, PLY_property_tuple& wrappers)
|
||||
{
|
||||
Properties_header<N-1>::write(stream, wrappers);
|
||||
property_header (stream, std::get<N+1>(wrappers));
|
||||
}
|
||||
};
|
||||
template <>
|
||||
struct Properties_header<0>
|
||||
{
|
||||
template <class PLY_property_tuple>
|
||||
static void write(std::ostream& stream, PLY_property_tuple& wrappers)
|
||||
{
|
||||
property_header (stream, std::get<1>(wrappers));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename PropertyMap,
|
||||
typename ... T>
|
||||
void output_property_header (std::ostream& stream,
|
||||
std::tuple<PropertyMap, PLY_property<T>... >&& current)
|
||||
{
|
||||
Properties_header<sizeof...(T)-1>::write(stream, current);
|
||||
}
|
||||
|
||||
|
||||
template <typename PropertyMap,
|
||||
typename T>
|
||||
void output_property_header (std::ostream& stream,
|
||||
std::pair<PropertyMap, PLY_property<T> >&& current)
|
||||
{
|
||||
property_header (stream, current.second);
|
||||
}
|
||||
|
||||
template <typename PropertyMap,
|
||||
typename T,
|
||||
typename NextPropertyHandler,
|
||||
typename ... PropertyHandler>
|
||||
void output_property_header (std::ostream& stream,
|
||||
std::pair<PropertyMap, PLY_property<T> >&& current,
|
||||
NextPropertyHandler&& next,
|
||||
PropertyHandler&& ... properties)
|
||||
{
|
||||
property_header (stream, current.second);
|
||||
output_property_header (stream, std::forward<NextPropertyHandler>(next),
|
||||
std::forward<PropertyHandler>(properties)...);
|
||||
}
|
||||
template <typename PropertyMap,
|
||||
typename ... T,
|
||||
typename NextPropertyHandler,
|
||||
typename ... PropertyHandler>
|
||||
void output_property_header (std::ostream& stream,
|
||||
std::tuple<PropertyMap, PLY_property<T>... >&& current,
|
||||
NextPropertyHandler&& next,
|
||||
PropertyHandler&& ... properties)
|
||||
{
|
||||
Properties_header<sizeof...(T)-1>::write(stream, current);
|
||||
output_property_header (stream, std::forward<NextPropertyHandler>(next),
|
||||
std::forward<PropertyHandler>(properties)...);
|
||||
}
|
||||
|
||||
|
||||
template <typename ForwardIterator,
|
||||
typename PropertyMap>
|
||||
void property_write (std::ostream& stream, ForwardIterator it, PropertyMap map)
|
||||
{
|
||||
stream << CGAL::oformat(get (map, *it));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T no_char_character (const T& t) { return t; }
|
||||
inline int no_char_character (const char& t) { return int(t); }
|
||||
inline int no_char_character (const signed char& t) { return int(t); }
|
||||
inline int no_char_character (const unsigned char& t) { return int(t); }
|
||||
|
||||
template <typename ForwardIterator,
|
||||
typename PropertyMap,
|
||||
typename T>
|
||||
void simple_property_write (std::ostream& stream, ForwardIterator it,
|
||||
std::pair<PropertyMap, PLY_property<T> > map)
|
||||
{
|
||||
if (CGAL::get_mode(stream) == IO::ASCII)
|
||||
stream << no_char_character(get (map.first, *it));
|
||||
else
|
||||
{
|
||||
typename PropertyMap::value_type value = get(map.first, *it);
|
||||
stream.write (reinterpret_cast<char*>(&value), sizeof(value));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ForwardIterator,
|
||||
typename PropertyMap,
|
||||
typename T>
|
||||
void simple_property_write (std::ostream& stream, ForwardIterator it,
|
||||
std::pair<PropertyMap, PLY_property<std::vector<T> > > map)
|
||||
{
|
||||
const typename PropertyMap::reference value = get(map.first, *it);
|
||||
|
||||
if (CGAL::get_mode(stream) == IO::ASCII)
|
||||
{
|
||||
stream << value.size();
|
||||
for (std::size_t i = 0; i < value.size(); ++ i)
|
||||
stream << " " << no_char_character(value[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned char size = static_cast<unsigned char>(value.size());
|
||||
stream.write (reinterpret_cast<char*>(&size), sizeof(size));
|
||||
for (std::size_t i = 0; i < value.size(); ++ i)
|
||||
{
|
||||
T t = T(value[i]);
|
||||
stream.write (reinterpret_cast<char*>(&t), sizeof(t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename ForwardIterator,
|
||||
typename PropertyMap,
|
||||
typename ... T>
|
||||
void output_properties (std::ostream& stream,
|
||||
ForwardIterator it,
|
||||
std::tuple<PropertyMap, PLY_property<T>... >&& current)
|
||||
{
|
||||
property_write (stream, it, std::get<0>(current));
|
||||
if (get_mode(stream) == IO::ASCII)
|
||||
stream << std::endl;
|
||||
}
|
||||
|
||||
|
||||
template <typename ForwardIterator,
|
||||
typename PropertyMap,
|
||||
typename T>
|
||||
void output_properties (std::ostream& stream,
|
||||
ForwardIterator it,
|
||||
std::pair<PropertyMap, PLY_property<T> >&& current)
|
||||
{
|
||||
simple_property_write (stream, it, std::forward<std::pair<PropertyMap, PLY_property<T> > >(current));
|
||||
if (get_mode(stream) == IO::ASCII)
|
||||
stream << std::endl;
|
||||
}
|
||||
|
||||
template <typename ForwardIterator,
|
||||
typename PropertyMap,
|
||||
typename T,
|
||||
typename NextPropertyHandler,
|
||||
typename ... PropertyHandler>
|
||||
void output_properties (std::ostream& stream,
|
||||
ForwardIterator it,
|
||||
std::pair<PropertyMap, PLY_property<T> >&& current,
|
||||
NextPropertyHandler&& next,
|
||||
PropertyHandler&& ... properties)
|
||||
{
|
||||
simple_property_write (stream, it, current);
|
||||
if (get_mode(stream) == IO::ASCII)
|
||||
stream << " ";
|
||||
output_properties (stream, it, std::forward<NextPropertyHandler>(next),
|
||||
std::forward<PropertyHandler>(properties)...);
|
||||
}
|
||||
|
||||
template <typename ForwardIterator,
|
||||
typename PropertyMap,
|
||||
typename ... T,
|
||||
typename NextPropertyHandler,
|
||||
typename ... PropertyHandler>
|
||||
void output_properties (std::ostream& stream,
|
||||
ForwardIterator it,
|
||||
std::tuple<PropertyMap, PLY_property<T>... >&& current,
|
||||
NextPropertyHandler&& next,
|
||||
PropertyHandler&& ... properties)
|
||||
{
|
||||
property_write (stream, it, std::get<0>(current));
|
||||
if (get_mode(stream) == IO::ASCII)
|
||||
stream << " ";
|
||||
output_properties (stream, it, std::forward<NextPropertyHandler>(next),
|
||||
std::forward<PropertyHandler>(properties)...);
|
||||
}
|
||||
|
||||
|
||||
// Printer classes used by Point_set_3 and Surface_mesh (translate a
|
||||
// property map to a PLY property)
|
||||
|
||||
template <typename Index>
|
||||
class Abstract_property_printer
|
||||
{
|
||||
public:
|
||||
virtual ~Abstract_property_printer() { }
|
||||
virtual void print (std::ostream& stream, const Index& index) = 0;
|
||||
};
|
||||
|
||||
template <typename Index, typename PropertyMap>
|
||||
class Property_printer : public Abstract_property_printer<Index>
|
||||
{
|
||||
PropertyMap m_pmap;
|
||||
public:
|
||||
Property_printer (const PropertyMap& pmap) : m_pmap (pmap)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual void print(std::ostream& stream, const Index& index)
|
||||
{
|
||||
stream << get(m_pmap, index);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Index, typename PropertyMap,
|
||||
typename Type = typename PropertyMap::value_type>
|
||||
class Simple_property_printer : public Abstract_property_printer<Index>
|
||||
{
|
||||
PropertyMap m_pmap;
|
||||
public:
|
||||
Simple_property_printer (const PropertyMap& pmap) : m_pmap (pmap)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual void print(std::ostream& stream, const Index& index)
|
||||
{
|
||||
if (get_mode(stream) == IO::ASCII)
|
||||
stream << get(m_pmap, index);
|
||||
else
|
||||
{
|
||||
Type t = Type(get (m_pmap, index));
|
||||
stream.write (reinterpret_cast<char*>(&t), sizeof(t));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Index, typename PropertyMap>
|
||||
class Char_property_printer : public Abstract_property_printer<Index>
|
||||
{
|
||||
typedef typename PropertyMap::value_type Type;
|
||||
PropertyMap m_pmap;
|
||||
public:
|
||||
Char_property_printer (const PropertyMap& pmap) : m_pmap (pmap)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual void print(std::ostream& stream, const Index& index)
|
||||
{
|
||||
if (get_mode(stream) == IO::ASCII)
|
||||
stream << int(get(m_pmap, index));
|
||||
else
|
||||
{
|
||||
Type t = get (m_pmap, index);
|
||||
stream.write (reinterpret_cast<char*>(&t), sizeof(t));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace PLY
|
||||
|
||||
} // namespace internal
|
||||
|
||||
} // namespace CGAL
|
||||
|
||||
|
||||
#endif // CGAL_IO_PLY_H
|
||||
|
|
@ -0,0 +1,729 @@
|
|||
// Copyright (c) 2018 GeometryFactory Sarl (France).
|
||||
// All rights reserved.
|
||||
//
|
||||
// This file is part of CGAL (www.cgal.org).
|
||||
// You can redistribute it and/or modify it under the terms of the GNU
|
||||
// General Public License as published by the Free Software Foundation,
|
||||
// either version 3 of the License, or (at your option) any later version.
|
||||
//
|
||||
// Licensees holding a valid commercial license may use this file in
|
||||
// accordance with the commercial license agreement provided with the software.
|
||||
//
|
||||
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// $URL$
|
||||
// $Id$
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
//
|
||||
//
|
||||
// Author(s) : Simon Giraudot
|
||||
|
||||
#ifndef CGAL_SURFACE_MESH_IO_PLY
|
||||
#define CGAL_SURFACE_MESH_IO_PLY
|
||||
|
||||
#include <CGAL/IO/PLY.h>
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
namespace internal {
|
||||
|
||||
#if !defined(CGAL_CFG_NO_CPP0X_RVALUE_REFERENCE) && !defined(CGAL_CFG_NO_CPP0X_VARIADIC_TEMPLATES)
|
||||
namespace PLY {
|
||||
|
||||
template <typename Point>
|
||||
class Surface_mesh_filler
|
||||
{
|
||||
public:
|
||||
typedef typename Kernel_traits<Point>::Kernel Kernel;
|
||||
typedef typename Kernel::Vector_3 Vector;
|
||||
typedef CGAL::Surface_mesh<Point> Surface_mesh;
|
||||
typedef typename Surface_mesh::size_type size_type;
|
||||
typedef typename Surface_mesh::Vertex_index Vertex_index;
|
||||
typedef typename Surface_mesh::Face_index Face_index;
|
||||
typedef typename Surface_mesh::Edge_index Edge_index;
|
||||
typedef typename Surface_mesh::Halfedge_index Halfedge_index;
|
||||
|
||||
private:
|
||||
|
||||
struct Abstract_ply_property_to_surface_mesh_property
|
||||
{
|
||||
virtual ~Abstract_ply_property_to_surface_mesh_property() { }
|
||||
virtual void assign (PLY_element& element, size_type index) = 0;
|
||||
};
|
||||
|
||||
template <typename Simplex, typename Type>
|
||||
class PLY_property_to_surface_mesh_property : public Abstract_ply_property_to_surface_mesh_property
|
||||
{
|
||||
typedef typename Surface_mesh::template Property_map<Simplex, Type> Map;
|
||||
Map m_map;
|
||||
std::string m_name;
|
||||
public:
|
||||
PLY_property_to_surface_mesh_property (Surface_mesh& sm, const std::string& name)
|
||||
: m_name (name)
|
||||
{
|
||||
m_map = sm.template add_property_map<Simplex, Type>(prefix(Simplex()) + name).first;
|
||||
}
|
||||
|
||||
virtual void assign (PLY_element& element, size_type index)
|
||||
{
|
||||
Type t{};
|
||||
element.assign (t, m_name.c_str());
|
||||
put(m_map, Simplex(index), t);
|
||||
}
|
||||
|
||||
std::string prefix(Vertex_index) const { return "v:"; }
|
||||
std::string prefix(Face_index) const { return "f:"; }
|
||||
std::string prefix(Edge_index) const { return "e:"; }
|
||||
std::string prefix(Halfedge_index) const { return "h:"; }
|
||||
};
|
||||
|
||||
Surface_mesh& m_mesh;
|
||||
std::vector<Vertex_index> m_map_v2v;
|
||||
bool m_use_floats;
|
||||
int m_normals;
|
||||
typename Surface_mesh::template Property_map<Vertex_index, Vector> m_normal_map;
|
||||
int m_vcolors;
|
||||
typename Surface_mesh::template Property_map<Vertex_index, CGAL::Color> m_vcolor_map;
|
||||
int m_fcolors;
|
||||
typename Surface_mesh::template Property_map<Face_index, CGAL::Color> m_fcolor_map;
|
||||
bool m_use_int32_t;
|
||||
std::string m_index_tag;
|
||||
std::vector<Abstract_ply_property_to_surface_mesh_property*> m_vertex_properties;
|
||||
std::vector<Abstract_ply_property_to_surface_mesh_property*> m_face_properties;
|
||||
std::vector<Abstract_ply_property_to_surface_mesh_property*> m_edge_properties;
|
||||
std::vector<Abstract_ply_property_to_surface_mesh_property*> m_halfedge_properties;
|
||||
|
||||
public:
|
||||
|
||||
Surface_mesh_filler (Surface_mesh& mesh)
|
||||
: m_mesh (mesh), m_use_floats (false), m_normals(0), m_vcolors(0), m_fcolors(0)
|
||||
{ }
|
||||
|
||||
~Surface_mesh_filler()
|
||||
{
|
||||
for (std::size_t i = 0; i < m_vertex_properties.size(); ++ i)
|
||||
delete m_vertex_properties[i];
|
||||
for (std::size_t i = 0; i < m_face_properties.size(); ++ i)
|
||||
delete m_face_properties[i];
|
||||
for (std::size_t i = 0; i < m_edge_properties.size(); ++ i)
|
||||
delete m_edge_properties[i];
|
||||
for (std::size_t i = 0; i < m_halfedge_properties.size(); ++ i)
|
||||
delete m_halfedge_properties[i];
|
||||
}
|
||||
|
||||
bool has_simplex_specific_property (internal::PLY::PLY_read_number* property, Vertex_index)
|
||||
{
|
||||
const std::string& name = property->name();
|
||||
if (name == "x" ||
|
||||
name == "y" ||
|
||||
name == "z")
|
||||
{
|
||||
if (dynamic_cast<PLY_read_typed_number<float>*>(property))
|
||||
m_use_floats = true;
|
||||
return true;
|
||||
}
|
||||
if (name == "nx" ||
|
||||
name == "ny" ||
|
||||
name == "nz")
|
||||
{
|
||||
++ m_normals;
|
||||
if (m_normals == 3)
|
||||
m_normal_map = m_mesh.template add_property_map<Vertex_index, Vector>("v:normal").first;
|
||||
return true;
|
||||
}
|
||||
if (name == "red" ||
|
||||
name == "green" ||
|
||||
name == "blue")
|
||||
{
|
||||
++ m_vcolors;
|
||||
if (m_vcolors == 3)
|
||||
m_vcolor_map = m_mesh.template add_property_map<Vertex_index, CGAL::Color>("v:color").first;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool has_simplex_specific_property (internal::PLY::PLY_read_number* property, Face_index)
|
||||
{
|
||||
const std::string& name = property->name();
|
||||
if (name == "vertex_indices" || name == "vertex_index")
|
||||
{
|
||||
CGAL_assertion (dynamic_cast<PLY_read_typed_list<boost::int32_t>*>(property)
|
||||
|| dynamic_cast<PLY_read_typed_list<boost::uint32_t>*>(property));
|
||||
m_index_tag = name;
|
||||
m_use_int32_t = dynamic_cast<PLY_read_typed_list<boost::int32_t>*>(property);
|
||||
return true;
|
||||
}
|
||||
if (name == "red" ||
|
||||
name == "green" ||
|
||||
name == "blue")
|
||||
{
|
||||
++ m_fcolors;
|
||||
if (m_fcolors == 3)
|
||||
m_fcolor_map = m_mesh.template add_property_map<Face_index, CGAL::Color>("f:color").first;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool has_simplex_specific_property (internal::PLY::PLY_read_number* property, Edge_index)
|
||||
{
|
||||
const std::string& name = property->name();
|
||||
if (name == "v0" || name == "v1")
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool has_simplex_specific_property (internal::PLY::PLY_read_number* property, Halfedge_index)
|
||||
{
|
||||
const std::string& name = property->name();
|
||||
if (name == "source" || name == "target")
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void instantiate_vertex_properties (PLY_element& element)
|
||||
{
|
||||
m_map_v2v.reserve(element.number_of_items());
|
||||
instantiate_properties<Vertex_index> (element, m_vertex_properties);
|
||||
}
|
||||
|
||||
void instantiate_face_properties (PLY_element& element)
|
||||
{
|
||||
instantiate_properties<Face_index> (element, m_face_properties);
|
||||
}
|
||||
|
||||
void instantiate_edge_properties (PLY_element& element)
|
||||
{
|
||||
instantiate_properties<Edge_index> (element, m_edge_properties);
|
||||
}
|
||||
|
||||
void instantiate_halfedge_properties (PLY_element& element)
|
||||
{
|
||||
instantiate_properties<Halfedge_index> (element, m_halfedge_properties);
|
||||
}
|
||||
|
||||
template <typename Simplex>
|
||||
void instantiate_properties (PLY_element& element,
|
||||
std::vector<Abstract_ply_property_to_surface_mesh_property*>& properties)
|
||||
{
|
||||
for (std::size_t j = 0; j < element.number_of_properties(); ++ j)
|
||||
{
|
||||
internal::PLY::PLY_read_number* property = element.property(j);
|
||||
|
||||
if (has_simplex_specific_property (property, Simplex()))
|
||||
continue;
|
||||
|
||||
const std::string& name = property->name();
|
||||
|
||||
if (dynamic_cast<PLY_read_typed_number<boost::int8_t>*>(property))
|
||||
{
|
||||
properties.push_back
|
||||
(new PLY_property_to_surface_mesh_property<Simplex, boost::int8_t>(m_mesh,
|
||||
name));
|
||||
}
|
||||
else if (dynamic_cast<PLY_read_typed_number<boost::uint8_t>*>(property))
|
||||
{
|
||||
properties.push_back
|
||||
(new PLY_property_to_surface_mesh_property<Simplex, boost::uint8_t>(m_mesh,
|
||||
name));
|
||||
}
|
||||
else if (dynamic_cast<PLY_read_typed_number<boost::int16_t>*>(property))
|
||||
{
|
||||
properties.push_back
|
||||
(new PLY_property_to_surface_mesh_property<Simplex, boost::int16_t>(m_mesh,
|
||||
name));
|
||||
}
|
||||
else if (dynamic_cast<PLY_read_typed_number<boost::uint16_t>*>(property))
|
||||
{
|
||||
properties.push_back
|
||||
(new PLY_property_to_surface_mesh_property<Simplex, boost::uint16_t>(m_mesh,
|
||||
name));
|
||||
}
|
||||
else if (dynamic_cast<PLY_read_typed_number<boost::int32_t>*>(property))
|
||||
{
|
||||
properties.push_back
|
||||
(new PLY_property_to_surface_mesh_property<Simplex, boost::int32_t>(m_mesh,
|
||||
name));
|
||||
}
|
||||
else if (dynamic_cast<PLY_read_typed_number<boost::uint32_t>*>(property))
|
||||
{
|
||||
properties.push_back
|
||||
(new PLY_property_to_surface_mesh_property<Simplex, boost::uint32_t>(m_mesh,
|
||||
name));
|
||||
}
|
||||
else if (dynamic_cast<PLY_read_typed_number<float>*>(property))
|
||||
{
|
||||
properties.push_back
|
||||
(new PLY_property_to_surface_mesh_property<Simplex, float>(m_mesh,
|
||||
name));
|
||||
}
|
||||
else if (dynamic_cast<PLY_read_typed_number<double>*>(property))
|
||||
{
|
||||
properties.push_back
|
||||
(new PLY_property_to_surface_mesh_property<Simplex, double>(m_mesh,
|
||||
name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void process_vertex_line (PLY_element& element)
|
||||
{
|
||||
Vertex_index vi;
|
||||
|
||||
if (m_use_floats)
|
||||
process_line<float>(element, vi);
|
||||
else
|
||||
process_line<double>(element, vi);
|
||||
|
||||
for (std::size_t i = 0; i < m_vertex_properties.size(); ++ i)
|
||||
m_vertex_properties[i]->assign (element, vi);
|
||||
}
|
||||
|
||||
template <typename FT>
|
||||
void process_line (PLY_element& element, Vertex_index& vi)
|
||||
{
|
||||
FT x = (FT)0.,y = (FT)0., z = (FT)0.,
|
||||
nx = (FT)0., ny = (FT)0., nz = (FT)0.;
|
||||
element.assign (x, "x");
|
||||
element.assign (y, "y");
|
||||
element.assign (z, "z");
|
||||
Point point (x, y, z);
|
||||
vi = m_mesh.add_vertex(point);
|
||||
m_map_v2v.push_back(vi);
|
||||
|
||||
if (m_normals == 3)
|
||||
{
|
||||
element.assign (nx, "nx");
|
||||
element.assign (ny, "ny");
|
||||
element.assign (nz, "nz");
|
||||
Vector normal (nx, ny, nz);
|
||||
m_normal_map[vi] = normal;
|
||||
}
|
||||
|
||||
if (m_vcolors == 3)
|
||||
{
|
||||
unsigned char r, g, b;
|
||||
element.assign (r, "red");
|
||||
element.assign (g, "green");
|
||||
element.assign (b, "blue");
|
||||
m_vcolor_map[vi] = CGAL::Color (r, g, b);
|
||||
}
|
||||
}
|
||||
|
||||
bool process_face_line (PLY_element& element)
|
||||
{
|
||||
Face_index fi = m_mesh.null_face();
|
||||
|
||||
if (m_use_int32_t)
|
||||
process_line<boost::int32_t>(element, fi);
|
||||
else
|
||||
process_line<boost::uint32_t>(element, fi);
|
||||
|
||||
if (fi == Surface_mesh::null_face())
|
||||
return false;
|
||||
|
||||
for (std::size_t i = 0; i < m_face_properties.size(); ++ i)
|
||||
m_face_properties[i]->assign (element, fi);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename IntType>
|
||||
void process_line (PLY_element& element, Face_index& fi)
|
||||
{
|
||||
std::vector<IntType> indices;
|
||||
element.assign (indices, m_index_tag.c_str());
|
||||
std::vector<Vertex_index> vertices;
|
||||
vertices.reserve(indices.size());
|
||||
for (std::size_t i = 0; i < indices.size(); ++ i)
|
||||
vertices.push_back (m_map_v2v[std::size_t(indices[i])]);
|
||||
|
||||
fi = m_mesh.add_face(vertices);
|
||||
if (fi == m_mesh.null_face())
|
||||
return;
|
||||
|
||||
if (m_fcolors == 3)
|
||||
{
|
||||
unsigned char r, g, b;
|
||||
element.assign (r, "red");
|
||||
element.assign (g, "green");
|
||||
element.assign (b, "blue");
|
||||
m_fcolor_map[fi] = CGAL::Color (r, g, b);
|
||||
}
|
||||
}
|
||||
|
||||
bool process_edge_line (PLY_element& element)
|
||||
{
|
||||
Edge_index ei = m_mesh.null_edge();
|
||||
|
||||
if (m_use_int32_t)
|
||||
process_line<boost::int32_t>(element, ei);
|
||||
else
|
||||
process_line<boost::uint32_t>(element, ei);
|
||||
|
||||
if (ei == Surface_mesh::null_edge())
|
||||
return false;
|
||||
|
||||
for (std::size_t i = 0; i < m_edge_properties.size(); ++ i)
|
||||
m_edge_properties[i]->assign (element, ei);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename IntType>
|
||||
void process_line (PLY_element& element, Edge_index& ei)
|
||||
{
|
||||
IntType v0, v1;
|
||||
element.assign (v0, "v0");
|
||||
element.assign (v1, "v1");
|
||||
|
||||
Halfedge_index hi = m_mesh.halfedge(m_map_v2v[std::size_t(v0)],
|
||||
m_map_v2v[std::size_t(v1)]);
|
||||
if (hi == m_mesh.null_halfedge())
|
||||
return;
|
||||
|
||||
ei = m_mesh.edge (hi);
|
||||
}
|
||||
|
||||
bool process_halfedge_line (PLY_element& element)
|
||||
{
|
||||
Halfedge_index hi = m_mesh.null_halfedge();
|
||||
|
||||
if (m_use_int32_t)
|
||||
process_line<boost::int32_t>(element, hi);
|
||||
else
|
||||
process_line<boost::uint32_t>(element, hi);
|
||||
|
||||
if (hi == Surface_mesh::null_halfedge())
|
||||
return false;
|
||||
|
||||
for (std::size_t i = 0; i < m_halfedge_properties.size(); ++ i)
|
||||
m_halfedge_properties[i]->assign (element, hi);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename IntType>
|
||||
void process_line (PLY_element& element, Halfedge_index& hi)
|
||||
{
|
||||
IntType source, target;
|
||||
element.assign (source, "source");
|
||||
element.assign (target, "target");
|
||||
hi = m_mesh.halfedge(m_map_v2v[std::size_t(source)], m_map_v2v[std::size_t(target)]);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
template <typename Point>
|
||||
bool fill_simplex_specific_header
|
||||
(std::ostream& os, const Surface_mesh<Point>& sm,
|
||||
std::vector<Abstract_property_printer<typename Surface_mesh<Point>::Vertex_index>*>& printers,
|
||||
const std::string& prop)
|
||||
{
|
||||
typedef Surface_mesh<Point> SMesh;
|
||||
typedef typename SMesh::Vertex_index VIndex;
|
||||
typedef typename Kernel_traits<Point>::Kernel Kernel;
|
||||
typedef typename Kernel::FT FT;
|
||||
typedef typename Kernel::Vector_3 Vector;
|
||||
typedef typename SMesh::template Property_map<VIndex, Point> Point_map;
|
||||
typedef typename SMesh::template Property_map<VIndex, Vector> Vector_map;
|
||||
typedef typename SMesh::template Property_map<VIndex, Color> Vcolor_map;
|
||||
|
||||
if (prop == "v:connectivity" ||
|
||||
prop == "v:removed")
|
||||
return true;
|
||||
|
||||
if (prop == "v:point")
|
||||
{
|
||||
if (boost::is_same<FT, float>::value)
|
||||
{
|
||||
os << "property float x" << std::endl
|
||||
<< "property float y" << std::endl
|
||||
<< "property float z" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
os << "property double x" << std::endl
|
||||
<< "property double y" << std::endl
|
||||
<< "property double z" << std::endl;
|
||||
}
|
||||
printers.push_back (new Property_printer<VIndex,Point_map>(sm.points()));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool okay = false;
|
||||
if (prop == "v:normal")
|
||||
{
|
||||
Vector_map pmap;
|
||||
boost::tie (pmap, okay) = sm.template property_map<VIndex,Vector>(prop);
|
||||
if (okay)
|
||||
{
|
||||
if (boost::is_same<FT, float>::value)
|
||||
{
|
||||
os << "property float nx" << std::endl
|
||||
<< "property float ny" << std::endl
|
||||
<< "property float nz" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
os << "property double nx" << std::endl
|
||||
<< "property double ny" << std::endl
|
||||
<< "property double nz" << std::endl;
|
||||
}
|
||||
printers.push_back (new Property_printer<VIndex,Vector_map>(pmap));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (prop == "v:color")
|
||||
{
|
||||
Vcolor_map pmap;
|
||||
boost::tie (pmap, okay) = sm.template property_map<VIndex,Color>(prop);
|
||||
if (okay)
|
||||
{
|
||||
os << "property uchar red" << std::endl
|
||||
<< "property uchar green" << std::endl
|
||||
<< "property uchar blue" << std::endl
|
||||
<< "property uchar alpha" << std::endl;
|
||||
|
||||
printers.push_back (new Property_printer<VIndex,Vcolor_map>(pmap));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Point>
|
||||
bool fill_simplex_specific_header
|
||||
(std::ostream& os, const Surface_mesh<Point>& sm,
|
||||
std::vector<Abstract_property_printer<typename Surface_mesh<Point>::Face_index>*>& printers,
|
||||
const std::string& prop)
|
||||
{
|
||||
typedef Surface_mesh<Point> SMesh;
|
||||
typedef typename SMesh::Face_index FIndex;
|
||||
typedef typename SMesh::template Property_map<FIndex, Color> Fcolor_map;
|
||||
|
||||
if (prop == "f:connectivity" ||
|
||||
prop == "f:removed")
|
||||
return true;
|
||||
|
||||
bool okay = false;
|
||||
if (prop == "f:color")
|
||||
{
|
||||
Fcolor_map pmap;
|
||||
boost::tie (pmap, okay) = sm.template property_map<FIndex,Color>(prop);
|
||||
if (okay)
|
||||
{
|
||||
os << "property uchar red" << std::endl
|
||||
<< "property uchar green" << std::endl
|
||||
<< "property uchar blue" << std::endl
|
||||
<< "property uchar alpha" << std::endl;
|
||||
|
||||
printers.push_back (new Property_printer<FIndex,Fcolor_map>(pmap));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Point>
|
||||
bool fill_simplex_specific_header
|
||||
(std::ostream& , const Surface_mesh<Point>& ,
|
||||
std::vector<Abstract_property_printer<typename Surface_mesh<Point>::Edge_index>*>& ,
|
||||
const std::string& prop)
|
||||
{
|
||||
if (prop == "e:removed")
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Point>
|
||||
bool fill_simplex_specific_header
|
||||
(std::ostream& , const Surface_mesh<Point>& ,
|
||||
std::vector<Abstract_property_printer<typename Surface_mesh<Point>::Halfedge_index>*>& ,
|
||||
const std::string& prop)
|
||||
{
|
||||
if (prop == "h:connectivity")
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Point>
|
||||
std::string get_property_raw_name (const std::string& prop, typename Surface_mesh<Point>::Vertex_index)
|
||||
{
|
||||
std::string name = prop;
|
||||
if (name.rfind("v:",0) == 0)
|
||||
name = std::string (prop.begin() + 2, prop.end());
|
||||
return name;
|
||||
}
|
||||
|
||||
template <typename Point>
|
||||
std::string get_property_raw_name (const std::string& prop, typename Surface_mesh<Point>::Face_index)
|
||||
{
|
||||
std::string name = prop;
|
||||
if (name.rfind("f:",0) == 0)
|
||||
name = std::string (prop.begin() + 2, prop.end());
|
||||
return name;
|
||||
}
|
||||
|
||||
template <typename Point>
|
||||
std::string get_property_raw_name (const std::string& prop, typename Surface_mesh<Point>::Edge_index)
|
||||
{
|
||||
std::string name = prop;
|
||||
if (name.rfind("e:",0) == 0)
|
||||
name = std::string (prop.begin() + 2, prop.end());
|
||||
return name;
|
||||
}
|
||||
|
||||
template <typename Point>
|
||||
std::string get_property_raw_name (const std::string& prop, typename Surface_mesh<Point>::Halfedge_index)
|
||||
{
|
||||
std::string name = prop;
|
||||
if (name.rfind("h:",0) == 0)
|
||||
name = std::string (prop.begin() + 2, prop.end());
|
||||
return name;
|
||||
}
|
||||
|
||||
template <typename Point, typename Simplex>
|
||||
void fill_header (std::ostream& os, const Surface_mesh<Point>& sm,
|
||||
std::vector<Abstract_property_printer<Simplex>*>& printers)
|
||||
{
|
||||
typedef Surface_mesh<Point> SMesh;
|
||||
typedef typename SMesh::template Property_map<Simplex, boost::int8_t> Int8_map;
|
||||
typedef typename SMesh::template Property_map<Simplex, boost::uint8_t> Uint8_map;
|
||||
typedef typename SMesh::template Property_map<Simplex, boost::int16_t> Int16_map;
|
||||
typedef typename SMesh::template Property_map<Simplex, boost::uint16_t> Uint16_map;
|
||||
typedef typename SMesh::template Property_map<Simplex, boost::int32_t> Int32_map;
|
||||
typedef typename SMesh::template Property_map<Simplex, boost::uint32_t> Uint32_map;
|
||||
typedef typename SMesh::template Property_map<Simplex, boost::int64_t> Int64_map;
|
||||
typedef typename SMesh::template Property_map<Simplex, boost::uint64_t> Uint64_map;
|
||||
typedef typename SMesh::template Property_map<Simplex, float> Float_map;
|
||||
typedef typename SMesh::template Property_map<Simplex, double> Double_map;
|
||||
std::vector<std::string> prop = sm.template properties<Simplex>();
|
||||
|
||||
for (std::size_t i = 0; i < prop.size(); ++ i)
|
||||
{
|
||||
if (fill_simplex_specific_header(os, sm, printers, prop[i]))
|
||||
continue;
|
||||
|
||||
// Cut the "v:" prefix
|
||||
std::string name = get_property_raw_name<Point> (prop[i], Simplex());
|
||||
|
||||
bool okay = false;
|
||||
{
|
||||
Int8_map pmap;
|
||||
boost::tie (pmap, okay) = sm.template property_map<Simplex,boost::int8_t>(prop[i]);
|
||||
if (okay)
|
||||
{
|
||||
os << "property char " << name << std::endl;
|
||||
printers.push_back (new internal::PLY::Char_property_printer<Simplex,Int8_map>(pmap));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
{
|
||||
Uint8_map pmap;
|
||||
boost::tie (pmap, okay) = sm.template property_map<Simplex,boost::uint8_t>(prop[i]);
|
||||
if (okay)
|
||||
{
|
||||
os << "property uchar " << name << std::endl;
|
||||
printers.push_back (new internal::PLY::Char_property_printer<Simplex,Uint8_map>(pmap));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
{
|
||||
Int16_map pmap;
|
||||
boost::tie (pmap, okay) = sm.template property_map<Simplex,boost::int16_t>(prop[i]);
|
||||
if (okay)
|
||||
{
|
||||
os << "property short " << name << std::endl;
|
||||
printers.push_back (new internal::PLY::Simple_property_printer<Simplex,Int16_map>(pmap));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
{
|
||||
Uint16_map pmap;
|
||||
boost::tie (pmap, okay) = sm.template property_map<Simplex,boost::uint16_t>(prop[i]);
|
||||
if (okay)
|
||||
{
|
||||
os << "property ushort " << name << std::endl;
|
||||
printers.push_back (new internal::PLY::Simple_property_printer<Simplex,Uint16_map>(pmap));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
{
|
||||
Int32_map pmap;
|
||||
boost::tie (pmap, okay) = sm.template property_map<Simplex,boost::int32_t>(prop[i]);
|
||||
if (okay)
|
||||
{
|
||||
os << "property int " << name << std::endl;
|
||||
printers.push_back (new internal::PLY::Simple_property_printer<Simplex,Int32_map>(pmap));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
{
|
||||
Uint32_map pmap;
|
||||
boost::tie (pmap, okay) = sm.template property_map<Simplex,boost::uint32_t>(prop[i]);
|
||||
if (okay)
|
||||
{
|
||||
os << "property uint " << name << std::endl;
|
||||
printers.push_back (new internal::PLY::Simple_property_printer<Simplex,Uint32_map>(pmap));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
{
|
||||
Int64_map pmap;
|
||||
boost::tie (pmap, okay) = sm.template property_map<Simplex,boost::int64_t>(prop[i]);
|
||||
if (okay)
|
||||
{
|
||||
os << "property int " << name << std::endl;
|
||||
printers.push_back (new internal::PLY::Simple_property_printer<Simplex,Int64_map,boost::int32_t>(pmap));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
{
|
||||
Uint64_map pmap;
|
||||
boost::tie (pmap, okay) = sm.template property_map<Simplex,boost::uint64_t>(prop[i]);
|
||||
if (okay)
|
||||
{
|
||||
os << "property uint " << name << std::endl;
|
||||
printers.push_back (new internal::PLY::Simple_property_printer<Simplex,Uint64_map,boost::uint32_t>(pmap));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
{
|
||||
Float_map pmap;
|
||||
boost::tie (pmap, okay) = sm.template property_map<Simplex,float>(prop[i]);
|
||||
if (okay)
|
||||
{
|
||||
os << "property float " << name << std::endl;
|
||||
printers.push_back (new internal::PLY::Simple_property_printer<Simplex,Float_map>(pmap));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
{
|
||||
Double_map pmap;
|
||||
boost::tie (pmap, okay) = sm.template property_map<Simplex,double>(prop[i]);
|
||||
if (okay)
|
||||
{
|
||||
os << "property double " << name << std::endl;
|
||||
printers.push_back (new internal::PLY::Simple_property_printer<Simplex,Double_map>(pmap));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace PLY
|
||||
#endif
|
||||
|
||||
} // namespace internal
|
||||
|
||||
} // namespace CGAL
|
||||
|
||||
|
||||
#endif // CGAL_SURFACE_MESH_IO_PLY
|
||||
|
|
@ -53,6 +53,7 @@
|
|||
#include <CGAL/boost/graph/iterator.h>
|
||||
#include <CGAL/boost/graph/Euler_operations.h>
|
||||
#include <CGAL/IO/File_scanner_OFF.h>
|
||||
#include <CGAL/Surface_mesh/IO/PLY.h>
|
||||
#include <CGAL/Handle_hash_function.h>
|
||||
#include <CGAL/boost/graph/named_params_helper.h>
|
||||
#include <CGAL/boost/graph/named_function_params.h>
|
||||
|
|
@ -2164,6 +2165,188 @@ private: //------------------------------------------------------- private data
|
|||
return os;
|
||||
}
|
||||
|
||||
#if !defined(CGAL_CFG_NO_CPP0X_RVALUE_REFERENCE) && !defined(CGAL_CFG_NO_CPP0X_VARIADIC_TEMPLATES)
|
||||
|
||||
/// \relates Surface_mesh
|
||||
/// Inserts the surface mesh in an output stream in PLY format.
|
||||
/// If found, "v:normal", "v:color" and "f:color" are inserted in the stream.
|
||||
/// All other vertex and face properties with simple types are inserted in the stream.
|
||||
/// Edges are only inserted in the stream if they have at least one
|
||||
/// property with simple type: if they do, all edge properties with
|
||||
/// simple types are inserted in the stream. The halfedges follow
|
||||
/// the same behavior.
|
||||
///
|
||||
/// If provided, the `comments` string is included line by line in
|
||||
/// the header of the PLY stream (each line will be precedeed by
|
||||
/// "comment ").
|
||||
///
|
||||
template <typename P>
|
||||
bool write_ply(std::ostream& os, const Surface_mesh<P>& sm, const std::string& comments = std::string())
|
||||
{
|
||||
typedef Surface_mesh<P> SMesh;
|
||||
typedef typename SMesh::Vertex_index VIndex;
|
||||
typedef typename SMesh::Face_index FIndex;
|
||||
typedef typename SMesh::Edge_index EIndex;
|
||||
typedef typename SMesh::Halfedge_index HIndex;
|
||||
|
||||
os << "ply" << std::endl
|
||||
<< ((get_mode(os) == IO::BINARY) ? "format binary_little_endian 1.0" : "format ascii 1.0") << std::endl
|
||||
<< "comment Generated by the CGAL library" << std::endl;
|
||||
|
||||
if (comments != std::string())
|
||||
{
|
||||
std::istringstream iss (comments);
|
||||
std::string line;
|
||||
while (getline(iss, line))
|
||||
{
|
||||
if (line != "Generated by the CGAL library") // Avoid repeating the line if multiple savings
|
||||
os << "comment " << line << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
os << "element vertex " << sm.number_of_vertices() << std::endl;
|
||||
|
||||
std::vector<internal::PLY::Abstract_property_printer<VIndex>*> vprinters;
|
||||
internal::PLY::fill_header (os, sm, vprinters);
|
||||
|
||||
os << "element face " << sm.number_of_faces() << std::endl;
|
||||
os << "property list uchar int vertex_indices" << std::endl;
|
||||
std::vector<internal::PLY::Abstract_property_printer<FIndex>*> fprinters;
|
||||
internal::PLY::fill_header (os, sm, fprinters);
|
||||
|
||||
std::vector<internal::PLY::Abstract_property_printer<EIndex>*> eprinters;
|
||||
if (sm.template properties<EIndex>().size() > 1)
|
||||
{
|
||||
os << "element edge " << sm.number_of_edges() << std::endl;
|
||||
os << "property int v0" << std::endl;
|
||||
os << "property int v1" << std::endl;
|
||||
internal::PLY::fill_header (os, sm, eprinters);
|
||||
}
|
||||
|
||||
std::vector<internal::PLY::Abstract_property_printer<HIndex>*> hprinters;
|
||||
if (sm.template properties<HIndex>().size() > 1)
|
||||
{
|
||||
os << "element halfedge " << sm.number_of_halfedges() << std::endl;
|
||||
os << "property int source" << std::endl;
|
||||
os << "property int target" << std::endl;
|
||||
internal::PLY::fill_header (os, sm, hprinters);
|
||||
}
|
||||
|
||||
os << "end_header" << std::endl;
|
||||
|
||||
BOOST_FOREACH(VIndex vi, sm.vertices())
|
||||
{
|
||||
for (std::size_t i = 0; i < vprinters.size(); ++ i)
|
||||
{
|
||||
vprinters[i]->print(os, vi);
|
||||
if (get_mode (os) == IO::ASCII)
|
||||
os << " ";
|
||||
}
|
||||
if (get_mode (os) == IO::ASCII)
|
||||
os << std::endl;
|
||||
}
|
||||
|
||||
std::vector<VIndex> polygon;
|
||||
|
||||
BOOST_FOREACH(FIndex fi, sm.faces())
|
||||
{
|
||||
// Get list of vertex indices
|
||||
polygon.clear();
|
||||
BOOST_FOREACH(HIndex hi, halfedges_around_face(halfedge(fi, sm), sm))
|
||||
polygon.push_back (sm.target(hi));
|
||||
|
||||
if (get_mode (os) == IO::ASCII)
|
||||
{
|
||||
os << polygon.size() << " ";
|
||||
for (std::size_t i = 0; i < polygon.size(); ++ i)
|
||||
os << int(polygon[i]) << " ";
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned char size = (unsigned char)(polygon.size());
|
||||
os.write (reinterpret_cast<char*>(&size), sizeof(size));
|
||||
for (std::size_t i = 0; i < polygon.size(); ++ i)
|
||||
{
|
||||
int idx = int(polygon[i]);
|
||||
os.write (reinterpret_cast<char*>(&idx), sizeof(idx));
|
||||
}
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < fprinters.size(); ++ i)
|
||||
{
|
||||
fprinters[i]->print(os, fi);
|
||||
if (get_mode (os) == IO::ASCII)
|
||||
os << " ";
|
||||
}
|
||||
|
||||
if (get_mode (os) == IO::ASCII)
|
||||
os << std::endl;
|
||||
}
|
||||
|
||||
if (!eprinters.empty())
|
||||
{
|
||||
BOOST_FOREACH(EIndex ei, sm.edges())
|
||||
{
|
||||
if (get_mode (os) == IO::ASCII)
|
||||
os << int(sm.vertex(ei,0)) << " " << int(sm.vertex(ei,1)) << " ";
|
||||
else
|
||||
{
|
||||
int v0 = int(sm.vertex(ei,0));
|
||||
int v1 = int(sm.vertex(ei,1));
|
||||
os.write (reinterpret_cast<char*>(&v0), sizeof(v0));
|
||||
os.write (reinterpret_cast<char*>(&v1), sizeof(v1));
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < eprinters.size(); ++ i)
|
||||
{
|
||||
eprinters[i]->print(os, ei);
|
||||
if (get_mode (os) == IO::ASCII)
|
||||
os << " ";
|
||||
}
|
||||
|
||||
if (get_mode (os) == IO::ASCII)
|
||||
os << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hprinters.empty())
|
||||
{
|
||||
BOOST_FOREACH(HIndex hi, sm.halfedges())
|
||||
{
|
||||
if (get_mode (os) == IO::ASCII)
|
||||
os << int(sm.source(hi)) << " " << int(sm.target(hi)) << " ";
|
||||
else
|
||||
{
|
||||
int source = int(sm.source(hi));
|
||||
int target = int(sm.target(hi));
|
||||
os.write (reinterpret_cast<char*>(&source), sizeof(source));
|
||||
os.write (reinterpret_cast<char*>(&target), sizeof(target));
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < hprinters.size(); ++ i)
|
||||
{
|
||||
hprinters[i]->print(os, hi);
|
||||
if (get_mode (os) == IO::ASCII)
|
||||
os << " ";
|
||||
}
|
||||
|
||||
if (get_mode (os) == IO::ASCII)
|
||||
os << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < vprinters.size(); ++ i)
|
||||
delete vprinters[i];
|
||||
for (std::size_t i = 0; i < fprinters.size(); ++ i)
|
||||
delete fprinters[i];
|
||||
for (std::size_t i = 0; i < eprinters.size(); ++ i)
|
||||
delete eprinters[i];
|
||||
for (std::size_t i = 0; i < hprinters.size(); ++ i)
|
||||
delete hprinters[i];
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @cond CGAL_DOCUMENT_INTERNALS
|
||||
|
||||
|
|
@ -2222,7 +2405,7 @@ private: //------------------------------------------------------- private data
|
|||
if(!is){
|
||||
return false;
|
||||
}
|
||||
sm.reserve(sm.num_vertices()+n, sm.num_faces()+2*f, sm.num_edges()+e);
|
||||
sm.reserve(sm.num_vertices()+n, sm.num_edges()+e, sm.num_faces()+f);
|
||||
std::vector<Vertex_index> vertexmap(n);
|
||||
P p;
|
||||
Vector_3 v;
|
||||
|
|
@ -2324,6 +2507,133 @@ private: //------------------------------------------------------- private data
|
|||
return read_off(is, sm, parameters::all_default());
|
||||
}
|
||||
|
||||
#if !defined(CGAL_CFG_NO_CPP0X_RVALUE_REFERENCE) && !defined(CGAL_CFG_NO_CPP0X_VARIADIC_TEMPLATES)
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
template <typename P>
|
||||
bool read_ply(std::istream& is, Surface_mesh<P>& sm)
|
||||
{
|
||||
std::string dummy;
|
||||
return read_ply (is, sm, dummy);
|
||||
}
|
||||
/// \endcond
|
||||
|
||||
/// Extracts the surface mesh from an input stream in Ascii or
|
||||
/// Binary PLY format and appends it to the surface mesh `sm`.
|
||||
///
|
||||
/// - the operator reads the vertex `point` property and the face
|
||||
/// `vertex_index` (or `vertex_indices`) property;
|
||||
/// - if three PLY properties `nx`, `ny` and `nz` with type `float`
|
||||
/// or `double` are found for vertices, a "v:normal" vertex
|
||||
/// property map is added;
|
||||
/// - if three PLY properties `red`, `green` and `blue` with type
|
||||
/// `uchar` are found for vertices, a "v:color" vertex property
|
||||
/// map is added;
|
||||
/// - if three PLY properties `red`, `green` and `blue` with type
|
||||
/// `uchar` are found for faces, a "f:color" face property map is
|
||||
/// added;
|
||||
/// - if any other PLY property is found, a "[s]:[name]" property map is
|
||||
/// added, where `[s]` is `v` for vertex and `f` for face, and
|
||||
/// `[name]` is the name of the PLY property.
|
||||
///
|
||||
/// The `comments` parameter can be omitted. If provided, it will be
|
||||
/// used to store the potential comments found in the PLY
|
||||
/// header. Each line starting by "comment " in the header is
|
||||
/// appended to the `comments` string (without the "comment " word).
|
||||
///
|
||||
/// \pre The data in the stream must represent a two-manifold. If this is not the case
|
||||
/// the `failbit` of `is` is set and the mesh cleared.
|
||||
/// \relates Surface_mesh
|
||||
|
||||
template <typename P>
|
||||
bool read_ply(std::istream& is, Surface_mesh<P>& sm, std::string& comments)
|
||||
{
|
||||
typedef typename Surface_mesh<P>::size_type size_type;
|
||||
|
||||
if(!is)
|
||||
{
|
||||
std::cerr << "Error: cannot open file" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
internal::PLY::PLY_reader reader;
|
||||
internal::PLY::Surface_mesh_filler<P> filler(sm);
|
||||
|
||||
if (!(reader.init (is)))
|
||||
{
|
||||
is.setstate(std::ios::failbit);
|
||||
return false;
|
||||
}
|
||||
|
||||
comments = reader.comments();
|
||||
|
||||
for (std::size_t i = 0; i < reader.number_of_elements(); ++ i)
|
||||
{
|
||||
internal::PLY::PLY_element& element = reader.element(i);
|
||||
|
||||
bool is_vertex = (element.name() == "vertex" || element.name() == "vertices");
|
||||
bool is_face = false;
|
||||
bool is_edge = false;
|
||||
bool is_halfedge = false;
|
||||
if (is_vertex)
|
||||
{
|
||||
sm.reserve(sm.number_of_vertices() + size_type(element.number_of_items()),
|
||||
sm.number_of_edges(),
|
||||
sm.number_of_faces());
|
||||
filler.instantiate_vertex_properties (element);
|
||||
}
|
||||
else
|
||||
is_face = (element.name() == "face" || element.name() == "faces");
|
||||
|
||||
if (is_face)
|
||||
{
|
||||
sm.reserve(sm.number_of_vertices(),
|
||||
sm.number_of_edges(),
|
||||
sm.number_of_faces() + size_type(element.number_of_items()));
|
||||
filler.instantiate_face_properties (element);
|
||||
}
|
||||
else
|
||||
is_edge = (element.name() == "edge");
|
||||
|
||||
if (is_edge)
|
||||
filler.instantiate_edge_properties (element);
|
||||
else
|
||||
is_halfedge = (element.name() == "halfedge");
|
||||
|
||||
if (is_halfedge)
|
||||
filler.instantiate_halfedge_properties (element);
|
||||
|
||||
for (std::size_t j = 0; j < element.number_of_items(); ++ j)
|
||||
{
|
||||
for (std::size_t k = 0; k < element.number_of_properties(); ++ k)
|
||||
{
|
||||
internal::PLY::PLY_read_number* property = element.property(k);
|
||||
property->get (is);
|
||||
if (is.fail())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_vertex)
|
||||
filler.process_vertex_line (element);
|
||||
else if (is_face)
|
||||
{
|
||||
if (!filler.process_face_line (element))
|
||||
{
|
||||
is.setstate(std::ios::failbit);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (is_edge)
|
||||
filler.process_edge_line (element);
|
||||
else if (is_halfedge)
|
||||
filler.process_halfedge_line (element);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// \relates Surface_mesh
|
||||
/// This operator calls `read_off(std::istream& is, CGAL::Surface_mesh& sm)`.
|
||||
/// \attention Up to %CGAL 4.10 this operator called `sm.clear()`.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
ply
|
||||
format ascii 1.0
|
||||
element vertex 4
|
||||
property double x
|
||||
property double y
|
||||
property double z
|
||||
property double nx
|
||||
property double ny
|
||||
property double nz
|
||||
property uchar red
|
||||
property uchar green
|
||||
property uchar blue
|
||||
property int id
|
||||
element face 4
|
||||
property list uchar int vertex_indices
|
||||
property uchar red
|
||||
property uchar green
|
||||
property uchar blue
|
||||
property int label
|
||||
element edge 6
|
||||
property int v0
|
||||
property int v1
|
||||
property float confidence
|
||||
end_header
|
||||
0 0 0 -0.5 -0.5 -0.5 255 255 0 0
|
||||
0 0 1 -0.5 -0.5 0 0 255 255 1
|
||||
0 1 0 -0.5 0 -0.5 128 0 255 2
|
||||
1 0 0 0 -0.5 -0.5 255 128 0 3
|
||||
3 0 1 2 255 0 0 -1
|
||||
3 0 3 1 0 255 0 1
|
||||
3 1 3 2 0 0 255 -1
|
||||
3 0 2 3 255 0 255 0
|
||||
0 1 0.1
|
||||
0 2 0.2
|
||||
0 3 0.3
|
||||
1 2 0.4
|
||||
1 3 0.5
|
||||
2 3 0.6
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
#include <CGAL/Surface_mesh/Surface_mesh.h>
|
||||
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
|
||||
typedef Kernel::Point_3 Point;
|
||||
typedef CGAL::Surface_mesh<Point> SMesh;
|
||||
typedef boost::graph_traits<SMesh>::face_descriptor face_descriptor;
|
||||
typedef boost::graph_traits<SMesh>::vertex_descriptor vertex_descriptor;
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
std::ifstream in ("colored_tetra.ply");
|
||||
SMesh mesh;
|
||||
CGAL::read_ply (in, mesh);
|
||||
|
||||
std::cerr << "Read mesh with " << mesh.number_of_vertices() << " vertices and "
|
||||
<< mesh.number_of_faces() << " faces" << std::endl;
|
||||
|
||||
std::cerr << "Properties associated with vertices:" << std::endl;
|
||||
std::vector<std::string> properties = mesh.properties<SMesh::Vertex_index>();
|
||||
for (std::size_t i = 0; i < properties.size(); ++ i)
|
||||
std::cerr << " * " << properties[i] << std::endl;
|
||||
|
||||
std::cerr << "Properties associated with faces:" << std::endl;
|
||||
properties = mesh.properties<SMesh::Face_index>();
|
||||
for (std::size_t i = 0; i < properties.size(); ++ i)
|
||||
std::cerr << " * " << properties[i] << std::endl;
|
||||
|
||||
mesh.add_property_map<SMesh::Edge_index, short>("id", 42);
|
||||
mesh.add_property_map<SMesh::Halfedge_index, float>("u", 13.f);
|
||||
mesh.add_property_map<SMesh::Halfedge_index, float>("v", 37.f);
|
||||
|
||||
// Append second mesh
|
||||
std::ifstream in2 ("tetra.ply");
|
||||
CGAL::read_ply (in2, mesh);
|
||||
|
||||
std::ofstream out ("out.ply");
|
||||
// CGAL::set_binary_mode(out);
|
||||
CGAL::write_ply (out, mesh);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
ply
|
||||
format ascii 1.0
|
||||
element vertex 3
|
||||
property double x
|
||||
property double y
|
||||
property double z
|
||||
property int blabla
|
||||
element face 1
|
||||
property list uchar int vertex_indices
|
||||
end_header
|
||||
0 0 2 1000
|
||||
0 1 2 200
|
||||
1 0 2 30
|
||||
3 0 1 2
|
||||
Loading…
Reference in New Issue