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