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