import numpy as N

from odict import odict
from attributes import readonly

from neuroimaging import reorder, reverse, hasattrs
from neuroimaging.reference.axis import VoxelAxis

class CoordinateSystem(odict):
    "A simple class to carry around coordinate information in one bundle."

    class name (readonly):
        "coordinate system name"
        implements=str
    class axes (readonly):
        "list of axes"
        implements=tuple
        get=lambda _,s: s.values()
    class axisnames (readonly):
        "list of axis names"
        implements=tuple
        get=lambda _,s: s.keys()
    class ndim (readonly):
        "number of dimensions"
        get=lambda _,s: len(s.axes)
 

    def __init__(self, name, axes):
        self.name = name
        odict.__init__(self, [(ax.name, ax) for ax in axes])


    def __getitem__(self, axisname):
        try:
            return odict.__getitem__(self, axisname)
        except KeyError:
            raise KeyError(
              "axis '%s' not found, names are %s"%(axisname,self.keys()))

    def __setitem__(self, name, value):
        raise TypeError, "CoordinateSystem does not support axis assignment"


    def __eq__(self, other):
        if not hasattrs(other, "name", "axes"): return False
        return (self.name, self.axes)==(other.name, other.axes)


    def __str__(self):
        _dict = {'name': self.name, 'axes':self.axes}
        return `_dict`
   

    def reorder(self, name, order):
        """
        Given a name for the reordered coordinates, and a new order, return a
        reordered coordinate system.
        """
        if name is None: name = self.name
        return CoordinateSystem(name, reorder(self.axes, order))


    def reverse(self, name=None):
        if name is None: name = self.name
        return CoordinateSystem(name, reverse(self.axes))


    def hasaxis(self, name):
        return self.has_key(name)


    def getaxis(self, name):
        return self.get(name)



class VoxelCoordinateSystem(CoordinateSystem):
    """
    Coordinates with a shape -- assumed to be
    voxel coordinates, i.e. if shape = [3,4,5] then valid range
    interpreted as [0,2] X [0,3] X [0,4].
    """
    class shape (readonly): implements=list


    def __init__(self, name, axes, shape=None):
        if shape is None:
            self.shape = [dim.length for dim in axes]
        else:
            self.shape = list(shape)
        axes = [VoxelAxis(ax.name, length) for (ax, length) in zip(axes, self.shape)]
        CoordinateSystem.__init__(self, name, axes)


    def isvalid(self, x):
        """
        Verify whether x is a valid (voxel) coordinate.
        """
        if not hasattr(self, '_box'):
            self._getbox()
        test = 1
        for i in range(self.ndim):
            test *= (N.greater_equal(x[i], self._box[i][0]) *
                     N.less_equal(x[i], self._box[i][1]))
        return test


    def _getbox(self):
        self._box = []
        for i in range(self.ndim):
            v = self.axes[i].values()
            self._box.append((min(v), max(v)))



class DiagonalCoordinateSystem(CoordinateSystem):

    def __init__(self, name, axes):
        self.shape = [dim.length for dim in axes]
        CoordinateSystem.__init__(self, name, axes)
        

    def transform(self):
        """
        Return an orthogonal homogeneous transformation matrix based on the
        step, start, length attributes of each axis.
        """
        value = N.zeros((self.ndim+1,)*2, N.float64)
        value[self.ndim, self.ndim] = 1.0
        for i in range(self.ndim):
            value[i,i] = self.axes[i].step
            value[i, self.ndim] = self.axes[i].start
        return value


@@ -1965,24 +1965,204 @@
 get(name)%0A%0A%0A
+    def isvalid(self, x):%0A        %22%22%22%0A        Verify whether x is a valid coordinate.%0A        %22%22%22%0A        return not False in %5Bself.axes%5Bi%5D.valid(x%5Bi%5D) for i in range(self.ndim)%5D%0A%0A
 %0Aclass Voxel
@@ -2734,542 +2734,8 @@
 )%0A%0A%0A
-    def isvalid(self, x):%0A        %22%22%22%0A        Verify whether x is a valid (voxel) coordinate.%0A        %22%22%22%0A        if not hasattr(self, '_box'):%0A            self._getbox()%0A        test = 1%0A        for i in range(self.ndim):%0A            test *= (N.greater_equal(x%5Bi%5D, self._box%5Bi%5D%5B0%5D) *%0A                     N.less_equal(x%5Bi%5D, self._box%5Bi%5D%5B1%5D))%0A        return test%0A%0A%0A    def _getbox(self):%0A        self._box = %5B%5D%0A        for i in range(self.ndim):%0A            v = self.axes%5Bi%5D.values()%0A            self._box.append((min(v), max(v)))%0A%0A%0A%0A
 clas
@@ -2919,16 +2919,16 @@
 , axes)%0A
+
         
@@ -2924,25 +2924,24 @@
 s)%0A        %0A
-%0A
     def tran

simplify the isvalid method by using the new Axis interface