2147: Add a forward iterator on complex-based images.

https://svn.lrde.epita.fr/svn/oln/trunk/milena Index: ChangeLog from Roland Levillain <roland@lrde.epita.fr> Add a forward iterator on complex-based images. * mln/core/p_complex_piter.hh: New. * mln/core/p_complex.h (mln::p_complex<D, P>::fwd_piter): Set typedef to p_complex_fwd_piter_<D, P>. (mln::p_complex<D, P>::cplx_): Make it mutable. (mln::p_complex<D, P>::cplx() const): Remove const qualifier on return type. * mln/core/face.hh (mln::any_face_handle<D>::is_valid): Fix this method: check the dimension too. (mln::any_face_handle<D>::set_cplx) (mln::any_face_handle<D>::set_n) (mln::any_face_handle<D>::set_face_id): New methods. * tests/core/complex_image.cc: Don't forget to initialize all cells of `values', including the ones of highest dimension. Test the forward iterator on complex_image. mln/core/face.hh | 36 ++++ mln/core/p_complex.hh | 26 ++- mln/core/p_complex_piter.hh | 341 ++++++++++++++++++++++++++++++++++++++++++++ tests/core/complex_image.cc | 37 ++++ 4 files changed, 430 insertions(+), 10 deletions(-) Index: tests/core/complex_image.cc --- tests/core/complex_image.cc (revision 2146) +++ tests/core/complex_image.cc (working copy) @@ -147,7 +147,6 @@ any_face_handle<D> af(e0_); // An associated psite. complex_psite<D, point2d> cs(af); - std::cout << cs << std::endl; /*--------------------. @@ -180,7 +179,7 @@ // Values. metal::vec<D + 1, std::vector< int_u8 > > values; // Assign 0 to 0-faces, 1 to 1-faces and 2 to 2-faces. - for (unsigned d = 0; d < D; ++d) + for (unsigned d = 0; d <= D; ++d) for (unsigned n = 0; n < pc.cplx().nfaces(d); ++n) values[d].push_back(d); @@ -195,8 +194,36 @@ | Complex-based image iterators. | `--------------------------------*/ - // FIXME: Enable when iterators are available. -#if 0 mln_piter_(ima_t) p(ima.domain()); -#endif + for_all(p) + std::cout << "ima(" << p << ") = " << ima(p) << std::endl; + + + /* FIXME: Implement windows (and neighborhoods) for complex-images. + + For a given (fixed) dimension N and a psite P on a N-face, + implement windows returning + + - the set of (N-1)-faces adjacent to P; + - the set of (N+1)-faces adjacent to P; + + - the set of N-faces sharing a (N-1)-face with P; + - the set of N-faces sharing a (N-1)-face or (N-2)-face (by + transitivity) with P (is it useful?); + + - the set of the ``cell'' including P (named ``P-hat'' in + couprie.08.pami), i.e., the set of all M-faces adjacent to P, + where M is in [0, N-1]. + + In that definition, P is said adjacent to an M-face Q if + if there is a sequence (M1, M2, ..., Mn) of faces so that + - M1 is an (M+1)-face adjacent to M ; + - M2 is an (M+2)-face adjacent to M1 ; + - and so on; + - Mn is an (N-1)-face adjacent to N. + + - what else? + + We might want to look at operators on (simplicial?) complexes + like star, link, etc. and possibly implement them. */ } Index: mln/core/p_complex_piter.hh --- mln/core/p_complex_piter.hh (revision 0) +++ mln/core/p_complex_piter.hh (revision 0) @@ -0,0 +1,341 @@ +// 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. +// reasons why the executable file might be covered by the GNU General +// Public License. + +#ifndef MLN_CORE_P_COMPLEX_PITER_HH +# define MLN_CORE_P_COMPLEX_PITER_HH + +# include <limits> + +# include <mln/core/internal/point_iterator_base.hh> +# include <mln/core/p_complex.hh> +# include <mln/core/complex_psite.hh> + +/// \file mln/core/p_complex_piter.hh +/// \brief Definition of point iterator on complex-based pset. + +namespace mln +{ + // Fwd decls. + template <unsigned D, typename P> class p_complex; + template <unsigned D, typename P> class complex_psite; + + + /*-----------------------------. + | p_complex_fwd_piter_<D, P>. | + `-----------------------------*/ + + /* FIXME: Get rid of P? */ + + /// \brief Forward iterator on point sites of a mln::p_complex<D, P>. + template <unsigned D, typename P> + class p_complex_fwd_piter_ + : public internal::point_iterator_base_< P, p_complex_fwd_piter_<D, P> > + { + typedef p_complex_fwd_piter_<D, P> self_; + typedef internal::point_iterator_base_< P, self_ > super_; + + public: + // Make definitions from super class available. + // FIXME: Is it still meaningful for a complex? + enum { dim = super_::dim }; + + typedef complex_psite<D, P> psite; + typedef P point; + typedef mln_coord(point) coord; + + /// Construction and assignment. + /// \{ + p_complex_fwd_piter_(const p_complex<D, P>& pc); + p_complex_fwd_piter_(const self_& rhs); + self_& operator= (const self_& rhs); + /// \} + + /// Manipulation. + /// \{ + /// Test if the iterator is valid. + bool is_valid() const; + /// Invalidate the iterator. + void invalidate(); + /// Start an iteration. + void start(); + + /// Go to the next point. + void next_(); + /// Update the internal data of the iterator. + void update_(); + /// \} + + /// Conversion and accessors. + /// \{ + /// Reference to the corresponding point. + // FIXME: Don't use this method (dummy value). + const point& to_point () const; + /// Reference to the corresponding point site. + const psite& to_psite () const; + /// Convert the iterator into a line graph psite. + operator psite() const; + + /// Read-only access to the \a i-th coordinate. + // FIXME: Don't use this operator (dummy value). + coord operator[](unsigned i) const; + /// \} + + private: + /// The face handle this iterator is pointing to. + any_face_handle<D> face_; + /// The psite corresponding to this iterator. + psite psite_; + /// \brief The point associated to this psite. + // FIXME: Actually, this is a dummy value! + point p_; + + /// \brief An invalid value for both the dimension and the id of + /// the face. + /// + /// Use a function instead of a static constant, since `static' + /// variables needs to be compiled once, which requires a compiled + /// library to avoid duplicate symbols, which is something that + /// was not really planned in Milena. A function tagged `inlined' + /// can appear multiple times in a program, and solves this + /// problem. We rely on the compiler to inline this call. + /// + /// Of course, we could have used UINT_MAX, but it is not very + /// C++. + unsigned invalid_unsigned_() const; + }; + + + /* FIXME: This hand-made delegation is painful. We should rely on + the general mechanism provided by Point_Site. But then again, we + need to refine/adjust the interface of Point_Site w.r.t. the + mandatory conversions to points. */ + template <unsigned D, typename P> + inline + std::ostream& + operator<<(std::ostream& ostr, const p_complex_fwd_piter_<D, P>& p); + + + /*-----------------------------. + | p_complex_bkd_piter_<D, P>. | + `-----------------------------*/ + + // FIXME: Define p_complex_bkd_piter_<D, P>. + + + +# ifndef MLN_INCLUDE_ONLY + + /*-----------------------------. + | p_line_graph_fwd_piter_<P>. | + `-----------------------------*/ + + template <unsigned D, typename P> + inline + p_complex_fwd_piter_<D, P>::p_complex_fwd_piter_(const p_complex<D, P>& pc) + // Initialize psite_ and p_ a dummy values. + : psite_(), + p_() + { + face_.set_cplx(pc.cplx()); + // Invalidate face_. + invalidate(); + } + + template <unsigned D, typename P> + inline + p_complex_fwd_piter_<D, P>::p_complex_fwd_piter_(const p_complex_fwd_piter_<D, P>& rhs) + : face_(rhs.face_), + psite_(rhs.psite_), + // Dummy value. + p_() + { + } + + template <unsigned D, typename P> + inline + p_complex_fwd_piter_<D, P>& + p_complex_fwd_piter_<D, P>::operator=(const p_complex_fwd_piter_<D, P>& rhs) + { + if (&rhs == this) + return *this; + face_ = rhs.face_; + psite_ = rhs.psite_; + return *this; + } + + template <unsigned D, typename P> + inline + mln_coord(P) + p_complex_fwd_piter_<D, P>::operator[](unsigned i) const + { + // Dummy value. + return p_[i]; + } + + template <unsigned D, typename P> + inline + bool + p_complex_fwd_piter_<D, P>::is_valid() const + { + return face_.is_valid(); + } + + template <unsigned D, typename P> + inline + void + p_complex_fwd_piter_<D, P>::invalidate() + { + face_.set_n(invalid_unsigned_()); + face_.set_face_id(invalid_unsigned_()); + } + + template <unsigned D, typename P> + inline + void + p_complex_fwd_piter_<D, P>::start() + { + face_.set_n(0u); + face_.set_face_id(0u); + update_(); + } + + template <unsigned D, typename P> + inline + void + p_complex_fwd_piter_<D, P>::next_() + { + unsigned n = face_.n(); + unsigned face_id = face_.face_id(); + if (n <= D) + { + if (face_id + 1 < face_.cplx().nfaces(n)) + /* FIXME: Provide accessor any_face_handle::n() returning + a mutable reference? This way, we could just write + + ++face_.face_id(); + + instead of the following. */ + face_.set_face_id(face_id + 1); + else + { + // Start to iterate on the faces of the next dimension. + // FIXME: Same remark as above. + face_.set_n(n + 1); + face_.set_face_id(0u); + } + } + if (is_valid()) + update_(); + } + + template <unsigned D, typename P> + inline + void + p_complex_fwd_piter_<D, P>::update_() + { + // Update psite_. + psite_ = complex_psite<D, P>(face_); + } + + template <unsigned D, typename P> + inline + const P& + p_complex_fwd_piter_<D, P>::to_point() const + { + // Dummy value. + return p_; + } + + template <unsigned D, typename P> + inline + const complex_psite<D, P>& + p_complex_fwd_piter_<D, P>::to_psite() const + { + /* We don't check whether the iterator is valid before returning + the value using + + mln_precondition(is_valid()); + + since this method may be called *before* the iterator is + actually initialized. This is the case for instance when this + point iterator (say, P) is used to initialize another iterator + on window or neighborhood (say, Q); most of the time, for_all() + is responsible for the initialization of P, but it takes place + *after* the creation of Q. */ + return psite_; + } + + template <unsigned D, typename P> + inline + p_complex_fwd_piter_<D, P>::operator complex_psite<D, P>() const + { + mln_precondition(is_valid()); + return psite_; + } + + template <unsigned D, typename P> + unsigned + p_complex_fwd_piter_<D, P>::invalid_unsigned_() const + { + return std::numeric_limits<unsigned>::max(); + } + + + template <unsigned D, typename P> + inline + std::ostream& + operator<<(std::ostream& ostr, const p_complex_fwd_piter_<D, P>& p) + { + /* FIXME: We should use p.to_psite() here, but as it lacks the + precondition the conversion operator has, so we use the latter. + + We should + - rename `to_psite' as `to_psite_'; + - write a new `to_psite' routine checking the validity of the + iterator; + - have the conversion operator to psite use this new `to_psite' + routine; + - adjust former clients of `to_psite' + + This is a general remark that applies to all point/psite + iterators of Milena. */ + return ostr << static_cast< complex_psite<D, P> >(p); + } + + + /*-----------------------------. + | p_complex_bkd_piter_<D, P>. | + `-----------------------------*/ + + // FIXME: Implement p_complex_bkd_piter_<D, P>. + +# endif // ! MLN_INCLUDE_ONLY + +} // end of mln + + +#endif // ! MLN_CORE_P_COMPLEX_PITER_HH Index: mln/core/face.hh --- mln/core/face.hh (revision 2146) +++ mln/core/face.hh (working copy) @@ -162,6 +162,7 @@ /// Return the complex the face belongs to. complex<D>& cplx() const; /// Return the id of the face. + // FIXME: Rename as `id'? unsigned face_id() const; /// Return the mln::face pointed by this handle. @@ -174,6 +175,7 @@ /// A const face_handle can be used to modify a complex. mutable complex<D>* cplx_; /// \brief The id of the face. + // FIXME: Rename as `id_'? unsigned face_id_; }; @@ -275,10 +277,19 @@ /// Return the complex the face belongs to. complex<D>& cplx() const; /// Return the dimension of the face. + // FIXME: Rename as `dim'? unsigned n() const; /// Return the id of the face. + // FIXME: Rename as `id'? unsigned face_id() const; + /// Set the complex the face belongs to. + void set_cplx(complex<D>& cplx); + /// Set the dimension of the face. + void set_n(unsigned n); + /// Set the id of the face. + void set_face_id(unsigned face_id); + /// Return the mln::face pointed by this handle. template <unsigned N> face<N, D>& to_face() const; @@ -290,8 +301,10 @@ /// A const any_face_handle can be used to modify a complex. mutable complex<D>* cplx_; /// The dimension of the face. + // FIXME: Rename as `dim_'? unsigned n_; /// \brief The id of the face. + // FIXME: Rename as `id_'? unsigned face_id_; }; @@ -538,7 +551,7 @@ bool any_face_handle<D>::is_valid() const { - return cplx_ != 0 && face_id_ < cplx_->nfaces(n_); + return cplx_ != 0 && n_ <= D && face_id_ < cplx_->nfaces(n_); } template <unsigned D> @@ -564,6 +577,27 @@ } template <unsigned D> + void + any_face_handle<D>::set_cplx(complex<D>& cplx) + { + cplx_ = &cplx; + } + + template <unsigned D> + void + any_face_handle<D>::set_n(unsigned n) + { + n_ = n; + } + + template <unsigned D> + void + any_face_handle<D>::set_face_id(unsigned face_id) + { + face_id_ = face_id; + } + + template <unsigned D> template <unsigned N> face<N, D>& any_face_handle<D>::to_face() const Index: mln/core/p_complex.hh --- mln/core/p_complex.hh (revision 2146) +++ mln/core/p_complex.hh (working copy) @@ -38,6 +38,7 @@ # include <mln/core/complex.hh> # include <mln/core/complex_psite.hh> +# include <mln/core/p_complex_piter.hh> namespace mln @@ -73,7 +74,7 @@ typedef complex_psite<D, P> psite; // FIXME: Fake. - typedef void fwd_piter; + typedef p_complex_fwd_piter_<D, P> fwd_piter; typedef void bkd_piter; /// \brief Return The number of points (sites) of the set, i.e., the @@ -93,7 +94,9 @@ /// \{ /// Return the complex associated to the p_complex domain (const /// version) - const complex<D>& cplx() const; + /* FIXME: Move back the const qualifier on this return type (see + comment below on cplx_). */ + complex<D>& cplx() const; /// Return the complex associated to the p_complex domain (mutable /// version). complex<D>& cplx(); @@ -104,7 +107,22 @@ private: /// The complex on which this pset is built. - util::tracked_ptr< complex<D> > cplx_; + /* FIXME:Get rid of this `mutable' qualifier. This is needed for + compatiblity reasons with any_face_handle (see p_complex_piter) + + We should either + + - do not use any_face_handle in the implementation of + p_complex_piter; + + - have an additional version of any_face_handles holding a + const (not mutable) complex; + + - or even have face_handle and any_face_handle do not hold a + reference on a complex, leading to a design of complexes + similar to graphs, where vertex and edge handles (named `id's) + are not tied to a specific graph. */ + mutable util::tracked_ptr< complex<D> > cplx_; // FIXME: Remove as soon as bbox become optional. box_<P> bb_; }; @@ -176,7 +194,7 @@ } template <unsigned D, typename P> - const complex<D>& + complex<D>& p_complex<D, P>::cplx() const { mln_precondition(cplx_);
participants (1)
-
Roland Levillain