arrayTools.py revision 41144b93069d17dc6aeed494016ba8d47c74f1e9
1# 2# Various array and rectangle tools, but mostly rectangles, hence the 3# name of this module (not). 4# 5 6import Numeric 7 8def calcBounds(array): 9 """Return the bounding rectangle of a 2D points array as a tuple: 10 (xMin, yMin, xMax, yMax) 11 """ 12 if len(array) == 0: 13 return 0, 0, 0, 0 14 xMin, yMin = Numeric.minimum.reduce(array) 15 xMax, yMax = Numeric.maximum.reduce(array) 16 return xMin, yMin, xMax, yMax 17 18def updateBounds(bounds, (x, y), min=min, max=max): 19 """Return the bounding recangle of rectangle bounds and point (x, y).""" 20 xMin, yMin, xMax, yMax = bounds 21 return min(xMin, x), min(yMin, y), max(xMax, x), max(yMax, y) 22 23def pointInRect((x, y), rect): 24 """Return True when point (x, y) is inside rect.""" 25 xMin, yMin, xMax, yMax = rect 26 return (xMin <= x <= xMax) and (yMin <= y <= yMax) 27 28def pointsInRect(array, rect): 29 """Find out which points or array are inside rect. 30 Returns an array with a boolean for each point. 31 """ 32 if len(array) < 1: 33 return [] 34 lefttop = rect[:2] 35 rightbottom = rect[2:] 36 condition = Numeric.logical_and( 37 Numeric.greater_equal(array, lefttop), 38 Numeric.less_equal(array, rightbottom)) 39 return Numeric.logical_and.reduce(condition, -1) 40 41def vectorLength(vector): 42 """Return the length of the given vector.""" 43 return Numeric.sqrt(vector[0]**2 + vector[1]**2) 44 45def asInt16(array): 46 """Round and cast to 16 bit integer.""" 47 return Numeric.floor(array + 0.5).astype(Numeric.Int16) 48 49 50def normRect((xMin, yMin, xMax, yMax)): 51 """Normalize the rectangle so that the following holds: 52 xMin <= xMax and yMin <= yMax 53 """ 54 return min(xMin, xMax), min(yMin, yMax), max(xMin, xMax), max(yMin, yMax) 55 56def scaleRect((xMin, yMin, xMax, yMax), x, y): 57 """Scale the rectangle by x, y.""" 58 return xMin * x, yMin * y, xMax * x, yMax * y 59 60def offsetRect((xMin, yMin, xMax, yMax), dx, dy): 61 """Offset the rectangle by dx, dy.""" 62 return xMin+dx, yMin+dy, xMax+dx, yMax+dy 63 64def insetRect((xMin, yMin, xMax, yMax), dx, dy): 65 """Inset the rectangle by dx, dy on all sides.""" 66 return xMin+dx, yMin+dy, xMax-dx, yMax-dy 67 68def sectRect((xMin1, yMin1, xMax1, yMax1), (xMin2, yMin2, xMax2, yMax2)): 69 """Return a boolean and a rectangle. If the input rectangles intersect, return 70 True and the intersecting rectangle. Return False and (0, 0, 0, 0) if the input 71 rectangles don't intersect. 72 """ 73 xMin, yMin, xMax, yMax = (max(xMin1, xMin2), max(yMin1, yMin2), 74 min(xMax1, xMax2), min(yMax1, yMax2)) 75 if xMin >= xMax or yMin >= yMax: 76 return 0, (0, 0, 0, 0) 77 return 1, (xMin, yMin, xMax, yMax) 78 79def unionRect((xMin1, yMin1, xMax1, yMax1), (xMin2, yMin2, xMax2, yMax2)): 80 """Return the smallest rectangle in which both input rectangles are fully 81 enclosed. In other words, return the total bounding rectangle of both input 82 rectangles. 83 """ 84 xMin, yMin, xMax, yMax = (min(xMin1, xMin2), min(yMin1, yMin2), 85 max(xMax1, xMax2), max(yMax1, yMax2)) 86 return (xMin, yMin, xMax, yMax) 87 88def rectCenter((xMin, yMin, xMax, yMax)): 89 """Return the center of the rectangle as an (x, y) coordinate.""" 90 return (xMin+xMax)/2, (yMin+yMax)/2 91 92def intRect((xMin, yMin, xMax, yMax)): 93 """Return the rectangle, rounded off to integer values, but guaranteeing that 94 the resulting rectangle is NOT smaller than the original. 95 """ 96 import math 97 xMin = int(math.floor(xMin)) 98 yMin = int(math.floor(yMin)) 99 xMax = int(math.ceil(xMax)) 100 yMax = int(math.ceil(yMax)) 101 return (xMin, yMin, xMax, yMax) 102 103 104def _test(): 105 """ 106 >>> import math 107 >>> calcBounds([(0, 40), (0, 100), (50, 50), (80, 10)]) 108 (0, 10, 80, 100) 109 >>> updateBounds((0, 0, 0, 0), (100, 100)) 110 (0, 0, 100, 100) 111 >>> pointInRect((50, 50), (0, 0, 100, 100)) 112 True 113 >>> pointInRect((0, 0), (0, 0, 100, 100)) 114 True 115 >>> pointInRect((100, 100), (0, 0, 100, 100)) 116 True 117 >>> not pointInRect((101, 100), (0, 0, 100, 100)) 118 True 119 >>> list(pointsInRect([(50, 50), (0, 0), (100, 100), (101, 100)], (0, 0, 100, 100))) 120 [1, 1, 1, 0] 121 >>> vectorLength((3, 4)) 122 5.0 123 >>> vectorLength((1, 1)) == math.sqrt(2) 124 True 125 >>> list(asInt16(Numeric.array([0, 0.1, 0.5, 0.9]))) 126 [0, 0, 1, 1] 127 >>> normRect((0, 10, 100, 200)) 128 (0, 10, 100, 200) 129 >>> normRect((100, 200, 0, 10)) 130 (0, 10, 100, 200) 131 >>> scaleRect((10, 20, 50, 150), 1.5, 2) 132 (15.0, 40, 75.0, 300) 133 >>> offsetRect((10, 20, 30, 40), 5, 6) 134 (15, 26, 35, 46) 135 >>> insetRect((10, 20, 50, 60), 5, 10) 136 (15, 30, 45, 50) 137 >>> insetRect((10, 20, 50, 60), -5, -10) 138 (5, 10, 55, 70) 139 >>> intersects, rect = sectRect((0, 10, 20, 30), (0, 40, 20, 50)) 140 >>> not intersects 141 True 142 >>> intersects, rect = sectRect((0, 10, 20, 30), (5, 20, 35, 50)) 143 >>> intersects 144 1 145 >>> rect 146 (5, 20, 20, 30) 147 >>> unionRect((0, 10, 20, 30), (0, 40, 20, 50)) 148 (0, 10, 20, 50) 149 >>> rectCenter((0, 0, 100, 200)) 150 (50, 100) 151 >>> rectCenter((0, 0, 100, 199.0)) 152 (50, 99.5) 153 >>> intRect((0.9, 2.9, 3.1, 4.1)) 154 (0, 2, 4, 5) 155 """ 156 157if __name__ == "__main__": 158 import doctest 159 doctest.testmod() 160