blob: cab30665c3ae24b367b83bd42cdd8c9227224ada [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 Esfahbod30e691e2013-11-27 17:27:45 -05007from __future__ import print_function
8from 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 Esfahbod3a9fd302013-11-27 03:19:32 -050021def updateBounds(bounds, p, min=min, max=max):
jvr91bca422012-10-18 12:49:22 +000022 """Return the bounding recangle of rectangle bounds and point (x, y)."""
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050023 (x, y) = p
jvr91bca422012-10-18 12:49:22 +000024 xMin, yMin, xMax, yMax = bounds
25 return min(xMin, x), min(yMin, y), max(xMax, x), max(yMax, y)
jvr182a7ba2003-06-29 18:18:54 +000026
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050027def pointInRect(p, rect):
jvr91bca422012-10-18 12:49:22 +000028 """Return True when point (x, y) is inside rect."""
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050029 (x, y) = p
jvr91bca422012-10-18 12:49:22 +000030 xMin, yMin, xMax, yMax = rect
31 return (xMin <= x <= xMax) and (yMin <= y <= yMax)
jvr182a7ba2003-06-29 18:18:54 +000032
Just470b5052000-01-16 20:37:11 +000033def pointsInRect(array, rect):
jvr91bca422012-10-18 12:49:22 +000034 """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]
Just470b5052000-01-16 20:37:11 +000041
Justdeb3b632000-01-26 19:32:45 +000042def vectorLength(vector):
jvr91bca422012-10-18 12:49:22 +000043 """Return the length of the given vector."""
44 x, y = vector
45 return math.sqrt(x**2 + y**2)
Justdeb3b632000-01-26 19:32:45 +000046
47def asInt16(array):
jvr91bca422012-10-18 12:49:22 +000048 """Round and cast to 16 bit integer."""
49 return [int(math.floor(i+0.5)) for i in array]
50
Just470b5052000-01-16 20:37:11 +000051
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050052def normRect(rect):
jvr91bca422012-10-18 12:49:22 +000053 """Normalize the rectangle so that the following holds:
54 xMin <= xMax and yMin <= yMax
55 """
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050056 (xMin, yMin, xMax, yMax) = rect
jvr91bca422012-10-18 12:49:22 +000057 return min(xMin, xMax), min(yMin, yMax), max(xMin, xMax), max(yMin, yMax)
Just470b5052000-01-16 20:37:11 +000058
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050059def scaleRect(rect, x, y):
jvr91bca422012-10-18 12:49:22 +000060 """Scale the rectangle by x, y."""
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050061 (xMin, yMin, xMax, yMax) = rect
jvr91bca422012-10-18 12:49:22 +000062 return xMin * x, yMin * y, xMax * x, yMax * y
Just470b5052000-01-16 20:37:11 +000063
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050064def offsetRect(rect, dx, dy):
jvr91bca422012-10-18 12:49:22 +000065 """Offset the rectangle by dx, dy."""
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050066 (xMin, yMin, xMax, yMax) = rect
jvr91bca422012-10-18 12:49:22 +000067 return xMin+dx, yMin+dy, xMax+dx, yMax+dy
Justbe7163c2000-01-18 22:29:39 +000068
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050069def insetRect(rect, dx, dy):
jvr91bca422012-10-18 12:49:22 +000070 """Inset the rectangle by dx, dy on all sides."""
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050071 (xMin, yMin, xMax, yMax) = rect
jvr91bca422012-10-18 12:49:22 +000072 return xMin+dx, yMin+dy, xMax-dx, yMax-dy
Justbe7163c2000-01-18 22:29:39 +000073
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050074def sectRect(rect1, rect2):
jvr91bca422012-10-18 12:49:22 +000075 """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 """
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050079 (xMin1, yMin1, xMax1, yMax1) = rect1
80 (xMin2, yMin2, xMax2, yMax2) = rect2
jvr91bca422012-10-18 12:49:22 +000081 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)
Justbe7163c2000-01-18 22:29:39 +000086
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050087def unionRect(rect1, rect2):
jvr91bca422012-10-18 12:49:22 +000088 """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 """
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050092 (xMin1, yMin1, xMax1, yMax1) = rect1
93 (xMin2, yMin2, xMax2, yMax2) = rect2
jvr91bca422012-10-18 12:49:22 +000094 xMin, yMin, xMax, yMax = (min(xMin1, xMin2), min(yMin1, yMin2),
95 max(xMax1, xMax2), max(yMax1, yMax2))
96 return (xMin, yMin, xMax, yMax)
Just02a739a2000-01-23 19:10:27 +000097
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050098def rectCenter(rect0):
jvr91bca422012-10-18 12:49:22 +000099 """Return the center of the rectangle as an (x, y) coordinate."""
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500100 (xMin, yMin, xMax, yMax) = rect0
jvr91bca422012-10-18 12:49:22 +0000101 return (xMin+xMax)/2, (yMin+yMax)/2
Just02a739a2000-01-23 19:10:27 +0000102
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500103def intRect(rect1):
jvr91bca422012-10-18 12:49:22 +0000104 """Return the rectangle, rounded off to integer values, but guaranteeing that
105 the resulting rectangle is NOT smaller than the original.
106 """
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500107 (xMin, yMin, xMax, yMax) = rect1
jvr91bca422012-10-18 12:49:22 +0000108 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)
Justbe7163c2000-01-18 22:29:39 +0000114
jvrfc3879e2003-08-29 19:29:46 +0000115
jvr41144b92008-03-04 14:47:11 +0000116def _test():
jvr91bca422012-10-18 12:49:22 +0000117 """
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, 100)
165 >>> rectCenter((0, 0, 100, 199.0))
166 (50, 99.5)
167 >>> intRect((0.9, 2.9, 3.1, 4.1))
168 (0, 2, 4, 5)
169 """
jvr41144b92008-03-04 14:47:11 +0000170
jvrfc3879e2003-08-29 19:29:46 +0000171if __name__ == "__main__":
jvr91bca422012-10-18 12:49:22 +0000172 import doctest
173 doctest.testmod()