2585: Add a routine to create a complex-based image from an OFF file.

* mln/io/off/load.hh: New. --- milena/ChangeLog | 6 + milena/mln/io/off/load.hh | 308 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 314 insertions(+), 0 deletions(-) create mode 100644 milena/mln/io/off/load.hh diff --git a/milena/ChangeLog b/milena/ChangeLog index 30e48e9..546bec0 100644 --- a/milena/ChangeLog +++ b/milena/ChangeLog @@ -1,5 +1,11 @@ 2008-10-16 Roland Levillain <roland@lrde.epita.fr> + Add a routine to create a complex-based image from an OFF file. + + * mln/io/off/load.hh: New. + +2008-10-16 Roland Levillain <roland@lrde.epita.fr> + Add a macro containing the path to meshes, to be used in tests. * tests/data.hh.in (MLN_MESH_DIR): New macro. diff --git a/milena/mln/io/off/load.hh b/milena/mln/io/off/load.hh new file mode 100644 index 0000000..ff8d106 --- /dev/null +++ b/milena/mln/io/off/load.hh @@ -0,0 +1,308 @@ +// Copyright (C) 2008 EPITA Research and Development Laboratory (LRDE) +// +// This file is part of the Olena Library. This library is free +// software; you can redistribute it and/or modify it under the terms +// of the GNU General Public License version 2 as published by the +// Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this library; see the file COPYING. If not, write to +// the Free Software Foundation, 51 Franklin Street, Fifth Floor, +// Boston, MA 02111-1307, USA. +// +// As a special exception, you may use this file as part of a free +// software library without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to +// produce an executable, this file does not by itself cause the +// resulting executable to be covered by the GNU General Public +// License. This exception does not however invalidate any other +// reasons why the executable file might be covered by the GNU General +// Public License. + +#ifndef MLN_IO_OFF_LOAD_HH +# define MLN_IO_OFF_LOAD_HH + +/// \file mln/io/off/load.hh +/// \brief Input loading function for OFF files. +/// +/// \see http://shape.cs.princeton.edu/benchmark/documentation/off_format.html + +# include <cstdlib> + +# include <iostream> +# include <fstream> + +# include <string> + +# include <mln/core/image/complex_image.hh> +# include <mln/geom/complex_geometry.hh> +# include <mln/core/alias/point3df.hh> + +// // FIXME: Really needed? +// # include <mln/topo/n_faces_set.hh> + + +namespace mln +{ + + namespace io + { + + namespace off + { + + /** \brief Load a (boolean) OFF image into a complex image. + + \param[out] ima A reference to the image to construct. + \param[in] filename The name of the file to load. + + The image is said boolean since data only represent the + existence of vertices and faces. */ + void load(complex_image<2, geom::complex_geometry<2,point3df>, bool>& ima, + const std::string& filename); + + namespace internal + { + /// A stream manipulator eating comments starting with a `#' + /// and ending at an end of line + std::istream& eat_comment (std::istream& istr); + } + + +# ifndef MLN_INCLUDE_ONLY + + void load(complex_image<2 ,geom::complex_geometry<2,point3df>, bool>& ima, + const std::string& filename) + { + const std::string me = "mln::io::load::off"; + + std::ifstream istr(filename.c_str()); + if (!istr) + { + std::cerr << me << ": `" << filename << "' not found." << std::endl; + /* FIXME: Too violent. We should allow the use of + exception, at least to have Milena's code behave + correctly in interpreted environments (std::exit() or + std::abort() causes the termination of a Python + interpreter, for instance!). */ + std::exit(1); + } + + /*---------. + | Header. | + `---------*/ + + /* ``The .off files in the Princeton Shape Benchmark conform + to the following standard. */ + + /* OFF files are all ASCII files beginning with the keyword + OFF. '' */ + std::string type; + istr >> internal::eat_comment >> type; + if (type != "OFF") + { + std::cerr << me << ": `" << filename << "': ill-formed header." + << std::endl; + std::exit(1); + } + + /* ``The next line states the number of vertices, the number + of faces, and the number of edges. The number of edges can + be safely ignored.'' */ + unsigned nvertices, nfaces, nedges; + istr >> internal::eat_comment >> nvertices + >> internal::eat_comment >> nfaces + >> internal::eat_comment >> nedges; + + /*-------. + | Data. | + `-------*/ + + /* FIXME: Maybe we could sugar all this (using make_whatever + helpers?). */ + + // --------- // + // Complex. // + // --------- // + + const unsigned D = 2; + topo::complex<D> c; + + // ------------------------------------------ // + // Vertices & geometry (vertices locations). // + // ------------------------------------------ // + + /* ``The vertices are listed with x, y, z coordinates, written + one per line. */ + + /* FIXME: We should have a faster way to create a bunch of + 0-faces (vertices). */ + for (unsigned v = 0; v < nvertices; ++v) + c.add_face(); + + typedef point3df P; + typedef mln_coord_(P) C; + typedef geom::complex_geometry<D, P> G; + G geom; + for (unsigned v = 0; v < nvertices; ++v) + { + C x, y, z; + istr >> internal::eat_comment >> x + >> internal::eat_comment >> y + >> internal::eat_comment >> z; + geom.add_location(point3df(x, y, z)); + } + + // --------------- // + // Faces & edges. // + // --------------- // + + /* After the list of vertices, the faces are listed, with one + face per line. For each face, the number of vertices is + specified, followed by indices into the list of + vertices.'' */ + + // An adjacenty matrix recording the edges seen so far. + typedef std::vector< std::vector<bool> > complex_edges_t; + complex_edges_t complex_edges (nvertices, + std::vector<bool>(nvertices, false)); + + for (unsigned f = 0; f < nfaces; ++f) + { + unsigned nface_vertices; + istr >> internal::eat_comment >> nface_vertices; + if (nface_vertices <= 2) + { + std::cerr << me << ": `" << filename + << "': ill-formed face (" << nface_vertices + << " vertices)" << std::endl; + std::exit(1); + } + + // The edges of the face. + topo::n_faces_set<1, D> face_edges_set; + face_edges_set.reserve(nface_vertices); + + // The first vertex id of the face. + unsigned first_vertex_id; + istr >> internal::eat_comment >> first_vertex_id; + // The current vertex id initialized with the first id. + unsigned vertex_id = first_vertex_id; + if (first_vertex_id >= nvertices) + { + std::cerr << me << ": `" << filename + << "': invalid vertex id " << first_vertex_id + << std::endl; + std::exit(1); + } + // Iterate on vertices and form edges. + for (unsigned v = 0; v < nface_vertices; ++v) + { + /* The next vertex id. The pair (vertex_id, next_vertex_id) + is an edge of the mesh/complex. */ + unsigned next_vertex_id; + /* When V is the id of the last vertex of the face F, + set next_vertex_id to first_vertex_id; otherwise, + read it from the input. */ + if (v == nface_vertices - 1) + next_vertex_id = first_vertex_id; + else + { + istr >> internal::eat_comment >> next_vertex_id; + if (next_vertex_id >= nvertices) + { + std::cerr << me << ": `" << filename + << "': invalid vertex id " << next_vertex_id + << std::endl; + std::exit(1); + } + } + // The ends of the current edge. + topo::n_face<0, D> vertex(c, vertex_id); + topo::n_face<0, D> next_vertex(c, next_vertex_id); + // The current edge. + topo::n_face<1, D> edge; + // If the edge has been constructed yet, create it; + // otherwise, retrieve its id from the complex. + if (!complex_edges[vertex_id][next_vertex_id]) + { + complex_edges[vertex_id][next_vertex_id] = true; + complex_edges[next_vertex_id][vertex_id] = true; + edge = c.add_face(vertex + next_vertex); + } + else + { + edge = topo::edge(vertex, next_vertex); + mln_assertion(edge.is_valid()); + } + // Record this edge. + face_edges_set += edge; + // Next vertex. + vertex_id = next_vertex_id; + } + + // Add face. + c.add_face(face_edges_set); + } + + /*--------. + | Image. | + `--------*/ + + // Value type. + typedef bool V; + + // Site set. + p_complex<D, G> pc(c, geom); + + // Values. + metal::vec<D + 1, std::vector<V> > values; + for (unsigned i = 0; i <= D; ++i) + values[i].insert(values[i].begin(), pc.cplx().nfaces(i), true); + + // Image. + ima.init_(pc, values); + + istr.close(); + } + + + namespace internal + { + /// A stream manipulator eating comments starting with a `#' + /// and ending at an end of line + std::istream& eat_comment (std::istream& istr) + { + // Skip whitespace and newlines. + std::ws(istr); + while (istr.peek() == '#') + { + /* Eat the `#' and the rest of the line until `\n' or + `\r' is found or the end of the file is reached. */ + char c; + do + istr.get(c); + while (c != '\n' && c != '\r' && !istr.eof()); + // Skip whitespace and newlines. + std::ws(istr); + } + return istr; + } + } + +# endif // ! MLN_INCLUDE_ONLY + + } // end of namespace mln::io::off + + } // end of namespace mln::io + +} // end of namespace mln + + +#endif // ! MLN_IO_OFF_LOAD_HH -- 1.6.0.1
participants (1)
-
Roland Levillain