* 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(a)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(a)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