save.hh 12.8 KB
Newer Older
1
// Copyright (C) 2008, 2009, 2011, 2012 EPITA Research and Development
2
// Laboratory (LRDE)
3
//
4
// This file is part of Olena.
5
//
6
7
8
9
10
// Olena is free software: you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation, version 2 of the License.
//
// Olena is distributed in the hope that it will be useful,
11
12
13
14
15
// 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
16
// along with Olena.  If not, see <http://www.gnu.org/licenses/>.
17
18
//
// As a special exception, you may use this file as part of a free
19
// software project without restriction.  Specifically, if other files
20
// instantiate templates or use macros or inline functions from this
21
22
23
24
25
// 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.
26
27
28
29

#ifndef MLN_IO_OFF_SAVE_HH
# define MLN_IO_OFF_SAVE_HH

30
/// \file
Guillaume Lazzara's avatar
Guillaume Lazzara committed
31
/// Input saving function for OFF files.
32
33
///
/// \see http://shape.cs.princeton.edu/benchmark/documentation/off_format.html
34
/// \see https://people.scs.fsu.edu/~burkardt/html/off_format.html
35
36
37
38
39
40
41
42
43

# include <cstdlib>

# include <iostream>
# include <fstream>
# include <sstream>

# include <string>

44
# include <mln/core/alias/complex_image.hh>
45
46
47
48
49
50
51
52
53
54
55
56
57
# include <mln/core/image/complex_neighborhoods.hh>
# include <mln/core/image/complex_neighborhood_piter.hh>


namespace mln
{

  namespace io
  {

    namespace off
    {

58
      /*! \brief Save a (binary) OFF image into a complex image.
59
60
61
62

	  \param[in] ima      The image to save.
	  \param[in] filename The name of the file where to save the image.

63
	  The image is said binary since data represent only the
64
65
66
67
	  existence of faces.

	  \ingroup iooff
      */
68
69
70
      void save(const bin_2complex_image3df& ima,
		const std::string& filename);

71
      /*! \brief Save an 8-bit grey-level OFF image into a complex image.
72
73
74
75
76

	  \param[in] ima      The image to save.
	  \param[in] filename The name of the file where to save the image.

	  Only data is attached to 2-faces is saved; the OFF file
77
78
79
80
	  cannot store data attached to faces of other dimensions.

	  \ingroup iooff
      */
81
82
83
      void save(const int_u8_2complex_image3df& ima,
		const std::string& filename);

84
      /*! \brief Save a floating-point value grey-level OFF image into
85
86
87
88
89
90
	  a complex image.

	  \param[in] ima      The image to save.
	  \param[in] filename The name of the file where to save the image.

	  Only data is attached to 2-faces is saved; the OFF file
91
92
93
94
	  cannot store data attached to faces of other dimensions.

	  \ingroup iooff
      */
95
96
97
      void save(const float_2complex_image3df& ima,
		const std::string& filename);

98
      /*! \brief Save a 3x8-bit RGB (color) OFF image into a complex image.
99
100
101
102
103

	  \param[in] ima      The image to save.
	  \param[in] filename The name of the file where to save the image.

	  Only data is attached to 2-faces is saved; the OFF file
104
105
106
107
	  cannot store data attached to faces of other dimensions.

	  \ingroup iooff
      */
108
109
110
      void save(const rgb8_2complex_image3df& ima,
		const std::string& filename);

111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126

      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();

127
	  /// Save an image \a ima into \a filename.
128
129
130
131
132
133
134
135
136
137
138
139
140
	  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;
	};

141
142
143
144
145
146
147
	struct int_u8_off_saver
	  : public off_saver< int_u8_2complex_image3df, int_u8_off_saver >
	{
	  /// \brief Save face data.
	  void write_face_data(std::ostream& ostr, const value& v) const;
	};

148
149
150
151
152
153
154
155
156
157
158

	/* FIXME: We should turn float_off_saver into a
	   float01_off_saver (see FIXME/comment in implementation
	   below).  */
	struct float_off_saver
	  : public off_saver< float_2complex_image3df, float_off_saver >
	{
	  /// \brief Save face data.
	  void write_face_data(std::ostream& ostr, const value& v) const;
	};

159
160
161
162
163
164
165
166

	struct rgb8_off_saver
	  : public off_saver< rgb8_2complex_image3df, rgb8_off_saver >
	{
	  /// \brief Save face data.
	  void write_face_data(std::ostream& ostr, const value& v) const;
	};

167
      } // end of namespace mln::io::off::internal
168
169
170
171
172



# ifndef MLN_INCLUDE_ONLY

173
174
175
176
      /*----------.
      | Facades.  |
      `----------*/

177
      inline
178
179
      void
      save(const bin_2complex_image3df& ima, const std::string& filename)
180
      {
Guillaume Lazzara's avatar
Guillaume Lazzara committed
181
	mln_trace("mln::io::off::save");
182
183
184
	internal::bin_off_saver()(ima, filename);
      }

185
      inline
186
187
188
      void
      save(const int_u8_2complex_image3df& ima, const std::string& filename)
      {
Guillaume Lazzara's avatar
Guillaume Lazzara committed
189
	mln_trace("mln::io::off::save");
190
191
192
	internal::int_u8_off_saver()(ima, filename);
      }

193
      inline
194
195
196
      void
      save(const float_2complex_image3df& ima, const std::string& filename)
      {
Guillaume Lazzara's avatar
Guillaume Lazzara committed
197
	mln_trace("mln::io::off::save");
198
199
200
	internal::float_off_saver()(ima, filename);
      }

201
      inline
202
203
204
      void
      save(const rgb8_2complex_image3df& ima, const std::string& filename)
      {
Guillaume Lazzara's avatar
Guillaume Lazzara committed
205
	mln_trace("mln::io::off::save");
206
207
208
	internal::rgb8_off_saver()(ima, filename);
      }

209

210
211
212
      /*-------------------------.
      | Actual implementations.  |
      `-------------------------*/
213

214
215
216
217
218
219
220
221
222
      // -------- //
      // Canvas.  //
      // -------- //

      namespace internal
      {

	template <typename I, typename E>
	off_saver<I, E>::off_saver()
223
	{
224
225
226
	  // Concept checking.
	  void (E::*m1)(std::ostream&, const value&) const =
	    &E::write_face_data;
227
	  (void) m1;
228
229
230
	}


231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
	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

272
	     ima.domain().template nfaces_of_static_dim<0>()
273
274
275

	     or even

276
	     ima.template nfaces_of_static_dim<0>().
277
	  */
278
279
280
281
	  ostr << ima.domain().cplx().template nfaces_of_static_dim<0>() << ' '
	       << ima.domain().cplx().template nfaces_of_static_dim<2>() << ' '
	       << ima.domain().cplx().template nfaces_of_static_dim<1>()
	       << std::endl;
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308

	  /*-------.
	  | 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;
	  }
309

310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
	  // --------------- //
	  // 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);
327
	  /* FIXME: We should be able to pass this value (m) either at
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
	     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;
	  }
349

350
351
	  ostr.close();
	}
352

353
354
355
	// ---------------- //
	// Specific parts.  //
	// ---------------- //
356

357
	/** \brief Writing values.
358

359
	    From https://people.scs.fsu.edu/~burkardt/html/off_format.html:
360

361
362
363
	    ``Following these [coordinates] are the face descriptions,
	    typically written with one line per face. Each has the
	    form
364

365
	    N  Vert1 Vert2 ... VertN  [color]
366

367
368
369
	    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).
370

371
372
373
374
	    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:
375

376
377
378
379
380
381
382
383
	    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
384

385
386
387
388
	    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.
389

390
391
392
393
	    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).''
394

395
	    \{ */
396
	inline
397
398
399
	void
	bin_off_saver::write_face_data(std::ostream& /* ostr */,
				       const value& /* v */) const
400
	{
401
	  // Do nothing (no data associated to faces).
402
	}
403

404
	inline
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
	void
	int_u8_off_saver::write_face_data(std::ostream& ostr,
					  const value& v) const
	{
	  /* Using RGBA colors to represent an 8-bit integer value.

	     Each channel (R, G, B) of the color V is an integer in
	     the range 0..255.  A fourth channel, A, controls the
	     transparency.

	     We just set the same value for each channel, as the OFF
	     file format does not support gray-level values as-is.  */
	  ostr << ' ' << v << ' ' << v << ' ' << v
	       << ' ' << 1.0f << std::endl;
	}
420
421
422

	/* FIXME: We should turn float_off_saver into a
	   float01_off_saver to avoid the assertions below.  */
423
	inline
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
	void
	float_off_saver::write_face_data(std::ostream& ostr,
					 const value& v) const
	{
	  /* Using RGBA colors to represent a floating-point value.

	     Each channel (R, G, B) of the color V is a floating-point
	     number in the range 0..1.  A fourth channel, A, controls
	     the transparency.

	     We just set the same value for each channel, as the OFF
	     file format does not support gray-level values as
	     such.  */
	  mln_assertion(0.0f <= v);
	  mln_assertion(v <= 1.0f);
	  ostr << ' ' << v << ' ' << v << ' ' << v
440
	       << ' ' << 1.0f;
441
	}
442

443
	inline
444
445
446
447
448
449
450
451
452
453
	void
	rgb8_off_saver::write_face_data(std::ostream& ostr,
					const value& v) const
	{
	  /* RGBA color.

	     Each channel (R, G, B) of the color V is an integer in
	     the range 0..255.  A fourth channel, A, controls the
	     transparency.  */
	  ostr << ' ' << v.red() << ' ' << v.green() << ' ' << v.blue()
454
	       << ' ' << 1.0f;
455
	}
456
	/* \} */
457

458
      } // end of namespace mln::io::off::internal
459

460
461
462
463
464
465
466
467
468
469
470

# endif // ! MLN_INCLUDE_ONLY

    } // end of namespace mln::io::off

  } // end of namespace mln::io

} // end of namespace mln


#endif // ! MLN_IO_OFF_SAVE_HH