blob: 3f39e7e4be4ee01054ad9d92ed10b8e41adc80fa [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
jvr182a7ba2003-06-29 18:18:54 +000019def updateBounds(bounds, (x, y), min=min, max=max):
jvr91bca422012-10-18 12:49:22 +000020 """Return the bounding recangle of rectangle bounds and point (x, y)."""
21 xMin, yMin, xMax, yMax = bounds
22 return min(xMin, x), min(yMin, y), max(xMax, x), max(yMax, y)
jvr182a7ba2003-06-29 18:18:54 +000023
24def pointInRect((x, y), rect):
jvr91bca422012-10-18 12:49:22 +000025 """Return True when point (x, y) is inside rect."""
26 xMin, yMin, xMax, yMax = rect
27 return (xMin <= x <= xMax) and (yMin <= y <= yMax)
jvr182a7ba2003-06-29 18:18:54 +000028
Just470b5052000-01-16 20:37:11 +000029def pointsInRect(array, rect):
jvr91bca422012-10-18 12:49:22 +000030 """Find out which points or array are inside rect.
31 Returns an array with a boolean for each point.
32 """
33 if len(array) < 1:
34 return []
35 xMin, yMin, xMax, yMax = rect
36 return [(xMin <= x <= xMax) and (yMin <= y <= yMax) for x, y in array]
Just470b5052000-01-16 20:37:11 +000037
Justdeb3b632000-01-26 19:32:45 +000038def vectorLength(vector):
jvr91bca422012-10-18 12:49:22 +000039 """Return the length of the given vector."""
40 x, y = vector
41 return math.sqrt(x**2 + y**2)
Justdeb3b632000-01-26 19:32:45 +000042
43def asInt16(array):
jvr91bca422012-10-18 12:49:22 +000044 """Round and cast to 16 bit integer."""
45 return [int(math.floor(i+0.5)) for i in array]
46
Just470b5052000-01-16 20:37:11 +000047
jvrfc3879e2003-08-29 19:29:46 +000048def normRect((xMin, yMin, xMax, yMax)):
jvr91bca422012-10-18 12:49:22 +000049 """Normalize the rectangle so that the following holds:
50 xMin <= xMax and yMin <= yMax
51 """
52 return min(xMin, xMax), min(yMin, yMax), max(xMin, xMax), max(yMin, yMax)
Just470b5052000-01-16 20:37:11 +000053
jvrfc3879e2003-08-29 19:29:46 +000054def scaleRect((xMin, yMin, xMax, yMax), x, y):
jvr91bca422012-10-18 12:49:22 +000055 """Scale the rectangle by x, y."""
56 return xMin * x, yMin * y, xMax * x, yMax * y
Just470b5052000-01-16 20:37:11 +000057
jvrfc3879e2003-08-29 19:29:46 +000058def offsetRect((xMin, yMin, xMax, yMax), dx, dy):
jvr91bca422012-10-18 12:49:22 +000059 """Offset the rectangle by dx, dy."""
60 return xMin+dx, yMin+dy, xMax+dx, yMax+dy
Justbe7163c2000-01-18 22:29:39 +000061
jvrfc3879e2003-08-29 19:29:46 +000062def insetRect((xMin, yMin, xMax, yMax), dx, dy):
jvr91bca422012-10-18 12:49:22 +000063 """Inset the rectangle by dx, dy on all sides."""
64 return xMin+dx, yMin+dy, xMax-dx, yMax-dy
Justbe7163c2000-01-18 22:29:39 +000065
jvrfc3879e2003-08-29 19:29:46 +000066def sectRect((xMin1, yMin1, xMax1, yMax1), (xMin2, yMin2, xMax2, yMax2)):
jvr91bca422012-10-18 12:49:22 +000067 """Return a boolean and a rectangle. If the input rectangles intersect, return
68 True and the intersecting rectangle. Return False and (0, 0, 0, 0) if the input
69 rectangles don't intersect.
70 """
71 xMin, yMin, xMax, yMax = (max(xMin1, xMin2), max(yMin1, yMin2),
72 min(xMax1, xMax2), min(yMax1, yMax2))
73 if xMin >= xMax or yMin >= yMax:
74 return 0, (0, 0, 0, 0)
75 return 1, (xMin, yMin, xMax, yMax)
Justbe7163c2000-01-18 22:29:39 +000076
jvrfc3879e2003-08-29 19:29:46 +000077def unionRect((xMin1, yMin1, xMax1, yMax1), (xMin2, yMin2, xMax2, yMax2)):
jvr91bca422012-10-18 12:49:22 +000078 """Return the smallest rectangle in which both input rectangles are fully
79 enclosed. In other words, return the total bounding rectangle of both input
80 rectangles.
81 """
82 xMin, yMin, xMax, yMax = (min(xMin1, xMin2), min(yMin1, yMin2),
83 max(xMax1, xMax2), max(yMax1, yMax2))
84 return (xMin, yMin, xMax, yMax)
Just02a739a2000-01-23 19:10:27 +000085
jvrfc3879e2003-08-29 19:29:46 +000086def rectCenter((xMin, yMin, xMax, yMax)):
jvr91bca422012-10-18 12:49:22 +000087 """Return the center of the rectangle as an (x, y) coordinate."""
88 return (xMin+xMax)/2, (yMin+yMax)/2
Just02a739a2000-01-23 19:10:27 +000089
jvrfc3879e2003-08-29 19:29:46 +000090def intRect((xMin, yMin, xMax, yMax)):
jvr91bca422012-10-18 12:49:22 +000091 """Return the rectangle, rounded off to integer values, but guaranteeing that
92 the resulting rectangle is NOT smaller than the original.
93 """
94 import math
95 xMin = int(math.floor(xMin))
96 yMin = int(math.floor(yMin))
97 xMax = int(math.ceil(xMax))
98 yMax = int(math.ceil(yMax))
99 return (xMin, yMin, xMax, yMax)
Justbe7163c2000-01-18 22:29:39 +0000100
jvrfc3879e2003-08-29 19:29:46 +0000101
jvr41144b92008-03-04 14:47:11 +0000102def _test():
jvr91bca422012-10-18 12:49:22 +0000103 """
104 >>> import math
105 >>> calcBounds([])
106 (0, 0, 0, 0)
107 >>> calcBounds([(0, 40), (0, 100), (50, 50), (80, 10)])
108 (0, 10, 80, 100)
109 >>> updateBounds((0, 0, 0, 0), (100, 100))
110 (0, 0, 100, 100)
111 >>> pointInRect((50, 50), (0, 0, 100, 100))
112 True
113 >>> pointInRect((0, 0), (0, 0, 100, 100))
114 True
115 >>> pointInRect((100, 100), (0, 0, 100, 100))
116 True
117 >>> not pointInRect((101, 100), (0, 0, 100, 100))
118 True
119 >>> list(pointsInRect([(50, 50), (0, 0), (100, 100), (101, 100)], (0, 0, 100, 100)))
120 [True, True, True, False]
121 >>> vectorLength((3, 4))
122 5.0
123 >>> vectorLength((1, 1)) == math.sqrt(2)
124 True
125 >>> list(asInt16([0, 0.1, 0.5, 0.9]))
126 [0, 0, 1, 1]
127 >>> normRect((0, 10, 100, 200))
128 (0, 10, 100, 200)
129 >>> normRect((100, 200, 0, 10))
130 (0, 10, 100, 200)
131 >>> scaleRect((10, 20, 50, 150), 1.5, 2)
132 (15.0, 40, 75.0, 300)
133 >>> offsetRect((10, 20, 30, 40), 5, 6)
134 (15, 26, 35, 46)
135 >>> insetRect((10, 20, 50, 60), 5, 10)
136 (15, 30, 45, 50)
137 >>> insetRect((10, 20, 50, 60), -5, -10)
138 (5, 10, 55, 70)
139 >>> intersects, rect = sectRect((0, 10, 20, 30), (0, 40, 20, 50))
140 >>> not intersects
141 True
142 >>> intersects, rect = sectRect((0, 10, 20, 30), (5, 20, 35, 50))
143 >>> intersects
144 1
145 >>> rect
146 (5, 20, 20, 30)
147 >>> unionRect((0, 10, 20, 30), (0, 40, 20, 50))
148 (0, 10, 20, 50)
149 >>> rectCenter((0, 0, 100, 200))
150 (50, 100)
151 >>> rectCenter((0, 0, 100, 199.0))
152 (50, 99.5)
153 >>> intRect((0.9, 2.9, 3.1, 4.1))
154 (0, 2, 4, 5)
155 """
jvr41144b92008-03-04 14:47:11 +0000156
jvrfc3879e2003-08-29 19:29:46 +0000157if __name__ == "__main__":
jvr91bca422012-10-18 12:49:22 +0000158 import doctest
159 doctest.testmod()