- renamed all l,t,r,b tuff to xMin, yMin, xMax, yMax
- added ome more doc strings
- added some minimal test code


git-svn-id: svn://svn.code.sf.net/p/fonttools/code/trunk@441 4cde692c-a291-49d1-8350-778aa11640f8
diff --git a/Lib/fontTools/misc/arrayTools.py b/Lib/fontTools/misc/arrayTools.py
index 02ace99..74ccd56 100644
--- a/Lib/fontTools/misc/arrayTools.py
+++ b/Lib/fontTools/misc/arrayTools.py
@@ -1,20 +1,19 @@
 #
-# Various array and rectangle tools
+# Various array and rectangle tools, but mostly rectangles, hence the
+# name of this module (not).
 #
 
-import Numeric
-
 
 def calcBounds(array):
-	"""Calculate the bounding rectangle of a 2D array.
-	Returns a 4-tuple:
-		smallest x, smallest y, largest x, largest y.
+	"""Return the bounding rectangle of a 2D points array as a tuple:
+	(xMin, yMin, xMax, yMax)
 	"""
+	import Numeric
 	if len(array) == 0:
 		return 0, 0, 0, 0
-	xmin, ymin = Numeric.minimum.reduce(array)
-	xmax, ymax = Numeric.maximum.reduce(array)
-	return xmin, ymin, xmax, ymax
+	xMin, yMin = Numeric.minimum.reduce(array)
+	xMax, yMax = Numeric.maximum.reduce(array)
+	return xMin, yMin, xMax, yMax
 
 def updateBounds(bounds, (x, y), min=min, max=max):
 	"""Return the bounding recangle of rectangle bounds and point (x, y)."""
@@ -30,52 +29,106 @@
 	"""Find out which points or array are inside rect. 
 	Returns an array with a boolean for each point.
 	"""
+	import Numeric
 	if len(array) < 1:
 		return []
 	lefttop = rect[:2]
 	rightbottom = rect[2:]
 	condition = Numeric.logical_and(
-			Numeric.greater(array, lefttop), 
-			Numeric.less(array, rightbottom))
+			Numeric.greater_equal(array, lefttop), 
+			Numeric.less_equal(array, rightbottom))
 	return Numeric.logical_and.reduce(condition, -1)
 
 def vectorLength(vector):
+	"""Return the length of the given vector."""
+	import Numeric
 	return Numeric.sqrt(vector[0]**2 + vector[1]**2)
 
 def asInt16(array):
-	"Round and cast to 16 bit integer."
+	"""Round and cast to 16 bit integer."""
+	import Numeric
 	return Numeric.floor(array + 0.5).astype(Numeric.Int16)
 	
 
-def normRect((l, t, r, b)):
-	"""XXX doc"""
-	return min(l, r), min(t, b), max(l, r), max(t, b)
+def normRect((xMin, yMin, xMax, yMax)):
+	"""Normalize the rectangle so that the following holds:
+		xMin <= xMax and yMin <= yMax
+	"""
+	return min(xMin, xMax), min(yMin, yMax), max(xMin, xMax), max(yMin, yMax)
 
-def scaleRect((l, t, r, b), x, y):
-	return l * x, t * y, r * x, b * y
+def scaleRect((xMin, yMin, xMax, yMax), x, y):
+	"""Scale the rectangle by x, y."""
+	return xMin * x, yMin * y, xMax * x, yMax * y
 
-def offsetRect((l, t, r, b), dx, dy):
-	return l+dx, t+dy, r+dx, b+dy
+def offsetRect((xMin, yMin, xMax, yMax), dx, dy):
+	"""Offset the rectangle by dx, dy."""
+	return xMin+dx, yMin+dy, xMax+dx, yMax+dy
 
-def insetRect((l, t, r, b), dx, dy):
-	return l+dx, t+dy, r-dx, b-dy
+def insetRect((xMin, yMin, xMax, yMax), dx, dy):
+	"""Inset the rectangle by dx, dy on all sides."""
+	return xMin+dx, yMin+dy, xMax-dx, yMax-dy
 
-def sectRect((l1, t1, r1, b1), (l2, t2, r2, b2)):
-	l, t, r, b = max(l1, l2), max(t1, t2), min(r1, r2), min(b1, b2)
-	if l >= r or t >= b:
+def sectRect((xMin1, yMin1, xMax1, yMax1), (xMin2, yMin2, xMax2, yMax2)):
+	"""Return a boolean and a rectangle. If the input rectangles intersect, return
+	True and the intersecting rectangle. Return False and (0, 0, 0, 0) if the input
+	rectangles don't intersect.
+	"""
+	xMin, yMin, xMax, yMax = (max(xMin1, xMin2), max(yMin1, yMin2),
+	                          min(xMax1, xMax2), min(yMax1, yMax2))
+	if xMin >= xMax or yMin >= yMax:
 		return 0, (0, 0, 0, 0)
-	return 1, (l, t, r, b)
+	return 1, (xMin, yMin, xMax, yMax)
 
-def unionRect((l1, t1, r1, b1), (l2, t2, r2, b2)):
-	l, t, r, b = min(l1, l2), min(t1, t2), max(r1, r2), max(b1, b2)
-	return (l, t, r, b)
+def unionRect((xMin1, yMin1, xMax1, yMax1), (xMin2, yMin2, xMax2, yMax2)):
+	"""Return the smallest rectangle in which both input rectangles are fully
+	enclosed. In other words, return the total bounding rectangle of both input
+	rectangles.
+	"""
+	xMin, yMin, xMax, yMax = (min(xMin1, xMin2), min(yMin1, yMin2),
+	                          max(xMax1, xMax2), max(yMax1, yMax2))
+	return (xMin, yMin, xMax, yMax)
 
-def rectCenter((l, t, r, b)):
-	return (l+r)/2, (t+b)/2
+def rectCenter((xMin, yMin, xMax, yMax)):
+	"""Return the center of the rectangle as an (x, y) coordinate."""
+	return (xMin+xMax)/2, (yMin+yMax)/2
 
-def intRect(rect):
-	rect = Numeric.array(rect)
-	l, t = Numeric.floor(rect[:2])
-	r, b = Numeric.ceil(rect[2:])
-	return tuple(Numeric.array((l, t, r, b)).astype(Numeric.Int))
+def intRect((xMin, yMin, xMax, yMax)):
+	"""Return the rectangle, rounded off to integer values, but guaranteeing that
+	the resulting rectangle is NOT smaller than the original.
+	"""
+	import math
+	xMin = int(math.floor(xMin))
+	yMin = int(math.floor(yMin))
+	xMax = int(math.ceil(xMax))
+	yMax = int(math.ceil(yMax))
+	return (xMin, yMin, xMax, yMax)
 
+
+if __name__ == "__main__":
+	import Numeric, math
+	assert calcBounds([(0, 40), (0, 100), (50, 50), (80, 10)]) == (0, 10, 80, 100)
+	assert updateBounds((0, 0, 0, 0), (100, 100)) == (0, 0, 100, 100)
+	assert pointInRect((50, 50), (0, 0, 100, 100))
+	assert pointInRect((0, 0), (0, 0, 100, 100))
+	assert pointInRect((100, 100), (0, 0, 100, 100))
+	assert not pointInRect((101, 100), (0, 0, 100, 100))
+	assert list(pointsInRect([(50, 50), (0, 0), (100, 100), (101, 100)],
+			(0, 0, 100, 100))) == [1, 1, 1, 0]
+	assert vectorLength((3, 4)) == 5
+	assert vectorLength((1, 1)) == math.sqrt(2)
+	assert list(asInt16(Numeric.array([0, 0.1, 0.5, 0.9]))) == [0, 0, 1, 1]
+	assert normRect((0, 10, 100, 200)) == (0, 10, 100, 200)
+	assert normRect((100, 200, 0, 10)) == (0, 10, 100, 200)
+	assert scaleRect((10, 20, 50, 150), 1.5, 2) == (15, 40, 75, 300)
+	assert offsetRect((10, 20, 30, 40), 5, 6) == ((15, 26, 35, 46))
+	assert insetRect((10, 20, 50, 60), 5, 10) == (15, 30, 45, 50)
+	assert insetRect((10, 20, 50, 60), -5, -10) == (5, 10, 55, 70)
+	intersects, rect = sectRect((0, 10, 20, 30), (0, 40, 20, 50))
+	assert not intersects
+	intersects, rect = sectRect((0, 10, 20, 30), (5, 20, 35, 50))
+	assert intersects
+	assert rect == (5, 20, 20, 30)
+	assert unionRect((0, 10, 20, 30), (0, 40, 20, 50)) == (0, 10, 20, 50)
+	assert rectCenter((0, 0, 100, 200)) == (50, 100)
+	assert rectCenter((0, 0, 100, 199.0)) == (50, 99.5)
+	assert intRect((0.9, 2.9, 3.1, 4.1)) == (0, 2, 4, 5)