CGAL 5.4.1 - IO Streams
User Manual

Authors
Andreas Fabri, Geert-Jan Giezeman, Lutz Kettner, and Maxime Gimeno

Introduction

An algorithm is the application of a serie of steps to convert some input data into output data. As such, it is necessary to have a way to input and output external data into CGAL data structures. This is achieved using input/output (I/O) streams of data, which enables reading and writing to and from files, the console, or other custom structures.

Outline

  • Reading and Writing Data With Streams : the most essential data structures of CGAL, its kernel objects, all provide adapted input and output operators. In addition, CGAL provides tools to enhance the existing stream system of the STL to easily read and write classes external to CGAL.
  • Importing and Exporting Data using Standard File Formats : a number of traditional data structures such as point sets or polygon meshes have well-established standards specifying a particular data format to facilitate data exchanges. Formats supported for CGAL data structures are detailed in this section; the page Supported File Formats offers the reversed viewpoint (file format to CGAL data structure). Finally, an alternative to standard file formats is serialization of data, see Section Reading Unsupported Formats.

Stream Precision

When manipulating floating point-based number types, it is important to always keep in mind that the stream of the precision should be chosen appropriately, lest potentially significant loss of data might incur. For example, consider the following extreme case:

double v = 184, w = 182;
std::cout << std::setprecision(2) << v << " " << w << std::endl;

This snipplet will produce the output 1.8e+02 1.8e+02 despite v and w being different. As the default precision of output streams of the STL is 6, this can be a real source of bugs and wrong outputs.

The stream precision of STL streams can be modified using std::setprecision. File I/O functions also offer an optional named parameter to set the output stream precision.

Reading and Writing Data With Streams

All classes in the CGAL kernel provide input and output operators for I/O streams. Classes external to CGAL are also supported, by means of oformat() (Section I/O for Non CGAL Types). The basic task of such an operator is to produce a representation of an object that can be written as a sequence of characters on devices as a console, a file, or a pipe. In CGAL we distinguish between a raw ASCII, a raw binary, and a pretty printing format.

enum Mode {ASCII = 0, BINARY, PRETTY};

In ASCII mode, objects are written as a set of numbers, e.g. the coordinates of a point or the coefficients of a line, in a machine independent format. In BINARY mode, data are written in a binary format, e.g. a double is represented as a sequence of four byte. The format depends on the machine. The mode PRETTY serves mainly for debugging as the type of the geometric object is written, as well as the data defining the object. For example for a point at the origin with Cartesian double coordinates, the output would be PointC2(0.0, 0.0). At the moment CGAL does not provide input operations for pretty printed data. By default a stream is in ASCII mode.

CGAL provides the following functions to modify the mode of an I/O stream.

The following functions enable testing whether a stream is in a certain mode:

IO::Mode get_mode(std::ios& s);
bool is_ascii(std::ios& s);
bool is_binary(std::ios& s);
bool is_pretty(std::ios& s);

Input Operator

CGAL defines input operators for classes that are derived from the class istream. This allows to read from istreams as std::cin, as well as from std::istringstream and std::ifstream. The input operator is defined for all classes in the CGAL Kernel. Let is be an input stream.

// Extracts object `c` from the stream `is`. Returns `is`.
istream& operator>>(istream& is, Class c);
#include <iostream>
#include <fstream>
#include <CGAL/Cartesian.h>
#include <CGAL/Segment_2.h>
int
main()
{
Point p, q;
Segment s;
std::cin >> p >> q;
std::ifstream f("data.txt");
f >> s >> p;
return 1;
}

Output Operator

CGAL defines output operators for classes that are derived from the class ostream. This allows to write to ostreams as std::cout or std::cerr, as well as to std::ostringstream and std::ofstream. The output operator is defined for all classes in the CGAL Kernel and for the class Color (see Section Colors) as well. Let os be an output stream.

// Inserts object `c` in the stream `os`. Returns `os`.
ostream& operator<<(ostream& os, Class c);
#include <iostream>
#include <fstream>
#include <CGAL/Cartesian.h>
#include <CGAL/Segment_2.h>
int main()
{
Point p(0,1), q(2,2);
Segment s(p,q);
std::cout << p << std::endl << q << std::endl;
std::ofstream f("data.txt");
f << s << p ;
return 1;
}

IO for Non-CGAL Types

Using Output Formatting

To ensure that non-CGAL types are formatted correctly (i.e., respecting IO::Mode ), oformat() can be used. For types with a Output_rep specialization, the respective output routine of Output_rep will be called by oformat(). Otherwise, the stream output operator will be called.

std::cout << CGAL::IO::oformat( myobject );

Optionally, you can provide a second template parameter F as a formatting tag:

std::cout << CGAL::IO::oformat( myobject, My_formatting_tag() );

For a list of formatting tags supported by the type T, please refer to the documentation of the respective type.

Customizing Output Formatting

In some situations, you want to control the output formatting for a type T. For external types (third party libraries etc.), there might be problems if their stream output operator does not respect IO::Mode . The purpose of Output_rep is to provide a way to control output formatting that works independently of the object's stream output operator.

Instead of putting T directly into an output stream, T is wrapped into an output representation Output_rep. For convenience, a function oformat() exists, which constructs an instance of Output_rep.

If you do not specialize Output_rep for T, T's stream output operator is called from within Output_rep, by default. If you want another behaviour for your type T, you have to provide a specialization for that type. Furthermore, you can provide specializations with a second template parameter (a formatting tag). The second template parameter defaults to Null_tag and means default behaviour.

For example, specializing Output_rep for CORE::BigRat (without a formatting tag parameter) could look like this:

template <class F>
class Output_rep< ::CORE::BigRat, F> {
const ::CORE::BigRat& t;
public:
Output_rep( const ::CORE::BigRat& tt) : t(tt) {}
std::ostream& operator()( std::ostream& out) const {
switch (get_mode(out)) {
case IO::PRETTY:{
if(CGAL_CORE_DENOMINATOR(t) == ::CORE::BigRat(1))
return out <<CGAL_CORE_NUMERATOR(t);
else
return out << CGAL_CORE_NUMERATOR(t)
<< "/"
<< CGAL_CORE_DENOMINATOR(t);
break;
}
default:
return out << CGAL_CORE_NUMERATOR(t)
<< "/"
<< CGAL_CORE_DENOMINATOR(t);
}
}
};

Colors

An object of the class Color is a color available for drawing operations in many CGAL output streams.

Each color is defined by a triple of integers (r,g,b) with 0 \( \le \) r,g,b \( \le \) 255, the so-called rgb-value of the color. There are a 11 predefined Color functions available: black(), white(), gray(), red(), green(), deep_blue(), blue(), purple(), violet(), orange(), and yellow().

Stream Support

Three classes are provided by CGAL as adaptors to input and output stream iterators. The class Istream_iterator is an input iterator adaptor and is particularly useful for classes that are similar but not compatible to std::istream. Similarly, the class Ostream_iterator is an output iterator adaptor. The class Verbose_ostream can be used as an output stream. The stream output operator << is defined for any type. The class stores in an internal state a stream and whether the output is active or not. If the state is active, the stream output operator << uses the internal stream to output its argument. If the state is inactive, nothing happens.

Importing and Exporting Data using Standard File Formats

Specific standards have been created to facilite the exchange of data for traditional data structures such as point sets or polygon meshes.

CGAL aims to provide a uniform and consistent approach for Input/Output functions, providing for a given file format with shorter name XXX an input function read_XXX(input, data_structure), and an output function called write_XXX(output, data_structure). Whenever possible, a generic I/O function which can deduce the file format from the extension is also provided for convenience.

In the following subsections, file formats supported in CGAL are listed along with compatible CGAL data structures. A reverse viewpoint, listing the data structures which can be used for a specific file format is available on the page: Supported File Formats.

Note that these categories will grow as the on-going effort to uniformize I/O in CGAL progresses.

Point Set IO

A set of points - possibly with normals - is the basic input of many algorithms. The following table shows which file formats can be read from and written for point ranges.

Generic OFF XYZ PLY LAS
Input read_points() read_OFF() read_XYZ() read_PLY() read_LAS()
Output write_points() write_OFF() write_XYZ() write_PLY() write_LAS()

All of these functions (with the exception of the LAS format) can read and write either points alone or points with normals (depending on whether the normal_map named parameter is used by the user or not).

Note that the PLY format handles both ASCII and binary formats. In addition, PLY and LAS are extensible formats that can embed additional properties. These can also be read by CGAL (see Section Points With Properties).

The class CGAL::Point_set_3 is the data structure used in CGAL to represent point sets. It is a vector-based data structure that contains a default property (named point) for the coordinates of the points, and is able to work with dynamic properties. The file formats supported for CGAL::Point_set_3 are detailed in the table below.

Generic OFF XYZ PLY LAS
Input read_point_set() read_OFF() read_XYZ() read_PLY() read_LAS()
Output write_point_set() write_OFF() write_XYZ() write_PLY() write_LAS()

Polygon Soup IO

A polygon soup is a set of polygons with no global combinatorial information, stored in a two containers: one storing the points, and the other one storing their indices per face (i.e a vector of 3 integers represent a triangle face).

Generic OFF OBJ STL PLY VTP GOCAD WKT 3MF
Input read_polygon_soup() read_OFF() read_OBJ() read_STL() read_PLY() read_VTP() read_GOCAD() read_WKT() read_3MF()
Output write_polygon_soup() write_OFF() write_OBJ() write_STL() write_PLY() write_VTP() write_GOCAD() write_WKT() write_3MF()

Polygon Mesh IO

A polygon mesh is a consistent and orientable surface mesh, that can have one or more boundaries. This refines the concept of FaceGraph with some additional restrictions; a complete definition can be found here. The table above only lists the functions that work with any polygon mesh.

Generic OFF STL VTP OBJ GOCAD WRL
Input read_polygon_mesh() read_OFF() read_STL() read_VTP() read_OBJ() read_GOCAD()
-
Output write_polygon_mesh() write_OFF() write_STL() write_VTP() write_OBJ() write_GOCAD() write_WRL()

Some particular polygon mesh data structures such as CGAL::Surface_mesh have specific overloads of these functions, enabling reading and writing of dynamic information for some file format. See the reference manual of each data structure for more information.

The functions above require the input to represent a 2-manifold surface (possibly with boundaries). If this is not the case, the package Polygon Mesh Processing offers the function CGAL::Polygon_mesh_processing::IO::read_polygon_mesh() which can perform some combinatorial repairing to ensure the input data is a 2-manifold.

Simple 2D Geometry IO

WKT stands for Well-Known Text and it is a text markup language for representing vector geometry objects on a geographical map. See the wikipedia page for details. CGAL supports a subset of WKT types: point, multipoint, linestring, multilinestring, polygon and multipolygon. Free functions are provided for reading and writing several CGAL types using these WKT types, namely:

You can find more information about WKT here. The following table summarizes the most useful functions. Note that the name deviates from the standard CGAL I/O functions.

Generic Points Polylines Polygons
Input read_WKT() read_multi_point_WKT() read_multi_linestring_WKT() read_multi_polygon_WKT()
Output
-
write_multi_point_WKT() write_multi_linestring_WKT() write_multi_polygon_WKT()

Other Data Structures

Other data structures of CGAL have their own I/O functions, see their respective user and reference manuals.

Reading Unsupported Formats

If CGAL does not support the file format of your data, the boost::property_tree might come in handy if your data has a simple-enough structure. The following small example shows how to parse an XML file, which might look as follows:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<PolySet>
<Polygon>
<Point X="-715.8811978465" Y="-2729.9490000000" Z="-534.9000000000"/>
<Point X="-718.1905989232" Y="-2729.9490000000" Z="-538.9000000000"/>
<Point X="-722.8094010768" Y="-2729.9490000000" Z="-538.9000000000"/>
</Polygon>
</PolySet>


File Stream_support/read_xml.cpp

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <vector>
#include <fstream>
typedef K::Point_3 Point_3;
int main(int argc, char* argv[])
{
std::ifstream in( (argc>1)? argv[1] : "data/cloud.pol");
boost::property_tree::ptree tree;
boost::property_tree::read_xml(in, tree);
std::vector<Point_3> points;
for(boost::property_tree::ptree::value_type& node : tree.get_child("PolySet.Polygon")){
boost::property_tree::ptree subtree = node.second;
if( node.first == "Point" ){
for( boost::property_tree::ptree::value_type const& v : subtree.get_child( "" ) ) {
std::string label = v.first;
if ( label == "<xmlattr>" ) {
Point_3 p(subtree.get<double>( label+".X"),
subtree.get<double>( label+".Y"),
subtree.get<double>( label+".Z"));
points.push_back(p);
}
}
}
}
std::cout << points.size() << " points read"<< std::endl;
return 0;
}