Merge pull request #6575 from nh2/ply-property-lists

PLY: Support writing per-face/vertex property lists
This commit is contained in:
Laurent Rineau 2023-05-11 17:01:44 +02:00
commit 4f6398a4e2
4 changed files with 213 additions and 174 deletions

View File

@ -545,7 +545,7 @@ bool write_PLY(std::ostream& os,
if(okay)
{
os << "property char " << prop[i] << std::endl;
printers.push_back(new internal::Char_property_printer<Index,Int8_map>(pmap));
printers.push_back(new internal::Simple_property_printer<Index,Int8_map>(pmap));
continue;
}
}
@ -555,7 +555,7 @@ bool write_PLY(std::ostream& os,
if(okay)
{
os << "property uchar " << prop[i] << std::endl;
printers.push_back(new internal::Char_property_printer<Index,Uint8_map>(pmap));
printers.push_back(new internal::Simple_property_printer<Index,Uint8_map>(pmap));
continue;
}
}

View File

@ -275,25 +275,37 @@ public:
}
};
template <typename Index, typename PropertyMap>
class Char_property_printer
template <typename Index,
typename PropertyMap,
typename VectorType = typename boost::property_traits<PropertyMap>::value_type,
typename ElementType = typename VectorType::value_type>
class Simple_property_vector_printer
: public Abstract_property_printer<Index>
{
typedef typename boost::property_traits<PropertyMap>::value_type Type;
PropertyMap m_pmap;
public:
Char_property_printer(const PropertyMap& pmap) : m_pmap(pmap) { }
Simple_property_vector_printer(const PropertyMap& pmap) : m_pmap(pmap) { }
virtual void print(std::ostream& stream, const Index& index)
{
const VectorType& vec = get(m_pmap, index);
if(get_mode(stream) == CGAL::IO::ASCII)
stream << int(get(m_pmap, index));
{
stream << vec.size();
for(const ElementType& v : vec)
{
stream << " " << v;
}
}
else
{
Type t = get(m_pmap, index);
stream.write(reinterpret_cast<char*>(&t), sizeof(t));
unsigned char size = (unsigned char)(vec.size());
stream.write(reinterpret_cast<char*>(&size), sizeof(size));
for(const ElementType& v : vec)
{
ElementType t = ElementType(v);
stream.write(reinterpret_cast<char*>(&t), sizeof(t));
}
}
}
};

View File

@ -145,10 +145,10 @@ public:
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));
CGAL_assertion(dynamic_cast<PLY_read_typed_list<std::int32_t>*>(property)
|| dynamic_cast<PLY_read_typed_list<std::uint32_t>*>(property));
m_index_tag = name;
m_use_int32_t = dynamic_cast<PLY_read_typed_list<boost::int32_t>*>(property);
m_use_int32_t = dynamic_cast<PLY_read_typed_list<std::int32_t>*>(property);
return true;
}
if(name == "red" ||
@ -205,10 +205,42 @@ public:
instantiate_properties<Halfedge_index>(element, m_halfedge_properties);
}
template <typename Simplex, class T, class ... TN>
void instantiate_properties_impl(PLY_element& element,
std::vector<Abstract_ply_property_to_surface_mesh_property*>& properties,
internal::PLY_read_number* property,
std::tuple<T, TN...>)
{
if(dynamic_cast<PLY_read_typed_number<T>*>(property))
{
properties.push_back(new PLY_property_to_surface_mesh_property<Simplex, T>(m_mesh, property->name()));
return;
}
if(dynamic_cast<PLY_read_typed_list<T>*>(property))
{
properties.push_back(new PLY_property_to_surface_mesh_property<Simplex, std::vector<T>>(m_mesh, property->name()));
return;
}
instantiate_properties_impl<Simplex>(element, properties, property, std::tuple<TN...>());
}
template <typename Simplex>
void instantiate_properties_impl(PLY_element&,
std::vector<Abstract_ply_property_to_surface_mesh_property*>&,
internal::PLY_read_number*,
std::tuple<>)
{}
template <typename Simplex>
void instantiate_properties(PLY_element& element,
std::vector<Abstract_ply_property_to_surface_mesh_property*>& properties)
{
typedef std::tuple<std::int8_t, std::uint8_t,
std::int16_t , std::uint16_t,
std::int32_t , std::uint32_t,
std::int64_t, std:: uint64_t,
float, double> Type_tuple;
for(std::size_t j = 0; j < element.number_of_properties(); ++ j)
{
internal::PLY_read_number* property = element.property(j);
@ -216,40 +248,7 @@ public:
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));
}
instantiate_properties_impl<Simplex>(element, properties, property, Type_tuple());
}
}
@ -302,9 +301,9 @@ public:
Face_index fi = m_mesh.null_face();
if(m_use_int32_t)
process_line<boost::int32_t>(element, fi);
process_line<std::int32_t>(element, fi);
else
process_line<boost::uint32_t>(element, fi);
process_line<std::uint32_t>(element, fi);
if(fi == Surface_mesh::null_face())
return false;
@ -344,9 +343,9 @@ public:
Edge_index ei = m_mesh.null_edge();
if(m_use_int32_t)
process_line<boost::int32_t>(element, ei);
process_line<std::int32_t>(element, ei);
else
process_line<boost::uint32_t>(element, ei);
process_line<std::uint32_t>(element, ei);
if(ei == Surface_mesh::null_edge())
return false;
@ -377,9 +376,9 @@ public:
Halfedge_index hi = m_mesh.null_halfedge();
if(m_use_int32_t)
process_line<boost::int32_t>(element, hi);
process_line<std::int32_t>(element, hi);
else
process_line<boost::uint32_t>(element, hi);
process_line<std::uint32_t>(element, hi);
if(hi == Surface_mesh::null_halfedge())
return false;
@ -421,7 +420,7 @@ bool fill_simplex_specific_header(std::ostream& os,
if(prop == "v:point")
{
if(boost::is_same<FT, float>::value)
if(std::is_same<FT, float>::value)
{
os << "property float x" << std::endl
<< "property float y" << std::endl
@ -441,10 +440,10 @@ bool fill_simplex_specific_header(std::ostream& os,
if(prop == "v:normal")
{
Vector_map pmap;
boost::tie(pmap, okay) = sm.template property_map<VIndex, Vector>(prop);
std::tie(pmap, okay) = sm.template property_map<VIndex, Vector>(prop);
if(okay)
{
if(boost::is_same<FT, float>::value)
if(std::is_same<FT, float>::value)
{
os << "property float nx" << std::endl
<< "property float ny" << std::endl
@ -464,7 +463,7 @@ bool fill_simplex_specific_header(std::ostream& os,
if(prop == "v:color")
{
Vcolor_map pmap;
boost::tie(pmap, okay) = sm.template property_map<VIndex, Color>(prop);
std::tie(pmap, okay) = sm.template property_map<VIndex, Color>(prop);
if(okay)
{
os << "property uchar red" << std::endl
@ -498,7 +497,7 @@ bool fill_simplex_specific_header(std::ostream& os,
if(prop == "f:color")
{
Fcolor_map pmap;
boost::tie(pmap, okay) = sm.template property_map<FIndex, Color>(prop);
std::tie(pmap, okay) = sm.template property_map<FIndex, Color>(prop);
if(okay)
{
os << "property uchar red" << std::endl
@ -575,21 +574,65 @@ std::string get_property_raw_name(const std::string& prop, typename Surface_mesh
return name;
}
template <std::size_t s, class Point, typename Simplex, class T, class ... TN>
void fill_header_impl(std::tuple<T,TN...>,
const char* const type_strings[],
const Surface_mesh<Point>& sm,
const std::string& pname,
std::ostream& os,
std::vector<Abstract_property_printer<Simplex>*>& printers)
{
constexpr std::size_t cid = s-std::tuple_size<std::tuple<T,TN...>>::value;
bool okay = false;
{
typedef typename Surface_mesh<Point>::template Property_map<Simplex, T> Pmap;
Pmap pmap;
std::tie(pmap, okay) = sm.template property_map<Simplex,T>(pname);
if(okay)
{
std::string name = get_property_raw_name<Point>(pname, Simplex());
os << "property " << type_strings[cid] << " " << name << std::endl;
printers.push_back(new internal::Simple_property_printer<Simplex,Pmap>(pmap));
return;
}
}
{
typedef typename Surface_mesh<Point>::template Property_map<Simplex, std::vector<T>> Pmap;
Pmap pmap;
std::tie(pmap, okay) = sm.template property_map<Simplex,std::vector<T>>(pname);
if(okay)
{
std::string name = get_property_raw_name<Point>(pname, Simplex());
os << "property list uchar " << type_strings[cid] << " " << name << std::endl;
printers.push_back(new internal::Simple_property_vector_printer<Simplex,Pmap>(pmap));
return;
}
}
fill_header_impl<s>(std::tuple<TN...>(),type_strings, sm, pname, os, printers);
}
template <std::size_t s, class Point, typename Simplex>
void fill_header_impl(std::tuple<>,
const char* const [],
const Surface_mesh<Point>&,
const std::string&,
std::ostream&,
std::vector<Abstract_property_printer<Simplex>*>&)
{}
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;
typedef std::tuple<std::int8_t, std::uint8_t,
std::int16_t , std::uint16_t,
std::int32_t , std::uint32_t,
std::int64_t, std:: uint64_t,
float, double> Type_tuple;
static constexpr const char* type_strings[] =
{ "char", "uchar", "short", "ushort","int", "uint", "int", "uint", "float", "double" };
std::vector<std::string> prop = sm.template properties<Simplex>();
@ -598,110 +641,7 @@ void fill_header(std::ostream& os, const Surface_mesh<Point>& sm,
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::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::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::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::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::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::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::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::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::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::Simple_property_printer<Simplex,Double_map>(pmap));
continue;
}
}
fill_header_impl<std::tuple_size<Type_tuple>::value>(Type_tuple(), type_strings, sm, prop[i], os, printers);
}
}

View File

@ -43,5 +43,92 @@ int main()
// CGAL::IO::set_binary_mode(out);
CGAL::IO::write_PLY(out, mesh);
// extra test for read/write of properties
mesh = SMesh();
in.close();
in.open(CGAL::data_file_path("meshes/colored_tetra.ply"));
CGAL::IO::read_PLY(in, mesh);
float fvalue=1001;
auto v_uvmap = mesh.add_property_map<SMesh::Vertex_index, std::vector<float>>("v:uv").first;
auto v_umap = mesh.add_property_map<SMesh::Vertex_index, float>("v:u").first;
auto v_vmap = mesh.add_property_map<SMesh::Vertex_index, float>("v:v").first;
for (SMesh::Vertex_index v : vertices(mesh))
{
v_uvmap[v]={fvalue, -fvalue};
v_umap[v]=fvalue;
v_vmap[v]=-fvalue;
++fvalue;
}
double dvalue=2001;
auto f_uvmap = mesh.add_property_map<SMesh::Face_index, std::vector<double>>("f:uv").first;
auto f_umap = mesh.add_property_map<SMesh::Face_index, double>("f:u").first;
auto f_vmap = mesh.add_property_map<SMesh::Face_index, double>("f:v").first;
for (SMesh::Face_index f : faces(mesh))
{
f_uvmap[f]={dvalue, -dvalue};
f_umap[f]=dvalue;
f_vmap[f]=-dvalue;
++dvalue;
}
out.close();
out.open("out_ascii.ply");
CGAL::IO::write_PLY(out, mesh);
out.close();
out.open("out_binary.ply");
CGAL::IO::set_binary_mode(out);
CGAL::IO::write_PLY(out, mesh);
out.close();
mesh.clear();
const std::array<std::string,2> fnames = {"out_ascii.ply", "out_binary.ply"};
for (std::string fn : fnames)
{
std::cout << "Reading " << fn << "\n";
in.close();
in.open(fn);
SMesh mesh_bis;
CGAL::IO::read_PLY(in, mesh_bis);
assert((mesh_bis.property_map<SMesh::Vertex_index, float>("v:u").second));
assert((mesh_bis.property_map<SMesh::Vertex_index, float>("v:v").second));
assert((mesh_bis.property_map<SMesh::Vertex_index, std::vector<float>>("v:uv").second));
v_uvmap = mesh_bis.property_map<SMesh::Vertex_index, std::vector<float>>("v:uv").first;
v_umap = mesh_bis.property_map<SMesh::Vertex_index, float>("v:u").first;
v_vmap = mesh_bis.property_map<SMesh::Vertex_index, float>("v:v").first;
fvalue=1001;
for (SMesh::Vertex_index v : vertices(mesh_bis))
{
assert(v_uvmap[v].size()==2);
assert(v_uvmap[v][0]==fvalue);
assert(v_uvmap[v][1]==-fvalue);
assert(v_umap[v]==fvalue);
assert(v_vmap[v]==-fvalue);
++fvalue;
}
assert((mesh_bis.property_map<SMesh::Face_index, double>("f:u").second));
assert((mesh_bis.property_map<SMesh::Face_index, double>("f:v").second));
assert((mesh_bis.property_map<SMesh::Face_index, std::vector<double>>("f:uv").second));
f_uvmap = mesh_bis.property_map<SMesh::Face_index, std::vector<double>>("f:uv").first;
f_umap = mesh_bis.property_map<SMesh::Face_index, double>("f:u").first;
f_vmap = mesh_bis.property_map<SMesh::Face_index, double>("f:v").first;
dvalue=2001;
for (SMesh::Face_index f : faces(mesh_bis))
{
assert(f_uvmap[f].size()==2);
assert(f_uvmap[f][0]==dvalue);
assert(f_uvmap[f][1]==-dvalue);
assert(f_umap[f]==dvalue);
assert(f_vmap[f]==-dvalue);
++dvalue;
}
}
return 0;
}