2143: Add a complex-base image type.

https://svn.lrde.epita.fr/svn/oln/trunk/milena Index: ChangeLog from Roland Levillain <roland@lrde.epita.fr> Add a complex-base image type. * mln/core/complex_image.hh: New. * mln/trait/ch_value.hh (mln::trait::impl::ch_value_<M<D, tag::psite_<P>, tag::value_<T> >, V>): New specialization for mln::complex_image<D, P, T>. * mln/core/p_complex.hh (mln::p_complex<D, P>::cplx): New mutable accessor. * mln/core/complex_psite.hh (mln::complex_psite<D, P>::n) (mln::complex_psite<D, P>::face_id): New accessors. * mln/core/face.hh (any_face_handle<D>::any_face_handle(const face_handle<N, D>&)): New ctor. (any_face_handle<D>::any_face_handle(const any_face_handle<D>&)): Don't forget to initialize n_. (any_face_handle<D>::any_face_handle(complex<D>&, unsigned, unsigned)): Ensure N is compatible with D. * tests/core/complex_image.cc: Exercise mln::complex_image. mln/core/complex_image.hh | 301 ++++++++++++++++++++++++++++++++++++++++++++ mln/core/complex_psite.hh | 24 +++ mln/core/face.hh | 17 ++ mln/core/p_complex.hh | 42 ++++-- mln/trait/ch_value.hh | 8 + tests/core/complex_image.cc | 91 +++++++++++-- 6 files changed, 457 insertions(+), 26 deletions(-) Index: tests/core/complex_image.cc --- tests/core/complex_image.cc (revision 2142) +++ tests/core/complex_image.cc (working copy) @@ -30,12 +30,11 @@ #include <iostream> +#include <mln/value/int_u8.hh> #include <mln/core/point2d.hh> -// #include <mln/core/complex_image.hh> #include <mln/core/p_faces.hh> -#include <mln/core/p_complex.hh> - +#include <mln/core/complex_image.hh> int main() @@ -91,9 +90,61 @@ `---------------------*/ // A pset. - p_complex<D, point2d> pc0(c); - // An any-face handle (on E0) - any_face_handle<D> af(c, 1, 0); + p_complex<D, point2d> pc(c); + /* An any-face handle (on E0). + + Note that AF is built on `e0_', not `e0' (e0_ is built on + `pc.cplx()', not `c'), since the p_complex `pc' makes a copy of + the complex `c', and crossed-ownership tests doesn't work. I.e., + + pc.has(e0); + + is false. + + + FIXME: This might be a problem, since `pc.cplx()' and `c' + represent the same complex, in two different memory location (but + the former is controlled by PC, while the latter can be modified, + or even destroyed). This is a common problem for ``big'' values + that we don't want to manipulate by value (copy), or when we + don't want to use expensive, deep comparisons of pset to ensure + consistency. Here (and in graph-based images), we choose to + create a copy of the pset once, and manipulate it with a + tracked_ptr, to ensure both + + 1. perfect control of the lifetime of the pset (here, you can + delete `c', and `pc' will still be valid); + + 2. no pset duplication when creating new images based on it. + + + I (Roland) don't see elegant solutions here. A possiblity would + be to disconnect a face_handle from its complex (currently, a + face_handle is a bit like a Trivial Iterator from the C++ + Standard Library), but this means relaxed dynamic checks, and + more obscure errors. + + At least, we could have better error messages, i.e., something + like + + mln/core/complex_image.hh 267: + mln::complex_image<D, P, V>::operator(): Uncompatible p_complex. + + instead of + + mln/core/complex_image.hh:267: + typename mln::complex_image<D, P, V>::lvalue + mln::complex_image<D, P, V>::operator()(const mln::complex_psite<D, P>&) + [with unsigned int D = 2u, + P = mln::point_<mln::grid::square, int>, + V = mln::value::int_u<8u>]: + Assertion `this->data_->pc_.has(p)' failed. + + (which looks even uglier in the original, non-indented version). + + Ask Akim for his improved versions of abort() and assert(). */ + face_handle<1, D> e0_(pc.cplx(), 0); + any_face_handle<D> af(e0_); // An associated psite. complex_psite<D, point2d> cs(af); @@ -115,20 +166,36 @@ faces_psite<2, D, point2d> fs2(t0); - // FIXME: Enable when complex_image is available. -#if 0 /*----------------------. | Complex-based image. | `----------------------*/ + using mln::value::int_u8; + // An image type built on a 2-complex with mln::int_u8 values on // each face. - typedef ima_t complex_image< p_complex<D, point2d>, int_u8>; + typedef complex_image<D, point2d, int_u8> ima_t; - // FIXME: Create and init IMA. - // ... - ima_t ima(pc2); + // 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 n = 0; n < pc.cplx().nfaces(d); ++n) + values[d].push_back(d); + // Create and init an image based on PC. + ima_t ima(pc, values); + + // Check the value associated to edge E0_ (through complex psite CS). + mln_assertion(ima(cs) == 1u); + + + /*--------------------------------. + | Complex-based image iterators. | + `--------------------------------*/ + + // FIXME: Enable when iterators are available. +#if 0 mln_piter_(ima_t) p(ima.domain()); #endif } Index: mln/trait/ch_value.hh --- mln/trait/ch_value.hh (revision 2142) +++ mln/trait/ch_value.hh (working copy) @@ -110,6 +110,14 @@ typedef mln_ch_value(I, V) ret; }; + // For mln::complex_image<D, P, T>. + template < template <unsigned, class, class> class M, + unsigned D, typename P, typename T, typename V > + struct ch_value_< M< D, tag::psite_<P>, tag::value_<T> >, V > + { + typedef M< D, P, V > ret; + }; + // For mln::neighb::image<I, N>. template < template <class, class> class M, typename I, typename N, typename V > Index: mln/core/complex_image.hh --- mln/core/complex_image.hh (revision 0) +++ mln/core/complex_image.hh (revision 0) @@ -0,0 +1,301 @@ +// 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. This exception does not however invalidate any other +// reasons why the executable file might be covered by the GNU General +// Public License. + +#ifndef MLN_CORE_COMPLEX_IMAGE_HH +# define MLN_CORE_COMPLEX_IMAGE_HH + +/// \file mln/core/complex_image.hh +/// \brief Definition of a line complex-based image. + +# include <vector> + +# include <mln/trait/images.hh> + +# include <mln/core/internal/image_primary.hh> +# include <mln/metal/vec.hh> +# include <mln/core/p_complex.hh> +# include <mln/core/complex_psite.hh> +# include <mln/value/set.hh> + + +/* FIXME: In the current implementation, the type of values on faces + of different dimensions is necessarily the same (V). We should + allow different data types for vertices and edges. */ + + +namespace mln +{ + + // Fwd decl. + template <unsigned D, typename P, typename V> struct complex_image; + + namespace internal + { + + /// \internal Data structure for \c mln::complex_image<P,V>. + template <unsigned D, typename P, typename V> + struct data_< complex_image<D, P, V> > + { + data_(const p_complex<D, P>& pc, + const metal::vec< D + 1, std::vector<V> >& values); + + metal::vec< D + 1, std::vector<V> > values_; + const p_complex<D, P> pc_; + }; + + } // end of namespace mln::internal + + + namespace trait + { + + template <unsigned D, typename P, typename V> + struct image_< complex_image<D, P, V> > + : default_image_< V, complex_image<D, P, V> > + { + typedef trait::image::category::primary category; + + // FIXME: Is that right? + typedef trait::image::access::random access; + typedef typename trait::image::space_from_point<P>::ret space; + typedef trait::image::size::regular size; + typedef trait::image::support::irregular support; + + typedef trait::image::border::none border; + typedef trait::image::data::stored data; + typedef trait::image::io::read_write io; + // FIXME: Is that right? + typedef trait::image::speed::fast speed; + }; + + } // end of namespace mln::trait + + + /// \brief Image based on a complex. + /// + /// Values are stored on the vertices of the graph. + template <unsigned D, typename P, typename V> + struct complex_image : + public internal::image_primary_< p_complex<D, P>, complex_image<D, P, V> > + { + /// Super type. + typedef mln::internal::image_base_< p_complex<D, P>, + complex_image<D, P, V> > super_; + + /// Value associated type. + typedef V value; + + /// \brief Return type of read-write access. + /// + /// We use the associated type \c reference instead of a plain + /// reference on th value type (\v V), because it's the only way + /// to safely form a reference on the element in the case of a + /// std::vector<bool>. + typedef typename std::vector<V>::reference lvalue; + + /// Return type of read-only access. + typedef typename std::vector<V>::const_reference rvalue; + + /// Value set associated type. + typedef mln::value::set<value> vset; + + /// Skeleton. + typedef complex_image< D, tag::psite_<P>, tag::value_<V> > skeleton; + + /// Constructors. + /// \{ + complex_image(); + complex_image(const p_complex<D, P>& pc); + complex_image(const p_complex<D, P>& pc, + const metal::vec< D + 1, std::vector<V> >& values); + /// \} + + /// Initialize an empty image. + void init_(const p_complex<D, P>& pc, + const metal::vec< D + 1, std::vector<V> >& values); + + /// Read-only access of pixel value at point site \p p. + rvalue operator()(const complex_psite<D, P>& p) const; + + /// Read-write access of pixel value at point site \p p. + lvalue operator()(const complex_psite<D, P>& p); + + /// Accessors. + /// \{ + /// Return the domain of psites od the image. + const p_complex<D, P>& domain() const; + /// Return the domain of values of the image. + const vset& values() const; + + /// Return the array of values associated to the faces. + const metal::vec<D + 1, std::vector<V> >& face_values() const; + /// \} + }; + + // Fwd decl. + template <unsigned D, typename P, typename V, typename W> + void init_(tag::image_t, + complex_image<D, P, V>& target, + const complex_image<D, P, W>& model); + + +# ifndef MLN_INCLUDE_ONLY + + /*-----------------. + | Initialization. | + `-----------------*/ + + template <unsigned D, typename P, typename V, typename W> + inline + void init_(tag::image_t, + complex_image<D, P, V>& target, + const complex_image<D, P, W>& model) + { + metal::vec<D + 1, std::vector<V> > values; + for (unsigned i = 0; i < D; ++i) + values[i].resize(model.values[i].size()); + target.init_(model.domain(), values); + } + + /*-------. + | Data. | + `-------*/ + + namespace internal + { + template <unsigned D, typename P, typename V> + inline + data_< complex_image<D, P, V> >::data_(const p_complex<D, P>& pc, + const metal::vec< D + 1, + std::vector<V> >& values) + : values_(values), + pc_(pc) + { + // Ensure the complex is consistent with the values. + /* FIXME: We need additional macros in mln/core/contract.hh for + big blocks of preconditions like this one. */ +# ifndef NDEBUG + for (unsigned i = 0; i < D; ++i) + mln_precondition(pc.cplx().nfaces(i) == values[i].size()); +# endif // !NDEBUG + } + + } // end of namespace mln::internal + + /*---------------. + | Construction. | + `---------------*/ + + template <unsigned D, typename P, typename V> + inline + complex_image<D, P, V>::complex_image() + { + } + + template <unsigned D, typename P, typename V> + inline + complex_image<D, P, V>::complex_image(const p_complex<D, P>& pc) + { + metal::vec<D + 1, std::vector<V> > values; + for (unsigned i = 0; i < D; ++i) + values[i].resize(pc.cplx().nfaces(i)); + init_(pc, values); + } + + template <unsigned D, typename P, typename V> + inline + complex_image<D, P, V>::complex_image(const p_complex<D, P>& pc, + const metal::vec< D + 1, + std::vector<V> >& values) + { + init_(pc, values); + } + + template <unsigned D, typename P, typename V> + inline + void + complex_image<D, P, V>::init_(const p_complex<D, P>& pc, + const metal::vec< D + 1, std::vector<V> >& values) + { + mln_precondition(! this->has_data()); + this->data_ = + new internal::data_< complex_image<D, P, V> >(pc, values); + } + + /*---------------. + | Manipulation. | + `---------------*/ + + template <unsigned D, typename P, typename V> + inline + typename complex_image<D, P, V>::rvalue + complex_image<D, P, V>::operator()(const complex_psite<D, P>& p) const + { + mln_precondition(this->data_->pc_.has(p)); + return this->data_->values_[p.n()][p.face_id()]; + } + + template <unsigned D, typename P, typename V> + inline + typename complex_image<D, P, V>::lvalue + complex_image<D, P, V>::operator()(const complex_psite<D, P>& p) + { + mln_precondition(this->data_->pc_.has(p)); + return this->data_->values_[p.n()][p.face_id()]; + } + + template <unsigned D, typename P, typename V> + inline + const mln::value::set<V> & + complex_image<D, P, V>::values() const + { + return vset::the(); + } + + template <unsigned D, typename P, typename V> + inline + const metal::vec< D + 1, std::vector<V> >& + complex_image<D, P, V>::face_values() const + { + return this->data_->val_; + } + + template <unsigned D, typename P, typename V> + inline + const p_complex<D, P>& + complex_image<D, P, V>::domain() const + { + mln_precondition(this->has_data()); + return this->data_->pc_; + } + +# endif // ! MLN_INCLUDE_ONLY + +} // end of namespace mln + + +#endif // ! MLN_CORE_COMPLEX_IMAGE_HH Index: mln/core/complex_psite.hh --- mln/core/complex_psite.hh (revision 2142) +++ mln/core/complex_psite.hh (working copy) @@ -76,11 +76,19 @@ coord operator[](unsigned face) const; /// \} + /// Accessors + /// \{ /// Return the face handle of this point site. any_face_handle<D> face() const; /// Return the complex on which this site is built. const complex<D>& cplx() const; + /// Return the dimension of the face of this psite. + unsigned n() const; + /// Return the id of the face of this psite. + unsigned face_id() const; + /// \} + /// Is this psite valid? bool is_valid() const; @@ -208,6 +216,22 @@ return face_.cplx(); } + template <unsigned D, typename P> + inline + unsigned + complex_psite<D, P>::n() const + { + return face_.n(); + } + + template <unsigned D, typename P> + inline + unsigned + complex_psite<D, P>::face_id() const + { + return face_.face_id(); + } + /*--------------. | Comparisons. | `--------------*/ Index: mln/core/face.hh --- mln/core/face.hh (revision 2142) +++ mln/core/face.hh (working copy) @@ -257,6 +257,10 @@ /// Build a face handle from \a complex and \a face_id. any_face_handle(complex<D>& complex, unsigned n, unsigned face_id); + /// Build a face handle from a face_handle. + template <unsigned N> + any_face_handle(const face_handle<N, D>& f); + /// Copy and assignment. /// \{ any_face_handle(const any_face_handle<D>& rhs); @@ -499,11 +503,22 @@ : cplx_(&c), n_(n), face_id_(face_id) { // Ensure N is compatible with D. + mln_precondition(n <= D); + } + + template <unsigned D> + template <unsigned N> + any_face_handle<D>::any_face_handle(const face_handle<N, D>& f) + : cplx_(&f.cplx()), n_(N), face_id_(f.face_id()) + { + // Ensure N is compatible with D. + metal::bool_< N <= D >::check(); + } template <unsigned D> any_face_handle<D>::any_face_handle(const any_face_handle<D>& rhs) - : cplx_(rhs.cplx_), face_id_(rhs.face_id_) + : cplx_(rhs.cplx_), n_(rhs.n_), face_id_(rhs.face_id_) { } Index: mln/core/p_complex.hh --- mln/core/p_complex.hh (revision 2142) +++ mln/core/p_complex.hh (working copy) @@ -85,13 +85,22 @@ /// Return The number of faces in the complex. std::size_t nfaces() const; - /// Give the exact bounding box. - const box_<P>& bbox() const; + // FIXME: Add nfaces(unsigned) routines? bool has(const psite& p) const; - /// Return the complex associated to the p_complex domain. + /// Accessors. + /// \{ + /// Return the complex associated to the p_complex domain (const + /// version) const complex<D>& cplx() const; + /// Return the complex associated to the p_complex domain (mutable + /// version). + complex<D>& cplx(); + + /// Give the exact bounding box. + const box_<P>& bbox() const; + /// \} private: /// The complex on which this pset is built. @@ -156,15 +165,6 @@ template <unsigned D, typename P> inline - const box_<P>& - p_complex<D, P>::bbox() const - { - // FIXME: Dummy value. - return bb_; - } - - template <unsigned D, typename P> - inline bool p_complex<D, P>::has(const psite& p) const { @@ -175,7 +175,6 @@ p.face().is_valid(); } - template <unsigned D, typename P> const complex<D>& p_complex<D, P>::cplx() const @@ -184,6 +183,23 @@ return *cplx_.ptr_; } + template <unsigned D, typename P> + complex<D>& + p_complex<D, P>::cplx() + { + mln_precondition(cplx_); + return *cplx_.ptr_; + } + + template <unsigned D, typename P> + inline + const box_<P>& + p_complex<D, P>::bbox() const + { + // FIXME: Dummy value. + return bb_; + } + template <unsigned D, typename P> bool
participants (1)
-
Roland Levillain