Search code examples
c++cgalply-file-format

How to write and read a binary PLY file with CGAL?


I can write a binary PLY file with CGAL but I'm not able to read it: either read_PLY returns false (as in my true code), or it crashes the session (as in the code below, "memory not mapped" error).

While write_PLY is successful, it's possible that I don't correctly use it, because I'm not able to read the PLY file with another software as well.

Do you see something wrong in my code below?

#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Polygon_mesh_processing/orient_polygon_soup.h>
#include <CGAL/Polygon_mesh_processing/orientation.h>
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/IO/io.h>
#include <CGAL/Surface_mesh/IO/PLY.h>
#include <fstream>
#include <iostream>
#include <vector>

typedef CGAL::Exact_predicates_exact_constructions_kernel EK;
typedef CGAL::Surface_mesh<EK::Point_3>                   EMesh3;
typedef EK::Point_3                                       EPoint3;
namespace PMP = CGAL::Polygon_mesh_processing;

int main() {
    
  // octahedron soup ----------
  double phi = (1.0 + sqrt(5.0)) / 2.0;
  std::vector<EPoint3> vertices = {EPoint3(0.0, 0.0, phi),  EPoint3(0.0, phi, 0.0),
                                   EPoint3(phi, 0.0, 0.0),  EPoint3(0.0, 0.0, -phi),
                                   EPoint3(0.0, -phi, 0.0), EPoint3(-phi, 0.0, 0.0)};
  std::vector<std::vector<int>> faces = {{1, 0, 2}, {0, 5, 4}, {5, 1, 0},
                                         {0, 2, 4}, {3, 2, 1}, {3, 5, 1},
                                         {2, 3, 4}, {5, 3, 4}};
  bool success = PMP::orient_polygon_soup(vertices, faces);
  if(!success) {
    std::cout << "Polygon orientation failed.";
    return 1;
  }

  // make mesh ----------
  EMesh3 mesh;
  PMP::polygon_soup_to_polygon_mesh(vertices, faces, mesh);

  // write to binary PLY file ----------
  std::ofstream outfile;
  outfile.open("octahedron.ply", std::ios::binary);
  CGAL::IO::set_binary_mode(outfile);
  bool ok = CGAL::IO::write_PLY(outfile, mesh);
  outfile.close();
  if(!ok) {
    std::cout << "Writing file failed.";
    return 1;
  } else {
    std::cout << "Writing file successful.\n";
  }

  // read the PLY file ----------
  EMesh3 mesh2;
  std::ifstream infile("octahedron.ply", std::ios::in|std::ios::binary);
  std::cout << "infile is open: " << infile.is_open();
  bool ok2 = CGAL::IO::read_PLY(infile, mesh2);
  infile.close();
  if(!ok2) {
    std::cout << "Reading file failed.";
    return 1;
  }
  
  // print mesh ----------
  std::cout << mesh2;

  return 0;
}

Solution

  • In write_PLY function of Surface_mesh/IO/PLY.h, after writing the header, it will write all vertices data. If you check the octahedron.ply file, you will see the ascii number after header, not binary value.

    To solve the problem, could you change the kernel to bellow?

    #include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
    typedef CGAL::Exact_predicates_inexact_constructions_kernel EK;
    

    I don't know why CGAL Surface_mesh not work correctly with Exact_predicates_exact_constructions_kernel kernel. Maybe it's a bug, or a desire behavior.

    I explain the current issue as bellow:

    • When using CGAL::Exact_predicates_exact_constructions_kernel kernel, each value in EPoint3 is not a double data type, but a const CGAL::Lazy_exact_nt<boost::multiprecision... type.

    • In this case, the data type is Lazy_exact_nt, then, when writing vertices data, it call to operator << Lazy_exact_nt (file Lazy_exact_nt.h), so the data is written wrongly.
      If the datatype is primitive (check file io_tags.h, the datatype with io_Read_write) will be written correctly.