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