
* mln/io/off/save.hh (mln::io::off::internal::off_saver): New. Use it to factor... (mln::io::off::save(const bin_2complex_image3df&, const std::string&)): ...this routine. --- milena/ChangeLog | 9 ++ milena/mln/io/off/save.hh | 316 +++++++++++++++++++++++++++++++-------------- 2 files changed, 228 insertions(+), 97 deletions(-) diff --git a/milena/ChangeLog b/milena/ChangeLog index 8c5e2ed..0fb9951 100644 --- a/milena/ChangeLog +++ b/milena/ChangeLog @@ -1,5 +1,14 @@ 2008-11-12 Roland Levillain <roland@lrde.epita.fr> + Introduce a factoring functor to save OFF image files. + + * mln/io/off/save.hh (mln::io::off::internal::off_saver): New. + Use it to factor... + (mln::io::off::save(const bin_2complex_image3df&, const std::string&)): + ...this routine. + +2008-11-12 Roland Levillain <roland@lrde.epita.fr> + Aesthetic changes. * mln/topo/adj_m_face_iter.hh diff --git a/milena/mln/io/off/save.hh b/milena/mln/io/off/save.hh index 4273821..a3fde3a 100644 --- a/milena/mln/io/off/save.hh +++ b/milena/mln/io/off/save.hh @@ -32,6 +32,7 @@ /// \brief Input saving function for OFF files. /// /// \see http://shape.cs.princeton.edu/benchmark/documentation/off_format.html +/// \see https://people.scs.fsu.edu/~burkardt/html/off_format.html # include <cstdlib> @@ -60,134 +61,255 @@ namespace mln \param[in] ima The image to save. \param[in] filename The name of the file where to save the image. - The image is said binary since data only represent the + The image is said binary since data represent only the existence of faces. */ - void save(const bin_2complex_image3df& ima, const std::string& filename); + void save(const bin_2complex_image3df& ima, + const std::string& filename); + + + namespace internal + { + + template <typename I, typename E> + struct off_saver : public Object<E> + { + /// Type of the values. + typedef mln_value(I) value; + + /// Dimension of the built complex. + static const unsigned D = 2; + + /// \brief Constructor, with static checks. + off_saver(); + + /// Load an image from \a filename into \a ima. + void operator()(const I& ima, const std::string& filename) const; + }; + + + struct bin_off_saver + : public off_saver< bin_2complex_image3df, bin_off_saver > + { + /// \brief Save face data. + /// + /// Dummy, does nothings (but required by the super class). + void write_face_data(std::ostream& ostr, const value& v) const; + }; + + } // end of namespace mln::io::off::internal # ifndef MLN_INCLUDE_ONLY - void save(const bin_2complex_image3df& ima, const std::string& filename) + /*----------. + | Facades. | + `----------*/ + + void + save(const bin_2complex_image3df& ima, const std::string& filename) { trace::entering("mln::io::off::save"); + internal::bin_off_saver()(ima, filename); + trace::exiting("mln::io::off::save"); + } + - const std::string me = "mln::io::off::save"; + /*-------------------------. + | Actual implementations. | + `-------------------------*/ - std::ofstream ostr(filename.c_str()); - if (!ostr) + // -------- // + // Canvas. // + // -------- // + + namespace internal + { + + template <typename I, typename E> + off_saver<I, E>::off_saver() { - std::cerr << me << ": `" << filename << "' invalid file." - << std::endl; - /* FIXME: Too violent. We should allow the use of - exceptions, 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); + // Concept checking. + void (E::*m1)(std::ostream&, const value&) const = + &E::write_face_data; + m1 = 0; } - /*---------. - | Header. | - `---------*/ - /* ``The .off files in the Princeton Shape Benchmark conform - to the following standard. */ + template <typename I, typename E> + void + off_saver<I, E>::operator()(const I& ima, + const std::string& filename) const + { + const std::string me = "mln::io::off::save"; + + std::ofstream ostr(filename.c_str()); + if (!ostr) + { + std::cerr << me << ": `" << filename << "' invalid file." + << std::endl; + /* FIXME: Too violent. We should allow the use of + exceptions, 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. '' */ + ostr << "OFF" << std::endl; + + // A comment. + ostr << "# Generated by Milena 1.0 http://olena.lrde.epita.fr\n" + << "# EPITA Research and Development Laboratory (LRDE)" + << std::endl; + + /* ``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.'' */ + /* FIXME: This is too long. We shall be able to write + + ima.domain().template nfaces<0>() + + or even + + ima.template nfaces<0>(). + */ + ostr << ima.domain().cplx().template nfaces<0>() << ' ' + << ima.domain().cplx().template nfaces<2>() << ' ' + << ima.domain().cplx().template nfaces<1>() << std::endl; + + /*-------. + | Data. | + `-------*/ + + // --------- // + // Complex. // + // --------- // + + typedef mln_geom(I) G; + + // ------------------------------------------ // + // Vertices & geometry (vertices locations). // + // ------------------------------------------ // + + /* ``The vertices are listed with x, y, z coordinates, written + one per line.'' */ + + // Traverse the 0-faces (vertices). + p_n_faces_fwd_piter<D, G> v(ima.domain(), 0); + for_all(v) + { + mln_invariant(v.to_site().size() == 1); + ostr << v.to_site().front()[0] << ' ' + << v.to_site().front()[1] << ' ' + << v.to_site().front()[2] << std::endl; + } - /* OFF files are all ASCII files beginning with the keyword - OFF. '' */ - ostr << "OFF" << std::endl; + // --------------- // + // 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.'' */ + + // Traverse the 2-faces (polygons). + p_n_faces_fwd_piter<D, G> f(ima.domain(), 2); + + typedef complex_m_face_neighborhood<D, G> nbh_t; + // A neighborhood where neighbors are the set of 0-faces + // transitively adjacent to the reference point. + nbh_t nbh; + mln_fwd_niter(nbh_t) u(nbh, f); + /* FIXME: We should be able to pas this value (m) either at + the construction of the neighborhood or at the construction + of the iterator. */ + u.iter().set_m(0); + + // For each (2-)face, iterate on (transitively) ajacent + // vertices (0-faces). + for_all(f) + { + unsigned nvertices = 0; + std::ostringstream vertices; + for_all(u) + { + // FIXME: Likewise, this is a bit too long... + vertices << ' ' << u.unproxy_().face().face_id(); + ++nvertices; + } + ostr << nvertices << vertices.str(); + // Possibly save a value (depends on the actual format). + exact(this)->write_face_data(ostr, ima(f)); + ostr << std::endl; + } - // A comment. - ostr << "# Generated by Milena 1.0 http://olena.lrde.epita.fr\n" - << "# EPITA Research and Development Laboratory (LRDE)" - << std::endl; + ostr.close(); + } - /* ``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.'' */ - /* FIXME: This is too long. We shall be able to write + // ---------------- // + // Specific parts. // + // ---------------- // - ima.domain().nfaces<0>() + /** \brief Writing values. - or even + From https://people.scs.fsu.edu/~burkardt/html/off_format.html: - ima.nfaces<0>(). - */ - ostr << ima.domain().cplx().nfaces<0>() << ' ' - << ima.domain().cplx().nfaces<2>() << ' ' - << ima.domain().cplx().nfaces<1>() << std::endl; + ``Following these [coordinates] are the face descriptions, + typically written with one line per face. Each has the + form - /*-------. - | Data. | - `-------*/ + N Vert1 Vert2 ... VertN [color] - // --------- // - // Complex. // - // --------- // + Here N is the number of vertices on this face, and Vert1 + through VertN are indices into the list of vertices (in + the range 0..NVertices-1). - typedef mln::bin_2complex_image3df I; - const unsigned D = I::dim; - typedef mln_geom_(I) G; + The optional color may take several forms. Line breaks + are significant here: the color description begins after + VertN and ends with the end of the line (or the next # + comment). A color may be: - // ------------------------------------------ // - // Vertices & geometry (vertices locations). // - // ------------------------------------------ // + nothing + the default color + one integer + index into "the" colormap; see below + three or four integers + RGB and possibly alpha values in the range 0..255 + three or four floating-point numbers + RGB and possibly alpha values in the range 0..1 - /* ``The vertices are listed with x, y, z coordinates, written - one per line. */ + For the one-integer case, the colormap is currently read + from the file `cmap.fmap' in Geomview's `data' + directory. Some better mechanism for supplying a colormap + is likely someday. - // Traverse the 0-faces (vertices). - p_n_faces_fwd_piter<D, G> v(ima.domain(), 0); - for_all(v) - { - mln_invariant(v.to_site().size() == 1); - ostr << v.to_site().front()[0] << ' ' - << v.to_site().front()[1] << ' ' - << v.to_site().front()[2] << std::endl; - } + The meaning of "default color" varies. If no face of the + object has a color, all inherit the environment's default + material color. If some but not all faces have colors, the + default is gray (R,G,B,A=.666).'' - // --------------- // - // 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.'' */ - - // Traverse the 2-faces (polygons). - p_n_faces_fwd_piter<D, G> f(ima.domain(), 2); - - typedef complex_m_face_neighborhood<D, G> nbh_t; - // A neighborhood where neighbors are the set of 0-faces - // transitively adjacent to the reference point. - nbh_t nbh; - mln_fwd_niter_(nbh_t) u(nbh, f); - /* FIXME: We should be able to pas this value (m) either at - the construction of the neighborhood or at the construction - of the iterator. */ - u.iter().set_m(0); - - // For each (2-)face, iterate on (transitively) ajacent - // vertices (0-faces). - for_all(f) + \{ */ + void + bin_off_saver::write_face_data(std::ostream& /* ostr */, + const value& /* v */) const { - unsigned nvertices = 0; - std::ostringstream vertices; - for_all(u) - { - // FIXME: Likewise, this is a bit too long... - vertices << ' ' << u.unproxy_().face().face_id(); - ++nvertices; - } - ostr << nvertices << vertices.str() << std::endl; + // Do nothing (no data associated to faces). } + /* \} */ - ostr.close(); + } // end of namespace mln::io::off::internal - trace::exiting("mln::io::off::save"); - } # endif // ! MLN_INCLUDE_ONLY -- 1.6.0.1