Lately I've been working with CGAL and its arbitrary precision kernel. These are convenient, but writing to standard floating point precision mesh file formats requires rounding. Here's a relatively straightforward way of using libigl's existing xml serialization routines to write an Eigen matrix tempalated on the CGAL Exact kernels number type (e.g. a matrix of vertex positions) to an xml file (using ASCII or base64 binary encoding).
Notice that there's a bit of gnarly template specialization that needs to be done inside the igl::xml::serialization_xml
namespace. The structure of the libigl xml serialization code is unfortunately not very flexible.
#include <igl/xml/serialize_xml.h>
#include <Eigen/Core>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <iostream>
typedef CGAL::Epeck::FT EScalar;
typedef Eigen::Matrix<EScalar,Eigen::Dynamic,Eigen::Dynamic> MatrixXE;
namespace igl
{
namespace xml
{
namespace serialization_xml
{
template <> inline void serialize(
const MatrixXE & obj,
tinyxml2::XMLDocument* doc,
tinyxml2::XMLElement* element,
const std::string& name)
{
const std::function<std::string(const MatrixXE::Scalar &) > to_string =
[](const MatrixXE::Scalar & v)->std::string
{
return
STR(CGAL::exact(v));
};
serialize(obj,name,to_string,doc,element);
}
template <> inline void deserialize(
MatrixXE & obj,
const tinyxml2::XMLDocument* doc,
const tinyxml2::XMLElement* element,
const std::string& name)
{
const std::function<void(const std::string &,MatrixXE::Scalar &)> &
from_string =
[](const std::string & s, MatrixXE::Scalar & v)
{
std::stringstream(s)>>v;
};
deserialize(doc,element,name,from_string,obj);
}
}
}
}
int main(int argc, char * argv[])
{
using namespace std;
using namespace Eigen;
// Little 4-vertex, 2-face mesh with rational coordinates
MatrixXE V(4,3),W;
V<<
EScalar(1)/EScalar(3), EScalar(1)/EScalar(13), EScalar(1)/EScalar(7),
EScalar(1)/EScalar(7), EScalar(1)/EScalar(3), EScalar(1)/EScalar(13),
EScalar(1)/EScalar(13), EScalar(1)/EScalar(7), EScalar(1)/EScalar(3),
EScalar(1)/EScalar(13), EScalar(1)/EScalar(7), -EScalar(1)/EScalar(3);
MatrixXi F(2,3),G;
F<<0,1,2,0,2,3;
// Write mesh
const bool binary = false;
// Write vertices, overwriting file (true)
igl::xml::serialize_xml(V,"vertices","exact.xml",binary,true);
// Write faces to same file, appending (false)
igl::xml::serialize_xml(F,"faces","exact.xml",binary,false);
// Read mesh
igl::xml::deserialize_xml(W,"vertices","exact.xml");
igl::xml::deserialize_xml(G,"faces","exact.xml");
// Verify
cout<<"V "<<(V.isApprox(W,0) ? "equals" : "does not equal")<<" W"<<endl;
}
Here's a little feeling for the overhead of this format on a big single-precision mesh cast to the exact kernel (the expressions are simple and short, so this is sort of a best case scenario):
| | .ply|.xml ascii|.xml binary| .obj|
|-----------|-----|----------|-----------|-----|
|File size: |172MB| 511MB| 330MB|288MB|
|Zip size: | 68M| 134MB| 74MB| 81MB|
|Read time: | 8s| 270s| 9s| 32s|
|Write time:| 13s| 46s| 13s| 28s|