blob: 2a3c22598d372e16406395d991ba02cde1d3e1d5 [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
Just470b5052000-01-16 20:37:11 +00008
9def calcBounds(array):
jvr91bca422012-10-18 12:49:22 +000010 """Return the bounding rectangle of a 2D points array as a tuple:
11 (xMin, yMin, xMax, yMax)
12 """
13 if len(array) == 0:
14 return 0, 0, 0, 0
15 xs = [x for x, y in array]
16 ys = [y for x, y in array]
17 return min(xs), min(ys), max(xs), max(ys)
Just470b5052000-01-16 20:37:11 +000018
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050019def updateBounds(bounds, p, min=min, max=max):
jvr91bca422012-10-18 12:49:22 +000020 """Return the bounding recangle of rectangle bounds and point (x, y)."""
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050021 (x, y) = p
jvr91bca422012-10-18 12:49:22 +000022 xMin, yMin, xMax, yMax = bounds
23 return min(xMin, x), min(yMin, y), max(xMax, x), max(yMax, y)
jvr182a7ba2003-06-29 18:18:54 +000024
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050025def pointInRect(p, rect):
jvr91bca422012-10-18 12:49:22 +000026 """Return True when point (x, y) is inside rect."""
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050027 (x, y) = p
jvr91bca422012-10-18 12:49:22 +000028 xMin, yMin, xMax, yMax = rect
29 return (xMin <= x <= xMax) and (yMin <= y <= yMax)
jvr182a7ba2003-06-29 18:18:54 +000030
Just470b5052000-01-16 20:37:11 +000031def pointsInRect(array, rect):
jvr91bca422012-10-18 12:49:22 +000032 """Find out which points or array are inside rect.
33 Returns an array with a boolean for each point.
34 """
35 if len(array) < 1:
36 return []
37 xMin, yMin, xMax, yMax = rect
38 return [(xMin <= x <= xMax) and (yMin <= y <= yMax) for x, y in array]
Just470b5052000-01-16 20:37:11 +000039
Justdeb3b632000-01-26 19:32:45 +000040def vectorLength(vector):
jvr91bca422012-10-18 12:49:22 +000041 """Return the length of the given vector."""
42 x, y = vector
43 return math.sqrt(x**2 + y**2)
Justdeb3b632000-01-26 19:32:45 +000044
45def asInt16(array):
jvr91bca422012-10-18 12:49:22 +000046 """Round and cast to 16 bit integer."""
47 return [int(math.floor(i+0.5)) for i in array]
48
Just470b5052000-01-16 20:37:11 +000049
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050050def normRect(rect):
jvr91bca422012-10-18 12:49:22 +000051 """Normalize the rectangle so that the following holds:
52 xMin <= xMax and yMin <= yMax
53 """
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050054 (xMin, yMin, xMax, yMax) = rect
jvr91bca422012-10-18 12:49:22 +000055 return min(xMin, xMax), min(yMin, yMax), max(xMin, xMax), max(yMin, yMax)
Just470b5052000-01-16 20:37:11 +000056
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050057def scaleRect(rect, x, y):
jvr91bca422012-10-18 12:49:22 +000058 """Scale the rectangle by x, y."""
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050059 (xMin, yMin, xMax, yMax) = rect
jvr91bca422012-10-18 12:49:22 +000060 return xMin * x, yMin * y, xMax * x, yMax * y
Just470b5052000-01-16 20:37:11 +000061
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050062def offsetRect(rect, dx, dy):
jvr91bca422012-10-18 12:49:22 +000063 """Offset the rectangle by dx, dy."""
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050064 (xMin, yMin, xMax, yMax) = rect
jvr91bca422012-10-18 12:49:22 +000065 return xMin+dx, yMin+dy, xMax+dx, yMax+dy
Justbe7163c2000-01-18 22:29:39 +000066
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050067def insetRect(rect, dx, dy):
jvr91bca422012-10-18 12:49:22 +000068 """Inset the rectangle by dx, dy on all sides."""
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050069 (xMin, yMin, xMax, yMax) = rect
jvr91bca422012-10-18 12:49:22 +000070 return xMin+dx, yMin+dy, xMax-dx, yMax-dy
Justbe7163c2000-01-18 22:29:39 +000071
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050072def sectRect(rect1, rect2):
jvr91bca422012-10-18 12:49:22 +000073 """Return a boolean and a rectangle. If the input rectangles intersect, return
74 True and the intersecting rectangle. Return False and (0, 0, 0, 0) if the input
75 rectangles don't intersect.
76 """
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050077 (xMin1, yMin1, xMax1, yMax1) = rect1
78 (xMin2, yMin2, xMax2, yMax2) = rect2
jvr91bca422012-10-18 12:49:22 +000079 xMin, yMin, xMax, yMax = (max(xMin1, xMin2), max(yMin1, yMin2),
80 min(xMax1, xMax2), min(yMax1, yMax2))
81 if xMin >= xMax or yMin >= yMax:
82 return 0, (0, 0, 0, 0)
83 return 1, (xMin, yMin, xMax, yMax)
Justbe7163c2000-01-18 22:29:39 +000084
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050085def unionRect(rect1, rect2):
jvr91bca422012-10-18 12:49:22 +000086 """Return the smallest rectangle in which both input rectangles are fully
87 enclosed. In other words, return the total bounding rectangle of both input
88 rectangles.
89 """
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050090 (xMin1, yMin1, xMax1, yMax1) = rect1
91 (xMin2, yMin2, xMax2, yMax2) = rect2
jvr91bca422012-10-18 12:49:22 +000092 xMin, yMin, xMax, yMax = (min(xMin1, xMin2), min(yMin1, yMin2),
93 max(xMax1, xMax2), max(yMax1, yMax2))
94 return (xMin, yMin, xMax, yMax)
Just02a739a2000-01-23 19:10:27 +000095
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050096def rectCenter(rect0):
jvr91bca422012-10-18 12:49:22 +000097 """Return the center of the rectangle as an (x, y) coordinate."""
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -050098 (xMin, yMin, xMax, yMax) = rect0
jvr91bca422012-10-18 12:49:22 +000099 return (xMin+xMax)/2, (yMin+yMax)/2
Just02a739a2000-01-23 19:10:27 +0000100
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500101def intRect(rect1):
jvr91bca422012-10-18 12:49:22 +0000102 """Return the rectangle, rounded off to integer values, but guaranteeing that
103 the resulting rectangle is NOT smaller than the original.
104 """
Behdad Esfahbod3a9fd302013-11-27 03:19:32 -0500105 (xMin, yMin, xMax, yMax) = rect1
jvr91bca422012-10-18 12:49:22 +0000106 import math
107 xMin = int(math.floor(xMin))
108 yMin = int(math.floor(yMin))
109 xMax = int(math.ceil(xMax))
110 yMax = int(math.ceil(yMax))
111 return (xMin, yMin, xMax, yMax)
Justbe7163c2000-01-18 22:29:39 +0000112
jvrfc3879e2003-08-29 19:29:46 +0000113
jvr41144b92008-03-04 14:47:11 +0000114def _test():
jvr91bca422012-10-18 12:49:22 +0000115 """
116 >>> import math
117 >>> calcBounds([])
118 (0, 0, 0, 0)
119 >>> calcBounds([(0, 40), (0, 100), (50, 50), (80, 10)])
120 (0, 10, 80, 100)
121 >>> updateBounds((0, 0, 0, 0), (100, 100))
122 (0, 0, 100, 100)
123 >>> pointInRect((50, 50), (0, 0, 100, 100))
124 True
125 >>> pointInRect((0, 0), (0, 0, 100, 100))
126 True
127 >>> pointInRect((100, 100), (0, 0, 100, 100))
128 True
129 >>> not pointInRect((101, 100), (0, 0, 100, 100))
130 True
131 >>> list(pointsInRect([(50, 50), (0, 0), (100, 100), (101, 100)], (0, 0, 100, 100)))
132 [True, True, True, False]
133 >>> vectorLength((3, 4))
134 5.0
135 >>> vectorLength((1, 1)) == math.sqrt(2)
136 True
137 >>> list(asInt16([0, 0.1, 0.5, 0.9]))
138 [0, 0, 1, 1]
139 >>> normRect((0, 10, 100, 200))
140 (0, 10, 100, 200)
141 >>> normRect((100, 200, 0, 10))
142 (0, 10, 100, 200)
143 >>> scaleRect((10, 20, 50, 150), 1.5, 2)
144 (15.0, 40, 75.0, 300)
145 >>> offsetRect((10, 20, 30, 40), 5, 6)
146 (15, 26, 35, 46)
147 >>> insetRect((10, 20, 50, 60), 5, 10)
148 (15, 30, 45, 50)
149 >>> insetRect((10, 20, 50, 60), -5, -10)
150 (5, 10, 55, 70)
151 >>> intersects, rect = sectRect((0, 10, 20, 30), (0, 40, 20, 50))
152 >>> not intersects
153 True
154 >>> intersects, rect = sectRect((0, 10, 20, 30), (5, 20, 35, 50))
155 >>> intersects
156 1
157 >>> rect
158 (5, 20, 20, 30)
159 >>> unionRect((0, 10, 20, 30), (0, 40, 20, 50))
160 (0, 10, 20, 50)
161 >>> rectCenter((0, 0, 100, 200))
162 (50, 100)
163 >>> rectCenter((0, 0, 100, 199.0))
164 (50, 99.5)
165 >>> intRect((0.9, 2.9, 3.1, 4.1))
166 (0, 2, 4, 5)
167 """
jvr41144b92008-03-04 14:47:11 +0000168
jvrfc3879e2003-08-29 19:29:46 +0000169if __name__ == "__main__":
jvr91bca422012-10-18 12:49:22 +0000170 import doctest
171 doctest.testmod()