# gridsample.py
# by: John Zelle 3/2018
# Python 2 compatibility
from __future__ import division, print_function
try:
input = raw_input
except NameError:
pass
class GridSample:
"""A grid sample is sampling of a function in the XY plane suitable
for computation of iteration counts in Mandelbrot, Julia, and
related sets. Conceptually, a grid is a rectangular array of
cells where each cell has:
location: (px,py) is the offset of the cell from the upper left
corner. px and py are integers ranging from (0,0) in
the upper left to (width-1, height-1) in the lower
right.
coords: (x,y) is the coordinates of a point on the XY plane. x
and y are floating point values based on the
coordinates assigned to the lower-left and upper-right
corner of of the grid.
value: A function value associated with each cell. For
Mandelbrot and Julia sets, this is an int that is the
"iteration count" corresponding to the coords of the
cell.
"""
def __init__(self, width, height):
"""Creates a Grid Sample with given width and height"""
self._size = (width, height)
self._grid = [[0] * width for i in range(height)]
self.setCoords(-2.5,-1,1,1)
def setCoords(self, left, bottom, right, top):
"""Sets the overall grid coordinates to run from (left,bottom) at the
lower-left to (right, top) at the upper-right
"""
#print("setCoords", left, bottom, right, top)
self._coords = left, bottom, right, top
width, height = self._size
self.xinc = (right-left)/(width-1)
self.yinc = (bottom-top)/(height-1)
def resolution(self):
""" Returns the grid dimensions (width,height)"""
return self._size
def gridCoords(self):
return self._coords
def locations(self):
"""Returns a sequence of pairs representing the legal locations (px,py)
Usage: for px,py in grid.locations():
"""
width, height = self._size
for px in range(width):
for py in range(height):
yield (px,py)
def value(self, px, py):
"""Returns function value at cell with location (px,py)
"""
return self._grid[py][px]
def coords(self, px, py):
"""Returns the (x,y) coords for cell at location (px,py)"""
left, _, _, top = self._coords
return (left+px*self.xinc, top+py*self.yinc)
def samplefn(self,fn):
"""Samples the function fn
pre: fn is a function that takes two float parameters
post: the value of each cell is fn(x,y) where (x,y) are the coords
of the cell.
"""
width, height = self._size
left, _, _, top = self._coords
x = left
for px in range(width):
y = top
for py in range(height):
#print("px,py", px, py)
self._grid[py][px] = fn(x,y)
y += self.yinc
x += self.xinc
def histogram(self):
"""Returns a histogram of the values in grid
pre: self.samplefn(f) has been called with an int-valued function, f, where
f(x,y) >= 0 for every cell.
post: result is a sequence of length maxval+1 where maxval is
the largest cell value in the grid and result[i] is the
count of cells in grid having value i.
"""
maxval = max(self.value(x,y) for x,y in self.locations())
hist = [0] * (maxval+1)
for x,y in self.locations():
hist[self.value(x,y)] += 1
return hist
def toFile(self, fname):
"""Writes the contents of a grid to file named fname"""
with open(fname, 'w') as outfile:
w,h = self._size
left, bottom, right, top = self._coords
print(w,h, file=outfile)
print(left,bottom,right,top,file=outfile)
for row in self._grid:
for value in row:
print(value, file=outfile)
def fromFile(self, fname):
"""Reads grid in from file named fname
Note: This changes the resolution and size of the grid to match the
grid in the fname.
"""
with open(fname) as infile:
w,h = (int(x) for x in infile.readline().split())
self._size = w,h
l,b,r,t = (float(x) for x in infile.readline().split())
self.setCoords(l,b,r,t)
self._grid = []
for j in range(h):
row = []
for i in range(w):
row.append(int(infile.readline()))
self._grid.append(row)
# ------ Added for iterators -------
class Cell:
def __init__(self, loc, coords, value):
self.location = loc
self.coords = coords
self.value = value
def __iter__(self):
for px,py in self.locations():
yield self.Cell((px,py), self.coords(px,py), self._grid[py][px])