Milena (Olena)  User documentation 2.0a Id
 All Classes Namespaces Functions Variables Typedefs Enumerator Groups Pages
off/load.hh
1 // Copyright (C) 2008, 2009, 2011 EPITA Research and Development
2 // Laboratory (LRDE)
3 //
4 // This file is part of Olena.
5 //
6 // Olena is free software: you can redistribute it and/or modify it under
7 // the terms of the GNU General Public License as published by the Free
8 // Software Foundation, version 2 of the License.
9 //
10 // Olena is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
14 //
15 // 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 // file, or you compile this file and link it with other files to produce
22 // an executable, this file does not by itself cause the resulting
23 // executable to be covered by the GNU General Public License. This
24 // exception does not however invalidate any other reasons why the
25 // executable file might be covered by the GNU General Public License.
26 
27 #ifndef MLN_IO_OFF_LOAD_HH
28 # define MLN_IO_OFF_LOAD_HH
29 
36 
37 # include <cstdlib>
38 # include <iostream>
39 # include <fstream>
40 # include <string>
41 
42 # include <mln/literal/black.hh>
43 # include <mln/core/concept/object.hh>
44 # include <mln/core/alias/complex_image.hh>
45 
46 
47 namespace mln
48 {
49 
50  namespace io
51  {
52 
53  namespace off
54  {
55 
63  void load(bin_2complex_image3df& ima, const std::string& filename);
64 
65  // FIXME: Implement a load routine for for
66  // int_u8_2complex_image3df.
67 
75  void load(float_2complex_image3df& ima, const std::string& filename);
76 
84  void load(rgb8_2complex_image3df& ima, const std::string& filename);
85 
86 
87  namespace internal
88  {
89 
90  template <typename I, typename E>
91  struct off_loader : public Object<E>
92  {
93  typedef off_loader<I, E> self;
94 
96  static const unsigned D = 2;
98  typedef metal::vec<D + 1, std::vector< mln_value(I) > > values;
100  typedef mln_domain(I) domain;
101 
103  off_loader();
104 
106  void operator()(I& ima, const std::string& filename);
107 
110  static std::istream& eat_comment(std::istream& istr);
111  };
112 
113 
114  struct bin_off_loader
115  : public off_loader< bin_2complex_image3df, bin_off_loader >
116  {
120  void read_face_data(std::istream& istr);
121 
123  void assign(values& vs, const domain& s);
124 
128  void reserve(unsigned nvertices, unsigned nedges, unsigned nfaces);
129  };
130 
131 
132  /* Factor float_off_loader::reserve and rgb8_off_loader::reserve
133  and float_off_loader::assign and rgb8_off_loader::assign
134  by introducing an intermediate class. */
135 
136  struct float_off_loader
137  : public off_loader< float_2complex_image3df, float_off_loader >
138  {
140  void read_face_data(std::istream& istr);
141 
143  void reserve(unsigned nvertices, unsigned nedges, unsigned nfaces);
144 
146  void assign(values& vs, const domain& s);
147 
149  std::vector<float> face_value;
150  };
151 
152 
153  struct rgb8_off_loader
154  : public off_loader< rgb8_2complex_image3df, rgb8_off_loader >
155  {
157  void read_face_data(std::istream& istr);
158 
160  void reserve(unsigned nvertices, unsigned nedges, unsigned nfaces);
161 
163  void assign(values& vs, const domain& s);
164 
166  std::vector<value::rgb8> face_value;
167  };
168 
169  } // end of namespace mln::io::off::internal
170 
171 
172 
173 # ifndef MLN_INCLUDE_ONLY
174 
175  /*----------.
176  | Facades. |
177  `----------*/
178 
179  inline
180  void
181  load(bin_2complex_image3df& ima, const std::string& filename)
182  {
183  trace::entering("mln::io::off::load");
184  internal::bin_off_loader()(ima, filename);
185  trace::exiting("mln::io::off::load");
186  }
187 
188  inline
189  void
190  load(float_2complex_image3df& ima, const std::string& filename)
191  {
192  trace::entering("mln::io::off::load");
193  internal::float_off_loader()(ima, filename);
194  trace::exiting("mln::io::off::load");
195  }
196 
197  inline
198  void
199  load(rgb8_2complex_image3df& ima, const std::string& filename)
200  {
201  trace::entering("mln::io::off::load");
202  internal::rgb8_off_loader()(ima, filename);
203  trace::exiting("mln::io::off::load");
204  }
205 
206 
207 
208  /*-------------------------.
209  | Actual implementations. |
210  `-------------------------*/
211 
212  // -------- //
213  // Canvas. //
214  // -------- //
215 
216  namespace internal
217  {
218 
219  template <typename I, typename E>
220  inline
221  off_loader<I, E>::off_loader()
222  {
223  // Concept checking.
224  void (E::*m1)(std::istream&) = &E::read_face_data;
225  m1 = 0;
226  void (E::*m2)(unsigned, unsigned, unsigned) = &E::reserve;
227  m2 = 0;
228  void (E::*m3)(values&, const domain&) = &E::assign;
229  m3 = 0;
230  }
231 
232 
233  template <typename I, typename E>
234  inline
235  void
236  off_loader<I, E>::operator()(I& ima, const std::string& filename)
237  {
238  const std::string me = "mln::io::off::load";
239 
240  std::ifstream istr(filename.c_str());
241  if (!istr)
242  {
243  std::cerr << me << ": `" << filename << "' not found."
244  << std::endl;
245  /* FIXME: Too violent. We should allow the use of
246  exceptions, at least to have Milena's code behave
247  correctly in interpreted environments (std::exit() or
248  std::abort() causes the termination of a Python
249  interpreter, for instance!). */
250  std::exit(1);
251  }
252 
253  /*---------.
254  | Header. |
255  `---------*/
256 
257  /* ``The .off files in the Princeton Shape Benchmark conform
258  to the following standard''. */
259 
260  /* ``OFF files are all ASCII files beginning with the keyword
261  OFF. '' */
262  std::string type;
263  istr >> &self::eat_comment >> type;
264  if (type != "OFF")
265  {
266  std::cerr << me << ": `" << filename << "': ill-formed header."
267  << std::endl;
268  std::exit(1);
269  }
270 
271  /* ``The next line states the number of vertices, the number
272  of faces, and the number of edges. The number of edges can
273  be safely ignored.'' */
274  unsigned nvertices, nfaces, nedges;
275  istr >> &self::eat_comment >> nvertices
276  >> &self::eat_comment >> nfaces
277  >> &self::eat_comment >> nedges;
278 
280  exact(this)->reserve(nvertices, nedges, nfaces);
281 
282  /*-------.
283  | Data. |
284  `-------*/
285 
286  /* FIXME: Maybe we could sugar all this (using make_whatever
287  helpers?). */
288 
289  // --------- //
290  // Complex. //
291  // --------- //
292 
293  const unsigned D = 2;
294  topo::complex<D> c;
295 
296  // ------------------------------------------ //
297  // Vertices & geometry (vertices locations). //
298  // ------------------------------------------ //
299 
300  /* ``The vertices are listed with x, y, z coordinates, written
301  one per line.'' */
302 
303  /* FIXME: We should have a faster way to create a bunch of
304  0-faces (vertices). */
305  for (unsigned v = 0; v < nvertices; ++v)
306  c.add_face();
307 
308  typedef point3df P;
309  typedef mln_coord_(P) C;
310  typedef geom::complex_geometry<D, P> G;
311  G geom;
312  for (unsigned v = 0; v < nvertices; ++v)
313  {
314  C x, y, z;
315  istr >> &self::eat_comment >> x
316  >> &self::eat_comment >> y
317  >> &self::eat_comment >> z;
318  geom.add_location(point3df(x, y, z));
319  }
320 
321  // --------------- //
322  // Faces & edges. //
323  // --------------- //
324 
325  /* ``After the list of vertices, the faces are listed, with
326  one face per line. For each face, the number of vertices
327  is specified, followed by indices into the list of
328  vertices.'' */
329 
330  // An adjacenty matrix recording the edges seen so far.
331  typedef std::vector< std::vector<bool> > complex_edges_t;
332  complex_edges_t complex_edges (nvertices,
333  std::vector<bool>(nvertices, false));
334 
335  for (unsigned f = 0; f < nfaces; ++f)
336  {
337  unsigned nface_vertices;
338  istr >> &self::eat_comment >> nface_vertices;
339  if (nface_vertices <= 2)
340  {
341  std::cerr << me << ": `" << filename
342  << "': ill-formed face (having "
343  << nface_vertices << ' '
344  << (nface_vertices < 2 ? "vertex" : "vertices")
345  << ')' << std::endl;
346  std::exit(1);
347  }
348 
349  // The edges of the face.
350  topo::n_faces_set<1, D> face_edges_set;
351  face_edges_set.reserve(nface_vertices);
352 
353  // The first vertex id of the face.
354  unsigned first_vertex_id;
355  istr >> &self::eat_comment >> first_vertex_id;
356  // The current vertex id initialized with the first id.
357  unsigned vertex_id = first_vertex_id;
358  if (first_vertex_id >= nvertices)
359  {
360  std::cerr << me << ": `" << filename
361  << "': invalid vertex id " << first_vertex_id
362  << std::endl;
363  std::exit(1);
364  }
365  // Iterate on vertices and form edges.
366  for (unsigned v = 0; v < nface_vertices; ++v)
367  {
368  /* The next vertex id. The pair (vertex_id,
369  next_vertex_id) is an edge of the
370  mesh/complex. */
371  unsigned next_vertex_id;
372  /* When V is the id of the last vertex of the face F,
373  set next_vertex_id to first_vertex_id; otherwise,
374  read it from the input. */
375  if (v == nface_vertices - 1)
376  next_vertex_id = first_vertex_id;
377  else
378  {
379  istr >> &self::eat_comment >> next_vertex_id;
380  if (next_vertex_id >= nvertices)
381  {
382  std::cerr << me << ": `" << filename
383  << "': invalid vertex id "
384  << next_vertex_id << std::endl;
385  std::exit(1);
386  }
387  }
388  // The ends of the current edge.
389  topo::n_face<0, D> vertex(c, vertex_id);
390  topo::n_face<0, D> next_vertex(c, next_vertex_id);
391  // The current edge.
392  topo::algebraic_n_face<1, D> edge;
393  // If the edge has been constructed yet, create it;
394  // otherwise, retrieve its id from the complex.
395  if (!complex_edges[vertex_id][next_vertex_id])
396  {
397  complex_edges[vertex_id][next_vertex_id] = true;
398  complex_edges[next_vertex_id][vertex_id] = true;
399  edge =
400  make_algebraic_n_face(c.add_face(vertex -
401  next_vertex),
402  true);
403  }
404  else
405  {
406  edge = topo::edge(vertex, next_vertex);
407  mln_assertion(edge.is_valid());
408  }
409  // Record this edge.
410  face_edges_set += edge;
411  // Next vertex.
412  vertex_id = next_vertex_id;
413  }
414 
415  // Possibly read a value (depends on the actual format).
416  exact(this)->read_face_data(istr);
417 
418  // Add face.
419  c.add_face(face_edges_set);
420  }
421 
422  /*--------.
423  | Image. |
424  `--------*/
425 
426  // Site set.
427  domain s(c, geom);
428 
429  // Values
430  values vs;
431  exact(this)->assign(vs, s);
432 
433  // Image.
434  ima.init_(s, vs);
435 
436  /*--------------.
437  | End of file. |
438  `--------------*/
439 
440  istr >> &self::eat_comment;
441  if (!istr.eof())
442  {
443  std::cerr << me << ": `" << filename
444  << "': end of file not reached" << std::endl;
445  std::exit(1);
446  }
447  istr.close();
448  }
449 
450 
451  // ---------------- //
452  // Specific parts. //
453  // ---------------- //
454 
455  /* FIXME: We do not honor the part
456 
457  ``Line breaks are significant here: the color description
458  begins after VertN and ends with the end of the line (or
459  the next # comment).
460 
461  in the following comment. */
462 
502  inline
503  void
504  bin_off_loader::read_face_data(std::istream& /* istr */)
505  {
506  // Do nothing (no data associated to faces).
507  }
508 
509  inline
510  void
511  float_off_loader::read_face_data(std::istream& istr)
512  {
513  /* We just use R and ignore G, B and A (transparency) when
514  considering the value (``color'') associated to a face as
515  a (scalar) floating-point value (though it really is an
516  RGB triplet).
517 
518  To ensure consistency, we /might/ (later) check that R, G
519  and B are equal---or better, ``almost equal'', as they
520  are floats.
521 
522  Moreover, R must (and G, B and A should) be
523  floating-point values between 0 and 1, according to the
524  OFF file format definition. */
525  // FIXME: `A' should be optional.
526  float r, g, b, a;
527  istr >> r >> g >> b >> a;
528  mln_assertion(0.0f <= r); mln_assertion(r <= 1.0f);
529  mln_assertion(0.0f <= g); mln_assertion(g <= 1.0f);
530  mln_assertion(0.0f <= b); mln_assertion(b <= 1.0f);
531  mln_assertion(0.0f <= a); mln_assertion(a <= 1.0f);
532  face_value.push_back(r);
533  }
534 
535  inline
536  void
537  rgb8_off_loader::read_face_data(std::istream& istr)
538  {
539  /* We just use R, G, and B and ignore A (transparency) when
540  considering the value (``color'') associated to a face.
541 
542  R must (and G, B and A should) be floating-point values
543  between 0 and 1, according to the OFF file format
544  definition. */
545  // FIXME: `A' should be optional.
546  float r, g, b, a;
547  istr >> r >> g >> b >> a;
548  mln_assertion(0.0f <= r); mln_assertion(r <= 1.0f);
549  mln_assertion(0.0f <= g); mln_assertion(g <= 1.0f);
550  mln_assertion(0.0f <= b); mln_assertion(b <= 1.0f);
551  mln_assertion(0.0f <= a); mln_assertion(a <= 1.0f);
552  face_value.push_back(value::rgb8(int(255 * r),
553  int(255 * g),
554  int(255 * b)));
555  }
556  /* \} */
557 
558 
559  inline
560  void
561  bin_off_loader::reserve(unsigned /* nvertices */,
562  unsigned /* nedges */,
563  unsigned /* nfaces */)
564  {
565  // Do nothing (no data associated to faces).
566  }
567 
568  inline
569  void
570  float_off_loader::reserve(unsigned /* nvertices */,
571  unsigned /* nedges */,
572  unsigned nfaces)
573  {
574  face_value.reserve(nfaces);
575  }
576 
577 
578  inline
579  void
580  rgb8_off_loader::reserve(unsigned /* nvertices */,
581  unsigned /* nedges */,
582  unsigned nfaces)
583  {
584  face_value.reserve(nfaces);
585  }
586 
587 
588  inline
589  void
590  bin_off_loader::assign(values& vs, const domain& s)
591  {
592  // Default values.
593  for (unsigned i = 0; i <= D; ++i)
594  vs[i].insert(vs[i].begin(), s.cplx().nfaces_of_dim(i), true);
595  }
596 
597  inline
598  void
599  float_off_loader::assign(values& vs, const domain& s)
600  {
601  // Default values for n-face with n in [0, D[.
602  for (unsigned i = 0; i < D; ++i)
603  vs[i].insert(vs[i].begin(), s.cplx().nfaces_of_dim(i), 0.0f);
604  // Values for D-faces.
605  vs[D] = face_value;
606  }
607 
608  inline
609  void
610  rgb8_off_loader::assign(values& vs, const domain& s)
611  {
612  // Default values for n-face with n in [0, D[.
613  for (unsigned i = 0; i < D; ++i)
614  vs[i].insert(vs[i].begin(), s.cplx().nfaces_of_dim(i),
616  // Values for D-faces.
617  vs[D] = face_value;
618  }
619 
620 
621  // --------- //
622  // Helpers. //
623  // --------- //
624 
625  template <typename I, typename E>
626  inline
627  std::istream&
628  off_loader<I, E>::eat_comment(std::istream& istr)
629  {
630  // Skip whitespace and newlines.
631  std::ws(istr);
632  while (istr.peek() == '#')
633  {
634  /* Eat the `#' and the rest of the line until `\n' or
635  `\r' is found or the end of the file is reached. */
636  char c;
637  do
638  istr.get(c);
639  while (c != '\n' && c != '\r' && !istr.eof());
640  // Skip whitespace and newlines.
641  std::ws(istr);
642  }
643  return istr;
644  }
645 
646  } // end of namespace mln::io::off::internal
647 
648 
649 # endif // ! MLN_INCLUDE_ONLY
650 
651 
652  } // end of namespace mln::io::off
653 
654  } // end of namespace mln::io
655 
656 } // end of namespace mln
657 
658 
659 #endif // ! MLN_IO_OFF_LOAD_HH