# Generic Image Class
#
# Built on top of the 'gd-1.2' library by Thomas Boutell
#

import gd

BLACK = 0
WHITE = 1
RED   = 2
GREEN = 3
BLUE  = 4

class Image:
	def __init__(self,width,height,xmin=0.0,ymin=0.0,xmax=1.0,ymax=1.0):
		self.im = gd.gdImageCreate(width,height)
		self.xmin   = xmin
		self.ymin   = ymin
		self.xmax   = xmax
		self.ymax   = ymax
		self.width  = width
		self.height = height
		self.dx     = 1.0*(xmax-xmin)
		self.dy     = 1.0*(ymax-ymin)
		self.xtick  = self.dx/10.0
		self.ytick  = self.dy/10.0
		self.ticklen= 3
		self.name   = "image.gif"
		gd.gdImageColorAllocate(self.im,0,0,0)        # Black
		gd.gdImageColorAllocate(self.im,255,255,255)  # White
		gd.gdImageColorAllocate(self.im,255,0,0)      # Red
		gd.gdImageColorAllocate(self.im,0,255,0)      # Green
		gd.gdImageColorAllocate(self.im,0,0,255)      # Blue

	def __del__(self):
		print "Deleting"
		gd.gdImageDestroy(self.im)
	
	# Dump out this image to a file

	def write(self,name="NONE"):
		if name == "NONE":
			name = self.name
		f = gd.fopen(name,"w")
		gd.gdImageGif(self.im,f)
		gd.fclose(f)
		self.name = name
		print "Wrote '",name,"'"
	
	# Virtual method that the user classes define

	def draw(self):
		print "No drawing method specified."

	# A combination of write and draw

	def show(self,filename="NONE"):
		self.draw()
		self.write(filename)

	# Load up a colormap from an array of tuples

	def colormap(self, cmap):
		for i in range(0,255):
			gd.gdImageColorDeallocate(self.im,i)
		for c in cmap:
			gd.gdImageColorAllocate(self.im,c[0],c[1],c[2])

	# Change viewing region

	def region(self,xmin,ymin,xmax,ymax):
		self.xmin = xmin
		self.ymin = ymin
		self.xmax = xmax
		self.ymax = ymax
		self.dx     = 1.0*(xmax-xmin)
		self.dy     = 1.0*(ymax-ymin)

	# Transforms a point into screen coordinates

	def transform(self,x,y):
		npt = []
		ix = (x-self.xmin)/self.dx*self.width + 0.5
		iy = (self.ymax-y)/self.dy*self.height + 0.5
		return (ix,iy)

	# A few graphics primitives

	def clear(self,color):
		gd.gdImageFilledRectangle(self.im,0,0,self.width,self.height,color)

	def plot(self,x,y,color):
		ix,iy = self.transform(x,y)
		gd.gdImageSetPixel(self.im,ix,iy,color)

	def line(self,x1,y1,x2,y2,color):
		ix1,iy1 = self.transform(x1,y1)
		ix2,iy2 = self.transform(x2,y2)
		gd.gdImageLine(self.im,ix1,iy1,ix2,iy2,color)

	def box(self,x1,y1,x2,y2,color):
		ix1,iy1 = self.transform(x1,y1)
		ix2,iy2 = self.transform(x2,y2)
		gd.gdImageRectangle(self.im,ix1,iy1,ix2,iy2,color)

	def solidbox(self,x1,y1,x2,y2,color):
		ix1,iy1 = self.transform(x1,y1)
		ix2,iy2 = self.transform(x2,y2)
		gd.gdImageFilledRectangle(self.im,ix1,iy1,ix2,iy2,color)
	
	def arc(self,cx,cy,w,h,s,e,color):
		ix,iy = self.transform(cx,cy)
		iw = (x - self.xmin)/self.dx * self.width
		ih = (y - self.ymin)/self.dy * self.height
		gd.gdImageArc(self.im,ix,iy,iw,ih,s,e,color)

	def fill(self,x,y,color):
		ix,iy = self.transform(x,y)
		gd.gdImageFill(self,ix,iy,color)

	def axis(self,color):
		self.line(self.xmin,0,self.xmax,0,color)
		self.line(0,self.ymin,0,self.ymax,color)
		x = -self.xtick*(int(-self.xmin/self.xtick)+1)
		while x <= self.xmax:
			ix,iy = self.transform(x,0)
			gd.gdImageLine(self.im,ix,iy-self.ticklen,ix,iy+self.ticklen,color)
			x = x + self.xtick
		y = -self.ytick*(int(-self.ymin/self.ytick)+1)
		while y <= self.ymax:
			ix,iy = self.transform(0,y)
			gd.gdImageLine(self.im,ix-self.ticklen,iy,ix+self.ticklen,iy,color)
			y = y + self.ytick

	# scalex(s).  Scales the x-axis.  s is given as a scaling factor

	def scalex(self,s):
		xc = self.xmin + self.dx/2.0
		dx = self.dx*s
		xmin = xc - dx/2.0
		xmax = xc + dx/2.0
		self.region(xmin,self.ymin,xmax,self.ymax)

	# scaley(s).  Scales the y-axis.  s is given as a percent with s = 100 meaning no effect

	def scaley(self,s):
		yc = self.ymin + self.dy/2.0
		dy = self.dy*s
		ymin = yc - dy/2.0
		ymax = yc + dy/2.0
		self.region(self.xmin,ymin,self.xmax,ymax)

	# Zooms a current image.  s is given as a percent with s = 100 meaning no effect

	def zoom(self,s):
		s = 100.0/s
		self.scalex(s)
		self.scaley(s)

	# Move image left.  s is given in range 0,100. 100 moves a full screen left

	def left(self,s):
		dx = self.dx*s/100.0
		xmin = self.xmin + dx
		xmax = self.xmax + dx
		self.region(xmin,self.ymin,xmax,self.ymax)

	# Move image right.  s is given in range 0,100. 100 moves a full screen right

	def right(self,s):
		self.left(-s)

	# Move image down.  s is given in range 0,100. 100 moves a full screen down

	def down(self,s):
		dy = self.dy*s/100.0
		ymin = self.ymin + dy
		ymax = self.ymax + dy
		self.region(self.xmin,ymin,self.xmax,ymax)

	# Move image up.  s is given in range 0,100. 100 moves a full screen up

	def up(self,s):
		self.down(-s)

	# Center image

	def center(self,x,y):
		self.right(50-x)
		self.up(50-y)
	
