blob: 0daabd9ab4cbba23a9765bfbdba3c254c2986d72 [file] [log] [blame]
Just470b5052000-01-16 20:37:11 +00001#
jvrfc3879e2003-08-29 19:29:46 +00002# Various array and rectangle tools, but mostly rectangles, hence the
3# name of this module (not).
Just470b5052000-01-16 20:37:11 +00004#
5
jvr91bca422012-10-18 12:49:22 +00006
Behdad Esfahbod1ae29592014-01-14 15:07:50 +08007from __future__ import print_function, division, absolute_import
Behdad Esfahbod30e691e2013-11-27 17:27:45 -05008from fontTools.misc.py23 import *
jvr91bca422012-10-18 12:49:22 +00009import math
Just470b5052000-01-16 20:37:11 +000010
11def calcBounds(array):
jvr91bca422012-10-18 12:49:22 +000012 """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)
Just470b5052000-01-16 20:37:11 +000020
Behdad Esfahbodf2c2b4d2013-12-01 13:36:09 -050021def 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
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050033def updateBounds(bounds, p, min=min, max=max):
jvr91bca422012-10-18 12:49:22 +000034 """Return the bounding recangle of rectangle bounds and point (x, y)."""
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050035 (x, y) = p
jvr91bca422012-10-18 12:49:22 +000036 xMin, yMin, xMax, yMax = bounds
37 return min(xMin, x), min(yMin, y), max(xMax, x), max(yMax, y)
jvr182a7ba2003-06-29 18:18:54 +000038
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050039def pointInRect(p, rect):
jvr91bca422012-10-18 12:49:22 +000040 """Return True when point (x, y) is inside rect."""
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050041 (x, y) = p
jvr91bca422012-10-18 12:49:22 +000042 xMin, yMin, xMax, yMax = rect
43 return (xMin <= x <= xMax) and (yMin <= y <= yMax)
jvr182a7ba2003-06-29 18:18:54 +000044
Just470b5052000-01-16 20:37:11 +000045def pointsInRect(array, rect):
jvr91bca422012-10-18 12:49:22 +000046 """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]
Just470b5052000-01-16 20:37:11 +000053
Justdeb3b632000-01-26 19:32:45 +000054def vectorLength(vector):
jvr91bca422012-10-18 12:49:22 +000055 """Return the length of the given vector."""
56 x, y = vector
57 return math.sqrt(x**2 + y**2)
Justdeb3b632000-01-26 19:32:45 +000058
59def asInt16(array):
jvr91bca422012-10-18 12:49:22 +000060 """Round and cast to 16 bit integer."""
61 return [int(math.floor(i+0.5)) for i in array]
62
Just470b5052000-01-16 20:37:11 +000063
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050064def normRect(rect):
jvr91bca422012-10-18 12:49:22 +000065 """Normalize the rectangle so that the following holds:
66 xMin <= xMax and yMin <= yMax
67 """
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050068 (xMin, yMin, xMax, yMax) = rect
jvr91bca422012-10-18 12:49:22 +000069 return min(xMin, xMax), min(yMin, yMax), max(xMin, xMax), max(yMin, yMax)
Just470b5052000-01-16 20:37:11 +000070
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050071def scaleRect(rect, x, y):
jvr91bca422012-10-18 12:49:22 +000072 """Scale the rectangle by x, y."""
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050073 (xMin, yMin, xMax, yMax) = rect
jvr91bca422012-10-18 12:49:22 +000074 return xMin * x, yMin * y, xMax * x, yMax * y
Just470b5052000-01-16 20:37:11 +000075
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050076def offsetRect(rect, dx, dy):
jvr91bca422012-10-18 12:49:22 +000077 """Offset the rectangle by dx, dy."""
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050078 (xMin, yMin, xMax, yMax) = rect
jvr91bca422012-10-18 12:49:22 +000079 return xMin+dx, yMin+dy, xMax+dx, yMax+dy
Justbe7163c2000-01-18 22:29:39 +000080
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050081def insetRect(rect, dx, dy):
jvr91bca422012-10-18 12:49:22 +000082 """Inset the rectangle by dx, dy on all sides."""
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050083 (xMin, yMin, xMax, yMax) = rect
jvr91bca422012-10-18 12:49:22 +000084 return xMin+dx, yMin+dy, xMax-dx, yMax-dy
Justbe7163c2000-01-18 22:29:39 +000085
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050086def sectRect(rect1, rect2):
jvr91bca422012-10-18 12:49:22 +000087 """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 """
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050091 (xMin1, yMin1, xMax1, yMax1) = rect1
92 (xMin2, yMin2, xMax2, yMax2) = rect2
jvr91bca422012-10-18 12:49:22 +000093 xMin, yMin, xMax, yMax = (max(xMin1, xMin2), max(yMin1, yMin2),
94 min(xMax1, xMax2), min(yMax1, yMax2))
95 if xMin >= xMax or yMin >= yMax:
Behdad Esfahboddc873722013-12-04 21:28:50 -050096 return False, (0, 0, 0, 0)
97 return True, (xMin, yMin, xMax, yMax)
Justbe7163c2000-01-18 22:29:39 +000098
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050099def unionRect(rect1, rect2):
jvr91bca422012-10-18 12:49:22 +0000100 """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 """
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500104 (xMin1, yMin1, xMax1, yMax1) = rect1
105 (xMin2, yMin2, xMax2, yMax2) = rect2
jvr91bca422012-10-18 12:49:22 +0000106 xMin, yMin, xMax, yMax = (min(xMin1, xMin2), min(yMin1, yMin2),
107 max(xMax1, xMax2), max(yMax1, yMax2))
108 return (xMin, yMin, xMax, yMax)
Just02a739a2000-01-23 19:10:27 +0000109
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500110def rectCenter(rect0):
jvr91bca422012-10-18 12:49:22 +0000111 """Return the center of the rectangle as an (x, y) coordinate."""
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500112 (xMin, yMin, xMax, yMax) = rect0
jvr91bca422012-10-18 12:49:22 +0000113 return (xMin+xMax)/2, (yMin+yMax)/2
Just02a739a2000-01-23 19:10:27 +0000114
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500115def intRect(rect1):
jvr91bca422012-10-18 12:49:22 +0000116 """Return the rectangle, rounded off to integer values, but guaranteeing that
117 the resulting rectangle is NOT smaller than the original.
118 """
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500119 (xMin, yMin, xMax, yMax) = rect1
jvr91bca422012-10-18 12:49:22 +0000120 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)
Justbe7163c2000-01-18 22:29:39 +0000125
jvrfc3879e2003-08-29 19:29:46 +0000126
jvr41144b92008-03-04 14:47:11 +0000127def _test():
jvr91bca422012-10-18 12:49:22 +0000128 """
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))
Behdad Esfahbod32c10ee2013-11-27 17:46:17 -0500175 (50.0, 100.0)
jvr91bca422012-10-18 12:49:22 +0000176 >>> rectCenter((0, 0, 100, 199.0))
Behdad Esfahbod32c10ee2013-11-27 17:46:17 -0500177 (50.0, 99.5)
jvr91bca422012-10-18 12:49:22 +0000178 >>> intRect((0.9, 2.9, 3.1, 4.1))
179 (0, 2, 4, 5)
180 """
jvr41144b92008-03-04 14:47:11 +0000181
jvrfc3879e2003-08-29 19:29:46 +0000182if __name__ == "__main__":
jvr91bca422012-10-18 12:49:22 +0000183 import doctest
184 doctest.testmod()