blob: dad9e859a8bb5702fea09df25fa616de0e0167ab [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
7import math
Behdad Esfahbod7ed91ec2013-11-27 15:16:28 -05008from .py23 import *
Just470b5052000-01-16 20:37:11 +00009
10def calcBounds(array):
jvr91bca422012-10-18 12:49:22 +000011 """Return the bounding rectangle of a 2D points array as a tuple:
12 (xMin, yMin, xMax, yMax)
13 """
14 if len(array) == 0:
15 return 0, 0, 0, 0
16 xs = [x for x, y in array]
17 ys = [y for x, y in array]
18 return min(xs), min(ys), max(xs), max(ys)
Just470b5052000-01-16 20:37:11 +000019
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050020def updateBounds(bounds, p, min=min, max=max):
jvr91bca422012-10-18 12:49:22 +000021 """Return the bounding recangle of rectangle bounds and point (x, y)."""
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050022 (x, y) = p
jvr91bca422012-10-18 12:49:22 +000023 xMin, yMin, xMax, yMax = bounds
24 return min(xMin, x), min(yMin, y), max(xMax, x), max(yMax, y)
jvr182a7ba2003-06-29 18:18:54 +000025
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050026def pointInRect(p, rect):
jvr91bca422012-10-18 12:49:22 +000027 """Return True when point (x, y) is inside rect."""
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050028 (x, y) = p
jvr91bca422012-10-18 12:49:22 +000029 xMin, yMin, xMax, yMax = rect
30 return (xMin <= x <= xMax) and (yMin <= y <= yMax)
jvr182a7ba2003-06-29 18:18:54 +000031
Just470b5052000-01-16 20:37:11 +000032def pointsInRect(array, rect):
jvr91bca422012-10-18 12:49:22 +000033 """Find out which points or array are inside rect.
34 Returns an array with a boolean for each point.
35 """
36 if len(array) < 1:
37 return []
38 xMin, yMin, xMax, yMax = rect
39 return [(xMin <= x <= xMax) and (yMin <= y <= yMax) for x, y in array]
Just470b5052000-01-16 20:37:11 +000040
Justdeb3b632000-01-26 19:32:45 +000041def vectorLength(vector):
jvr91bca422012-10-18 12:49:22 +000042 """Return the length of the given vector."""
43 x, y = vector
44 return math.sqrt(x**2 + y**2)
Justdeb3b632000-01-26 19:32:45 +000045
46def asInt16(array):
jvr91bca422012-10-18 12:49:22 +000047 """Round and cast to 16 bit integer."""
48 return [int(math.floor(i+0.5)) for i in array]
49
Just470b5052000-01-16 20:37:11 +000050
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050051def normRect(rect):
jvr91bca422012-10-18 12:49:22 +000052 """Normalize the rectangle so that the following holds:
53 xMin <= xMax and yMin <= yMax
54 """
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050055 (xMin, yMin, xMax, yMax) = rect
jvr91bca422012-10-18 12:49:22 +000056 return min(xMin, xMax), min(yMin, yMax), max(xMin, xMax), max(yMin, yMax)
Just470b5052000-01-16 20:37:11 +000057
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050058def scaleRect(rect, x, y):
jvr91bca422012-10-18 12:49:22 +000059 """Scale the rectangle by x, y."""
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050060 (xMin, yMin, xMax, yMax) = rect
jvr91bca422012-10-18 12:49:22 +000061 return xMin * x, yMin * y, xMax * x, yMax * y
Just470b5052000-01-16 20:37:11 +000062
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050063def offsetRect(rect, dx, dy):
jvr91bca422012-10-18 12:49:22 +000064 """Offset the rectangle by dx, dy."""
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050065 (xMin, yMin, xMax, yMax) = rect
jvr91bca422012-10-18 12:49:22 +000066 return xMin+dx, yMin+dy, xMax+dx, yMax+dy
Justbe7163c2000-01-18 22:29:39 +000067
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050068def insetRect(rect, dx, dy):
jvr91bca422012-10-18 12:49:22 +000069 """Inset the rectangle by dx, dy on all sides."""
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050070 (xMin, yMin, xMax, yMax) = rect
jvr91bca422012-10-18 12:49:22 +000071 return xMin+dx, yMin+dy, xMax-dx, yMax-dy
Justbe7163c2000-01-18 22:29:39 +000072
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050073def sectRect(rect1, rect2):
jvr91bca422012-10-18 12:49:22 +000074 """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 """
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050078 (xMin1, yMin1, xMax1, yMax1) = rect1
79 (xMin2, yMin2, xMax2, yMax2) = rect2
jvr91bca422012-10-18 12:49:22 +000080 xMin, yMin, xMax, yMax = (max(xMin1, xMin2), max(yMin1, yMin2),
81 min(xMax1, xMax2), min(yMax1, yMax2))
82 if xMin >= xMax or yMin >= yMax:
83 return 0, (0, 0, 0, 0)
84 return 1, (xMin, yMin, xMax, yMax)
Justbe7163c2000-01-18 22:29:39 +000085
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050086def unionRect(rect1, rect2):
jvr91bca422012-10-18 12:49:22 +000087 """Return the smallest rectangle in which both input rectangles are fully
88 enclosed. In other words, return the total bounding rectangle of both input
89 rectangles.
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 = (min(xMin1, xMin2), min(yMin1, yMin2),
94 max(xMax1, xMax2), max(yMax1, yMax2))
95 return (xMin, yMin, xMax, yMax)
Just02a739a2000-01-23 19:10:27 +000096
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050097def rectCenter(rect0):
jvr91bca422012-10-18 12:49:22 +000098 """Return the center of the rectangle as an (x, y) coordinate."""
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050099 (xMin, yMin, xMax, yMax) = rect0
jvr91bca422012-10-18 12:49:22 +0000100 return (xMin+xMax)/2, (yMin+yMax)/2
Just02a739a2000-01-23 19:10:27 +0000101
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500102def intRect(rect1):
jvr91bca422012-10-18 12:49:22 +0000103 """Return the rectangle, rounded off to integer values, but guaranteeing that
104 the resulting rectangle is NOT smaller than the original.
105 """
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500106 (xMin, yMin, xMax, yMax) = rect1
jvr91bca422012-10-18 12:49:22 +0000107 import math
108 xMin = int(math.floor(xMin))
109 yMin = int(math.floor(yMin))
110 xMax = int(math.ceil(xMax))
111 yMax = int(math.ceil(yMax))
112 return (xMin, yMin, xMax, yMax)
Justbe7163c2000-01-18 22:29:39 +0000113
jvrfc3879e2003-08-29 19:29:46 +0000114
jvr41144b92008-03-04 14:47:11 +0000115def _test():
jvr91bca422012-10-18 12:49:22 +0000116 """
117 >>> import math
118 >>> calcBounds([])
119 (0, 0, 0, 0)
120 >>> calcBounds([(0, 40), (0, 100), (50, 50), (80, 10)])
121 (0, 10, 80, 100)
122 >>> updateBounds((0, 0, 0, 0), (100, 100))
123 (0, 0, 100, 100)
124 >>> pointInRect((50, 50), (0, 0, 100, 100))
125 True
126 >>> pointInRect((0, 0), (0, 0, 100, 100))
127 True
128 >>> pointInRect((100, 100), (0, 0, 100, 100))
129 True
130 >>> not pointInRect((101, 100), (0, 0, 100, 100))
131 True
132 >>> list(pointsInRect([(50, 50), (0, 0), (100, 100), (101, 100)], (0, 0, 100, 100)))
133 [True, True, True, False]
134 >>> vectorLength((3, 4))
135 5.0
136 >>> vectorLength((1, 1)) == math.sqrt(2)
137 True
138 >>> list(asInt16([0, 0.1, 0.5, 0.9]))
139 [0, 0, 1, 1]
140 >>> normRect((0, 10, 100, 200))
141 (0, 10, 100, 200)
142 >>> normRect((100, 200, 0, 10))
143 (0, 10, 100, 200)
144 >>> scaleRect((10, 20, 50, 150), 1.5, 2)
145 (15.0, 40, 75.0, 300)
146 >>> offsetRect((10, 20, 30, 40), 5, 6)
147 (15, 26, 35, 46)
148 >>> insetRect((10, 20, 50, 60), 5, 10)
149 (15, 30, 45, 50)
150 >>> insetRect((10, 20, 50, 60), -5, -10)
151 (5, 10, 55, 70)
152 >>> intersects, rect = sectRect((0, 10, 20, 30), (0, 40, 20, 50))
153 >>> not intersects
154 True
155 >>> intersects, rect = sectRect((0, 10, 20, 30), (5, 20, 35, 50))
156 >>> intersects
157 1
158 >>> rect
159 (5, 20, 20, 30)
160 >>> unionRect((0, 10, 20, 30), (0, 40, 20, 50))
161 (0, 10, 20, 50)
162 >>> rectCenter((0, 0, 100, 200))
163 (50, 100)
164 >>> rectCenter((0, 0, 100, 199.0))
165 (50, 99.5)
166 >>> intRect((0.9, 2.9, 3.1, 4.1))
167 (0, 2, 4, 5)
168 """
jvr41144b92008-03-04 14:47:11 +0000169
jvrfc3879e2003-08-29 19:29:46 +0000170if __name__ == "__main__":
jvr91bca422012-10-18 12:49:22 +0000171 import doctest
172 doctest.testmod()