#from __future__ import division

from matplotlib.numerix import array, asarray, alltrue, arange
from matplotlib.numerix.mlab import rand
from matplotlib.transforms import Point, Bbox, Value, Affine
from matplotlib.transforms import multiply_affines
from matplotlib.transforms import Func, IDENTITY, LOG10, POLAR, FuncXY
from matplotlib.transforms import SeparableTransformation
from matplotlib.transforms import identity_transform, unit_bbox
from matplotlib.transforms import get_bbox_transform
from matplotlib.transforms import transform_bbox, inverse_transform_bbox
from matplotlib.transforms import bbox_all
from matplotlib.transforms import copy_bbox_transform


def closeto(x,y):
    return abs(asarray(x)-asarray(y))<1e-10

def closeto_seq(xs,ys):
    return alltrue([closeto(x,y) for x,y in zip(xs, ys)])

def closeto_bbox(b1, b2):
    xmin1, xmax1 = b1.intervalx().get_bounds()
    ymin1, ymax1 = b1.intervaly().get_bounds()
    xmin2, xmax2 = b2.intervalx().get_bounds()
    ymin2, ymax2 = b2.intervaly().get_bounds()

    pairs = ( (xmin1, xmin2), (xmax1, xmax2), (ymin1, ymin2), (ymax1, ymax2))
    return alltrue([closeto(x,y) for x,y in pairs])
    
ll = Point( Value(10),  Value(10) )
ur = Point( Value(200), Value(40) )

bbox = Bbox(ll, ur)

assert(bbox.xmin()==10)
assert(bbox.width()==190)
assert(bbox.height()==30)

ll.x().set(12.0)
assert(bbox.xmin()==12)
assert(bbox.width()==188)
assert(bbox.height()==30)


a  = Value(10)
b  = Value(0)
c  = Value(0)
d  = Value(20)
tx = Value(-10)
ty = Value(-20)

affine = Affine(a,b,c,d,tx,ty)
# test transformation of xy tuple
x, y = affine.xy_tup( (10,20) )
assert(x==90)
assert(y==380)

# test transformation of sequence of xy tuples
xy = affine.seq_xy_tups( ( (10,20), (20,30), ) )
assert(xy[0] == (90, 380))
assert(xy[1] == (190, 580))

# test transformation of x and y sequences
xy = affine.seq_x_y(  (10,20), (20,30))
assert(xy[0] == (90, 190))
assert(xy[1] == (380, 580))

# test with numeric arrays
xy = affine.seq_x_y(  array((10,20)), array((20,30)))
assert(xy[0] == (90, 190))
assert(xy[1] == (380, 580))

# now change the x scale factor and make sure the affine updated
# properly
a.set(20)
xy = affine.seq_xy_tups( ( (10,20), (20,30), ) )
assert(xy[0] == (190, 380))
assert(xy[1] == (390, 580))

# Test the aritmetic operations on lazy values
v1 = Value(10)
v2 = Value(20)
o1 =  v1 + v2
assert( o1.get() == 30)

o2 =  v1 * v2
assert( o2.get() == 200)

v3 = Value(2)
o3 = (v1+v2)*v3
assert( o3.get() == 60)

# test a composition of affines
zero = Value(0)
one = Value(1)
two = Value(2)
num = Value(2)
a1 = Affine(num, zero, zero, num, zero, zero)
a2 = Affine(one, zero, zero, num, num, one )

pnt = 3,4
a = multiply_affines(a1, a2)
assert( a2.xy_tup(pnt) == (5,9) )
assert( a.xy_tup(pnt) == (10,18) )

a = multiply_affines(a2, a1)
assert( a1.xy_tup(pnt) == (6,8) )
assert( a.xy_tup(pnt) == (8,17) )


# change num to 4 and make sure the affine product is still right
num.set(4)
assert( a1.xy_tup(pnt) == (12,16) )
assert( a.xy_tup(pnt) == (16,65) )

# test affines with arithemtic sums of lazy values
val = num*(one + two)
a1 = Affine(one, zero, zero, val, num, val)
assert(a1.xy_tup(pnt) == (7, 60))

x = rand(20)
y = rand(20)
transform = identity_transform()
xout, yout = transform.seq_x_y(x,y)
assert((x,y) == transform.seq_x_y(x,y))


# test bbox transforms; transform the unit coordinate system to
# "display coords"
bboxin = unit_bbox()
ll = Point( Value(10),  Value(10) )
ur = Point( Value(200), Value(40) )
bboxout = Bbox(ll, ur)

transform = get_bbox_transform(bboxin, bboxout)

assert( transform.xy_tup( (0,0) )==(10, 10))
assert( transform.xy_tup( (1,1) )==(200, 40))
assert( transform.xy_tup( (0.5, 0.5) )==(105, 25))

# simulate a resize
ur.x().set(400)
ur.y().set(400)
assert( transform.xy_tup( (0,0) )==(10, 10))
assert( transform.xy_tup( (1,1) )==(400, 400))
assert( transform.xy_tup( (0.5, 0.5) )==(205, 205))

pairs = ( ( (0,   0  ), (10,  10 )  ),
          ( (1,   1  ), (400, 400) ),
          ( (0.5, 0.5), (205, 205) ) )

for p1, p2 in pairs:
    assert( closeto_seq( transform.xy_tup(p1), p2 ) )
    assert( closeto_seq( transform.inverse_xy_tup(p2), p1) )

# make some random bbox transforms and test inversion
def rand_point():
    xy = rand(2)
    return Point( Value(xy[0]),  Value(xy[1]) )

def rand_bbox():
    ll = rand_point()
    ur = rand_point()
    return Bbox(ll, ur)

def rand_transform():
    b1 = rand_bbox()
    b2 = rand_bbox()
    return get_bbox_transform(b1, b2)


transform = rand_transform()
transform.set_funcx(Func(LOG10))

x = rand(100)
y = rand(100)
xys = zip(x,y)
for xy in xys:
    xyt = transform.xy_tup(xy)
    xyi = transform.inverse_xy_tup(xyt)
    assert( closeto_seq(xy,xyi) )


ll = Point( Value(-10),  Value(-10) )
ur = Point( Value(200), Value(40) )
bbox = Bbox(ll, ur)
assert(bbox.xmin()==-10)
assert(bbox.xmax()==200)
assert(bbox.ymin()==-10)
assert(bbox.ymax()==40)

bbox.update(xys, False)  # don't ignore current lim

bbox.update(xys, True)  #ignore current lim
assert(bbox.xmin()==min(x))
assert(bbox.xmax()==max(x))
assert(bbox.ymin()==min(y))
assert(bbox.ymax()==max(y))


ll = Point( Value(-10),  Value(-10) )
ur = Point( Value(200), Value(40) )
bbox = Bbox(ll, ur)

ix = bbox.intervalx()
iy = bbox.intervaly()

assert(bbox.xmin()==-10)
assert(bbox.xmax()==200)
assert(bbox.ymin()==-10)
assert(bbox.ymax()==40)

ix.set_bounds(-30, 400)
assert(bbox.xmin()==-30)
assert(bbox.xmax()==400)
assert(bbox.ymin()==-10)
assert(bbox.ymax()==40)


num = Value(200.0)
den = Value(100.0)
div = num/den
assert(div.get()==2.0)


# test the inverse bbox functions
trans = rand_transform()
bbox1 = rand_bbox() 
ibbox = inverse_transform_bbox(trans, bbox1)
bbox2 = transform_bbox(trans, ibbox)
assert(closeto_bbox(bbox1, bbox2))


ll = Point( Value(-10),  Value(-10) )
ur = Point( Value(200), Value(40) )
bbox = Bbox(ll, ur)
transform = get_bbox_transform(unit_bbox(), bbox)
assert( closeto_seq( inverse_transform_bbox(transform, bbox).get_bounds(),
                     (0,0,1,1)))
assert( closeto_seq( transform_bbox(transform, unit_bbox()).get_bounds(),
                     (-10,-10,210,50)))


# test the bbox all bounding functions
boxes = [rand_bbox() for i in range(20)]
xmin = min([box.xmin() for box in boxes])
xmax = max([box.xmax() for box in boxes])
ymin = min([box.ymin() for box in boxes])
ymax = max([box.ymax() for box in boxes])

box = bbox_all(boxes)
assert( closeto_seq( box.get_bounds(), (xmin, ymin, xmax-xmin, ymax-ymin)))




t1 = rand_transform()
oboundsx = t1.get_bbox1().intervalx().get_bounds()
oboundsy = t1.get_bbox1().intervaly().get_bounds()
t2 = copy_bbox_transform(t1)
t1.get_bbox1().intervalx().set_bounds(1,2)
t2.get_bbox2().intervaly().set_bounds(-1,12)
newboundsx = t2.get_bbox1().intervalx().get_bounds()
newboundsy = t2.get_bbox1().intervaly().get_bounds()
assert(oboundsx==newboundsx)
assert(oboundsy==newboundsy)


import math
polar = FuncXY(POLAR)
assert( closeto_seq( polar.map(math.pi,1), (-1,0)) )
assert( closeto_seq( polar.inverse(1,1), ( (math.pi/4), math.sqrt(2))) )



# This unit test requires "nan", which numarray.ieeespecial
# exports.  (But we can keep using the numerix module.)
try:
    from numarray.ieeespecial import nan
    have_nan = True
except ImportError:
    have_nan = False

if have_nan:
    y1=array([  2,nan,1,2,3,4])
    y2=array([nan,nan,1,2,3,4])

    x1=arange(len(y1))
    x2=arange(len(y2))

    bbox1 = Bbox(Point(Value(0),Value(0)),
                 Point(Value(1),Value(1)))

    bbox2 = Bbox(Point(Value(0),Value(0)),
                 Point(Value(1),Value(1)))

    bbox1.update_numerix(x1,y1,1)
    bbox2.update_numerix(x2,y2,1)

    assert( closeto_seq( bbox1.get_bounds(), bbox2.get_bounds() ) )
else:
    print 'nan could not be imported from numarray.ieeespecial, test skipped'
 
print 'all tests passed'
