https://svn.lrde.epita.fr/svn/oln/trunk/swilena
Index: ChangeLog
from Roland Levillain <roland(a)lrde.epita.fr>
Add a C++ image type extensible in Python.
* dynamic_image2d.i: New.
* Makefile.am: Add dynamic_image2d.i.
* point2d.i: Add row() and col() accessors.
* box2d.i: Add nrows() and ncols() accessors.
* python/dynamic-image2d-misc.py: New test.
* python/Makefile.am (AM_SWIGFLAGS): Add `-Wall'
Handle module dynamic_image2d.
(TESTS): Add dynamic-image2d-misc.py.
Makefile.am | 4
box2d.i | 16 ++
dynamic_image2d.i | 317 +++++++++++++++++++++++++++++++++++++++++
point2d.i | 9 +
python/Makefile.am | 14 +
python/dynamic-image2d-misc.py | 81 ++++++++++
6 files changed, 438 insertions(+), 3 deletions(-)
Index: dynamic_image2d.i
--- dynamic_image2d.i (revision 0)
+++ dynamic_image2d.i (revision 0)
@@ -0,0 +1,317 @@
+// -*- C++ -*-
+// 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.
+
+/// \file dynamic_image2d
+/// \brief A dynamic version of image2d, extendable in the target language.
+
+
+%module(directors="1") dynamic_image2d
+
+%import "point2d.i"
+%import "box2d.i"
+
+%import "int_u8.i"
+// FIXME: The import directive does not include the `%{ ... %}' clauses.
+%{
+#include "mln/value/int_u8.hh"
+%}
+
+%{
+# include <mln/core/concept/image.hh>
+# include <mln/core/box2d.hh>
+# include <mln/core/init.hh>
+
+# include <mln/value/set.hh>
+%}
+
+// Shortcuts macros.
+%include "mln/core/macros.hh"
+
+// Concept.
+%include "mln/core/concept/image.hh"
+
+%feature("director");
+
+%inline %{
+namespace mln
+{
+
+ // FIXME: Separate interface and implementation (and/or move the C++
+ // code into its own file).
+
+ // Fwd decl.
+ template <typename T> struct dynamic_image2d;
+
+
+ namespace trait
+ {
+
+ template <typename T>
+ struct image_< dynamic_image2d<T> >
+ : default_image_< T, dynamic_image2d<T> >
+ {
+ typedef trait::image::category::primary category;
+
+ typedef trait::image::access::random access;
+ typedef trait::image::space::two_d space;
+ typedef trait::image::size::regular size;
+ typedef trait::image::support::aligned support;
+
+ typedef trait::image::border::none border;
+ typedef trait::image::data::raw data;
+ typedef trait::image::io::read_write io;
+ typedef trait::image::speed::fast speed;
+ };
+
+ } // end of namespace mln::trait
+
+
+ /* FIXME: Inherit from internal::image_base to factor methods (see
+ below)? */
+ // \brief A semi-abstract class that serves as an entry point
+ // (called � director � by SWIG) for Python code.
+ //
+ // This class is not really abstract in the C++ sense, but it lacks
+ // methods to be a real image class.
+ template <typename T>
+ struct dynamic_image2d : Image< dynamic_image2d<T> >
+ {
+ typedef box2d pset;
+ typedef point2d psite;
+ typedef point2d point;
+ typedef dpoint2d dpoint;
+ typedef mln_fwd_piter(box2d) fwd_piter;
+ typedef mln_bkd_piter(box2d) bkd_piter;
+ // End of warning.
+
+
+ /// Value associated type.
+ typedef T value;
+
+ /// Return type of read-only access.
+ typedef const T& rvalue;
+
+ /// Return type of read-write access.
+ typedef T& lvalue;
+
+
+ /// Skeleton.
+ typedef dynamic_image2d< tag::value_<T> > skeleton;
+
+
+ /// Value_Set associated type.
+ typedef mln::value::set<T> vset;
+
+
+ /// Constructor without argument.
+ dynamic_image2d();
+
+ /// Constructor with the numbers of rows and columns and the
+ /// border thickness.
+ dynamic_image2d(int nrows, int ncols);
+
+ /// Constructor with a box and the border thickness (default is
+ /// 3).
+ dynamic_image2d(const box2d& b);
+
+
+ /// Initialize an empty image.
+ virtual void init_(const box2d& b);
+
+
+ /// Test if \p p is valid.
+ virtual bool owns_(const point2d& p) const;
+
+ /// Give the set of values of the image.
+ virtual const vset& values() const;
+
+ /// Give the definition domain.
+ virtual const box2d& domain() const;
+
+ /// Read-only access to the image value located at point \p p.
+ virtual const T& operator()(const point2d& p) const;
+
+ /// Read-write access to the image value located at point \p p.
+ virtual T& operator()(const point2d& p);
+
+
+ // From internal::image_base.
+
+ /// Mesh associated type.
+ typedef mln_mesh(pset) mesh;
+ /// Coordinate associated type.
+ typedef mln_coord(point) coord;
+
+ /// Test if \p p belongs to the image domain.
+ virtual bool has(const psite& p) const;
+ /// Give a bounding box of the image domain.
+ virtual const box2d& bbox() const;
+ /// Give the number of points of the image domain.
+ virtual std::size_t npoints() const;
+ /// Test if this image has been initialized; default impl.
+ virtual bool has_data() const;
+
+ // We can set domain_ to protected, otherwise Python subclasses
+ // won't see it.
+ pset domain_;
+ };
+
+
+ template <typename T, typename J>
+ inline
+ void init_(tag::image_t, dynamic_image2d<T>& target, const J& model)
+ {
+ box2d b;
+ init_(tag::bbox, b, model);
+ target.init_(b);
+ }
+
+
+ template <typename T>
+ inline
+ dynamic_image2d<T>::dynamic_image2d()
+ {
+ }
+
+ template <typename T>
+ inline
+ dynamic_image2d<T>::dynamic_image2d(int nrows, int ncols)
+ {
+ init_(make::box2d(nrows, ncols));
+ }
+
+ template <typename T>
+ inline
+ dynamic_image2d<T>::dynamic_image2d(const box2d& b)
+ {
+ init_(b);
+ }
+
+ template <typename T>
+ inline
+ void
+ dynamic_image2d<T>::init_(const box2d& b)
+ {
+ domain_ = b;
+ }
+
+ template <typename T>
+ inline
+ bool
+ dynamic_image2d<T>::owns_(const point2d& p) const
+ {
+ mln_precondition(exact(this)->has_data());
+ return exact(this)->has(p);
+ }
+
+ template <typename T>
+ inline
+ const typename dynamic_image2d<T>::vset&
+ dynamic_image2d<T>::values() const
+ {
+ return vset::the();
+ }
+
+ template <typename T>
+ inline
+ const box2d&
+ dynamic_image2d<T>::domain() const
+ {
+ mln_precondition(this->has_data());
+ return domain_;
+ }
+
+ // Dummy implementation, provided to have swig wrap the class (swig won't
+ // wrap an abstract class).
+ template <typename T>
+ inline
+ const T&
+ dynamic_image2d<T>::operator()(const point2d& p) const
+ {
+ // Nothing.
+ assert(false);
+ }
+
+ // Dummy implementation, provided to have swig wrap the class (swig won't
+ // wrap an abstract class).
+ template <typename T>
+ inline
+ T&
+ dynamic_image2d<T>::operator()(const point2d& p)
+ {
+ // Nothing.
+ assert(false);
+ }
+
+ template <typename T>
+ inline
+ bool
+ dynamic_image2d<T>::has(const psite& p) const
+ {
+ mln_precondition(exact(this)->has_data());
+ return exact(this)->domain().has(p);
+ }
+
+ template <typename T>
+ inline
+ const box2d&
+ dynamic_image2d<T>::bbox() const
+ {
+ mln_precondition(exact(this)->has_data());
+ return exact(this)->domain().bbox();
+ }
+
+ template <typename T>
+ inline
+ std::size_t
+ dynamic_image2d<T>::npoints() const
+ {
+ mln_precondition(exact(this)->has_data());
+ return exact(this)->domain().npoints();
+ }
+
+ template <typename T>
+ inline
+ bool
+ dynamic_image2d<T>::has_data() const
+ {
+ return true;
+ }
+
+} // end of namespace mln
+%}
+
+
+%template(dynamic_image2d_int_u8) mln::dynamic_image2d< mln::value::int_u<8> >;
+
+
+%include "fill.ixx"
+%template(fill) mln::level::fill< mln::dynamic_image2d< mln::value::int_u<8> > >;
+
+%include "println.ixx"
+%template(println) mln::debug::println< mln::dynamic_image2d< mln::value::int_u<8> > >;
Index: Makefile.am
--- Makefile.am (revision 2148)
+++ Makefile.am (working copy)
@@ -11,7 +11,9 @@
# Wrappers (generating actual modules).
wrappers = \
box2d.i dpoint2d.i image2d_int.i image2d_int_u8.i int_u8.i \
- int_u32.i neighb2d.i point2d.i window2d.i
+ int_u32.i neighb2d.i point2d.i window2d.i \
+ \
+ dynamic_image2d.i
EXTRA_DIST = $(meta_wrappers) $(wrappers)
Index: point2d.i
--- point2d.i (revision 2148)
+++ point2d.i (working copy)
@@ -61,4 +61,13 @@
%ignore mln::point_<mln::grid::square,int>::point_(int);
%ignore mln::point_<mln::grid::square,int>::point_(int, int, int);
+/* FIXME: Swig doesn't wrap operator[]... Provide row() and col()
+ accessors instead (we should wrap internal::mutable_coord_impl_
+ instead). */
+%extend mln::point_
+{
+ int row() const { return $self->operator[](0); }
+ int col() const { return $self->operator[](1); }
+}
+
%template(point2d) mln::point_<mln::grid::square, int>;
Index: box2d.i
--- box2d.i (revision 2148)
+++ box2d.i (working copy)
@@ -52,4 +52,20 @@
typename mln::point_<mln::grid::square, int>::coord,
typename mln::point_<mln::grid::square, int>::coord);
+%extend mln::box_
+{
+ unsigned nrows() const
+ {
+ // FIXME: This is the exact content of box_impl_<2, C, E>::nrows.
+ return mln::internal::force_exact<mln::box2d>(*$self).bbox().len(0);
+ }
+
+ unsigned ncols() const
+ {
+ // FIXME: This is the exact content of box_impl_<2, C, E>::ncols.
+ return mln::internal::force_exact<mln::box2d>(*$self).bbox().len(1);
+ }
+}
+
+
%template(box2d) mln::box_< mln::point_<mln::grid::square, int> >;
Index: python/dynamic-image2d-misc.py
--- python/dynamic-image2d-misc.py (revision 0)
+++ python/dynamic-image2d-misc.py (revision 0)
@@ -0,0 +1,81 @@
+#! /usr/bin/env python
+
+# 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.
+
+# \file python/dynamic-image2d-misc.py
+# \brief Test on C++ ``dynamic'' image2d extended in Python.
+
+
+from swilena import *
+import dynamic_image2d
+
+# Shortcuts.
+image = dynamic_image2d
+dyn_ima2d = image.dynamic_image2d_int_u8
+
+
+# A simple image extending dynamic_image2d_int_u8.
+class simple_image(dyn_ima2d):
+
+ # FIXME: Implement other ctors.
+ def __init__(self, b):
+ dyn_ima2d.__init__(self, b)
+ # FIXME: There must be a better way...
+ self.values_ = []
+ for n in range(0, b.nrows() * b.ncols()):
+ self.values_.append(int_u8())
+
+ def __call__(self, p):
+ # FIXME: This assumes the box starts at (0, 0), which is not
+ # always true!
+ off = p.row() * self.domain_.ncols() + p.col()
+ return self.values_[off]
+
+
+# FIXME: Why can't I use
+#
+# b = box2d(10, 10)
+#
+# directly?
+#
+# I think this is probably because SWIG's way to handle overloading in
+# Python depends just on the *number* of arguments, not their types --
+# and we already have a wrapped ctor with 2 arguments, the one taking
+# two mln::point2d's, so the second one (with two int's) is probably
+# hidden). Pay more attention to swig's warnings!
+b = box2d(point2d(0,0), point2d(9,9))
+ima = simple_image(b)
+
+image.fill(ima, int_u8(42))
+image.println(ima)
+
+# FIXME: Doesn't really work yet, since int_u8 is not convertible
+# to int.
+# for r in range(0, 10):
+# for c in range(0, 10):
+# print ima(point2d(r, c))
Index: python/Makefile.am
--- python/Makefile.am (revision 2148)
+++ python/Makefile.am (working copy)
@@ -10,7 +10,7 @@
AM_CPPFLAGS = -I$(PYTHONINC) -I$(top_srcdir)/milena
TOOLS_CXXFLAGS = @TOOLS_CXXFLAGS@
AM_CXXFLAGS = $(TOOLS_CXXFLAGS)
-AM_SWIGFLAGS = -c++ -python -I$(top_srcdir)/milena
+AM_SWIGFLAGS = -Wall -c++ -python -I$(top_srcdir)/milena
## We build modules, not plain libs.
AM_LDFLAGS = -avoid-version -module -shared
#### All the modules depend on libhw.
@@ -134,6 +134,16 @@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/_image2d_int_u8-wrap.Pcc@am__quote@
nodist_python_PYTHON += image2d_int_u8.py
+## dynamic_image2d.
+pyexec_LTLIBRARIES += _dynamic_image2d.la
+nodist__dynamic_image2d_la_SOURCES = dynamic_image2d-wrap.cc
+_dynamic_image2d_la_LIBADD = $(AM_LIBADD)
+CLEANFILES += $(nodist__dynamic_image2d_la_SOURCES) dynamic_image2d.py dynamic_image2d.py[co]
+## Include the dependency files. Copied from Automake's generated
+## case for C++.
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/_dynamic_image2d-wrap.Pcc@am__quote@
+nodist_python_PYTHON += dynamic_image2d.py
+
## ---------------------------- ##
## Swilena Python Shell (sps). ##
@@ -172,5 +182,5 @@
$(MAKE) $(AM_MAKEFLAGS) $(RUN)
@mv -f $@.tmp $@
-TESTS = image2d-misc.py morpho-fun.py morpho-segm.py
+TESTS = image2d-misc.py morpho-fun.py morpho-segm.py dynamic-image2d-misc.py
EXTRA_DIST += $(TESTS)
https://svn.lrde.epita.fr/svn/oln/trunk/milena
Index: ChangeLog
from Roland Levillain <roland(a)lrde.epita.fr>
Add a backward iterator on complex-based images.
* mln/core/p_complex_piter.hh (mln::p_complex_bkd_piter_<D, P>):
(operator<<(std::ostream&, const p_complex_bkd_piter_<D, P>&)):
New.
(mln::p_complex_fwd_piter_<D, P>::next_): Reimplement to make it
symmetric to mln::p_complex_bkd_piter_<D, P>::next_.
* mln/core/p_complex.hh (mln::p_complex<D, P>::bkd_piter): Set
typedef to p_complex_bkd_piter_<D, P>.
* mln/core/p_complex_piter.hh: Test the backward iterator on
complex_image.
mln/core/p_complex.hh | 5
mln/core/p_complex_piter.hh | 285 ++++++++++++++++++++++++++++++++++++++++++--
tests/core/complex_image.cc | 28 +++-
3 files changed, 302 insertions(+), 16 deletions(-)
Index: tests/core/complex_image.cc
--- tests/core/complex_image.cc (revision 2147)
+++ tests/core/complex_image.cc (working copy)
@@ -194,18 +194,34 @@
| Complex-based image iterators. |
`--------------------------------*/
- mln_piter_(ima_t) p(ima.domain());
- for_all(p)
- std::cout << "ima(" << p << ") = " << ima(p) << std::endl;
+ mln_fwd_piter_(ima_t) fp(ima.domain());
+ for_all(fp)
+ std::cout << "ima(" << fp << ") = " << ima(fp) << std::endl;
+ std::cout << std::endl;
- /* FIXME: Implement windows (and neighborhoods) for complex-images.
+ mln_bkd_piter_(ima_t) bp(ima.domain());
+ for_all(bp)
+ std::cout << "ima(" << bp << ") = " << ima(bp) << std::endl;
+
+
+ /* FIXME: Implement other psite iterators, for instance:
+
+ - iterators on N-faces with N fixed in [0, D] (using p_faces
+ and faces_psite?)
+ - iterators on N-faces with N in a subset of [0, D];
+ - etc. */
+
+ /* FIXME: Implement windows (and neighborhoods) and corresponding
+ iterators for complex-based 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-1)-faces adjacent to P (using p_faces and
+ faces_psite?);
+ - the set of (N+1)-faces adjacent to P (using p_faces and
+ faces_psite?);
- 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
Index: mln/core/p_complex_piter.hh
--- mln/core/p_complex_piter.hh (revision 2147)
+++ mln/core/p_complex_piter.hh (working copy)
@@ -37,8 +37,12 @@
/// \file mln/core/p_complex_piter.hh
/// \brief Definition of point iterator on complex-based pset.
+// Factor p_complex_fwd_piter_<D, P> and p_complex_bkd_piter_<D, P>
+
namespace mln
{
+ /* FIXME: Get rid of P? */
+
// Fwd decls.
template <unsigned D, typename P> class p_complex;
template <unsigned D, typename P> class complex_psite;
@@ -48,8 +52,6 @@
| 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_
@@ -143,14 +145,100 @@
| p_complex_bkd_piter_<D, P>. |
`-----------------------------*/
- // FIXME: Define p_complex_bkd_piter_<D, P>.
+ /// \brief Backward iterator on point sites of a mln::p_complex<D, P>.
+ template <unsigned D, typename P>
+ class p_complex_bkd_piter_
+ : public internal::point_iterator_base_< P, p_complex_bkd_piter_<D, P> >
+ {
+ typedef p_complex_bkd_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_bkd_piter_(const p_complex<D, P>& pc);
+ p_complex_bkd_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_bkd_piter_<D, P>& p);
# ifndef MLN_INCLUDE_ONLY
/*-----------------------------.
- | p_line_graph_fwd_piter_<P>. |
+ | p_complex_fwd_piter_<D, P>. |
`-----------------------------*/
template <unsigned D, typename P>
@@ -228,10 +316,11 @@
void
p_complex_fwd_piter_<D, P>::next_()
{
+ if (is_valid())
+ {
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
@@ -241,12 +330,16 @@
instead of the following. */
face_.set_face_id(face_id + 1);
else
+ // Start to iterate on the faces of the next dimension if
+ // possible.
+ if (n <= D)
{
- // 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);
}
+ else
+ invalidate();
}
if (is_valid())
update_();
@@ -331,7 +424,183 @@
| p_complex_bkd_piter_<D, P>. |
`-----------------------------*/
- // FIXME: Implement p_complex_bkd_piter_<D, P>.
+ template <unsigned D, typename P>
+ inline
+ p_complex_bkd_piter_<D, P>::p_complex_bkd_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_bkd_piter_<D, P>::p_complex_bkd_piter_(const p_complex_bkd_piter_<D, P>& rhs)
+ : face_(rhs.face_),
+ psite_(rhs.psite_),
+ // Dummy value.
+ p_()
+ {
+ }
+
+ template <unsigned D, typename P>
+ inline
+ p_complex_bkd_piter_<D, P>&
+ p_complex_bkd_piter_<D, P>::operator=(const p_complex_bkd_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_bkd_piter_<D, P>::operator[](unsigned i) const
+ {
+ // Dummy value.
+ return p_[i];
+ }
+
+ template <unsigned D, typename P>
+ inline
+ bool
+ p_complex_bkd_piter_<D, P>::is_valid() const
+ {
+ return face_.is_valid();
+ }
+
+ template <unsigned D, typename P>
+ inline
+ void
+ p_complex_bkd_piter_<D, P>::invalidate()
+ {
+ face_.set_n(invalid_unsigned_());
+ face_.set_face_id(invalid_unsigned_());
+ }
+
+ template <unsigned D, typename P>
+ inline
+ void
+ p_complex_bkd_piter_<D, P>::start()
+ {
+ face_.set_n(D);
+ face_.set_face_id(face_.cplx().template nfaces<D>() - 1);
+ update_();
+ }
+
+ template <unsigned D, typename P>
+ inline
+ void
+ p_complex_bkd_piter_<D, P>::next_()
+ {
+ if (is_valid())
+ {
+ unsigned n = face_.n();
+ unsigned face_id = face_.face_id();
+
+ if (face_id > 0)
+ /* 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 previous dimension
+ // if it exists.
+ if (n > 0)
+ {
+ // FIXME: Same remark as above.
+ face_.set_n(n - 1);
+ face_.set_face_id(face_.cplx().nfaces(n - 1) - 1);
+ }
+ else
+ invalidate();
+ }
+ if (is_valid())
+ update_();
+ }
+
+ template <unsigned D, typename P>
+ inline
+ void
+ p_complex_bkd_piter_<D, P>::update_()
+ {
+ // Update psite_.
+ psite_ = complex_psite<D, P>(face_);
+ }
+
+ template <unsigned D, typename P>
+ inline
+ const P&
+ p_complex_bkd_piter_<D, P>::to_point() const
+ {
+ // Dummy value.
+ return p_;
+ }
+
+ template <unsigned D, typename P>
+ inline
+ const complex_psite<D, P>&
+ p_complex_bkd_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_bkd_piter_<D, P>::operator complex_psite<D, P>() const
+ {
+ mln_precondition(is_valid());
+ return psite_;
+ }
+
+ template <unsigned D, typename P>
+ unsigned
+ p_complex_bkd_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_bkd_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);
+ }
# endif // ! MLN_INCLUDE_ONLY
Index: mln/core/p_complex.hh
--- mln/core/p_complex.hh (revision 2147)
+++ mln/core/p_complex.hh (working copy)
@@ -73,9 +73,10 @@
/// Point_Site associated type.
typedef complex_psite<D, P> psite;
- // FIXME: Fake.
+ /// Forward Point_Iterator associated type.
typedef p_complex_fwd_piter_<D, P> fwd_piter;
- typedef void bkd_piter;
+ /// Backward Point_Iterator associated type.
+ typedef p_complex_bkd_piter_<D, P> bkd_piter;
/// \brief Return The number of points (sites) of the set, i.e., the
/// number of \em faces.
https://svn.lrde.epita.fr/svn/oln/trunk/milena
Index: ChangeLog
from Roland Levillain <roland(a)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_);