blob: 8bec0fd773707e972f7258b46daed8cfa7c7e171 [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
jvr41144b92008-03-04 14:47:11 +00006import Numeric
Just470b5052000-01-16 20:37:11 +00007
8def calcBounds(array):
jvrfc3879e2003-08-29 19:29:46 +00009 """Return the bounding rectangle of a 2D points array as a tuple:
10 (xMin, yMin, xMax, yMax)
Just470b5052000-01-16 20:37:11 +000011 """
12 if len(array) == 0:
13 return 0, 0, 0, 0
jvrfc3879e2003-08-29 19:29:46 +000014 xMin, yMin = Numeric.minimum.reduce(array)
15 xMax, yMax = Numeric.maximum.reduce(array)
16 return xMin, yMin, xMax, yMax
Just470b5052000-01-16 20:37:11 +000017
jvr182a7ba2003-06-29 18:18:54 +000018def updateBounds(bounds, (x, y), min=min, max=max):
19 """Return the bounding recangle of rectangle bounds and point (x, y)."""
20 xMin, yMin, xMax, yMax = bounds
21 return min(xMin, x), min(yMin, y), max(xMax, x), max(yMax, y)
22
23def pointInRect((x, y), rect):
24 """Return True when point (x, y) is inside rect."""
25 xMin, yMin, xMax, yMax = rect
26 return (xMin <= x <= xMax) and (yMin <= y <= yMax)
27
Just470b5052000-01-16 20:37:11 +000028def pointsInRect(array, rect):
29 """Find out which points or array are inside rect.
30 Returns an array with a boolean for each point.
31 """
32 if len(array) < 1:
33 return []
34 lefttop = rect[:2]
35 rightbottom = rect[2:]
36 condition = Numeric.logical_and(
jvrfc3879e2003-08-29 19:29:46 +000037 Numeric.greater_equal(array, lefttop),
38 Numeric.less_equal(array, rightbottom))
Just470b5052000-01-16 20:37:11 +000039 return Numeric.logical_and.reduce(condition, -1)
40
Justdeb3b632000-01-26 19:32:45 +000041def vectorLength(vector):
jvrfc3879e2003-08-29 19:29:46 +000042 """Return the length of the given vector."""
Justdeb3b632000-01-26 19:32:45 +000043 return Numeric.sqrt(vector[0]**2 + vector[1]**2)
44
45def asInt16(array):
jvrfc3879e2003-08-29 19:29:46 +000046 """Round and cast to 16 bit integer."""
Justdeb3b632000-01-26 19:32:45 +000047 return Numeric.floor(array + 0.5).astype(Numeric.Int16)
48
Just470b5052000-01-16 20:37:11 +000049
jvrfc3879e2003-08-29 19:29:46 +000050def normRect((xMin, yMin, xMax, yMax)):
51 """Normalize the rectangle so that the following holds:
52 xMin <= xMax and yMin <= yMax
53 """
54 return min(xMin, xMax), min(yMin, yMax), max(xMin, xMax), max(yMin, yMax)
Just470b5052000-01-16 20:37:11 +000055
jvrfc3879e2003-08-29 19:29:46 +000056def scaleRect((xMin, yMin, xMax, yMax), x, y):
57 """Scale the rectangle by x, y."""
58 return xMin * x, yMin * y, xMax * x, yMax * y
Just470b5052000-01-16 20:37:11 +000059
jvrfc3879e2003-08-29 19:29:46 +000060def offsetRect((xMin, yMin, xMax, yMax), dx, dy):
61 """Offset the rectangle by dx, dy."""
62 return xMin+dx, yMin+dy, xMax+dx, yMax+dy
Justbe7163c2000-01-18 22:29:39 +000063
jvrfc3879e2003-08-29 19:29:46 +000064def insetRect((xMin, yMin, xMax, yMax), dx, dy):
65 """Inset the rectangle by dx, dy on all sides."""
66 return xMin+dx, yMin+dy, xMax-dx, yMax-dy
Justbe7163c2000-01-18 22:29:39 +000067
jvrfc3879e2003-08-29 19:29:46 +000068def sectRect((xMin1, yMin1, xMax1, yMax1), (xMin2, yMin2, xMax2, yMax2)):
69 """Return a boolean and a rectangle. If the input rectangles intersect, return
70 True and the intersecting rectangle. Return False and (0, 0, 0, 0) if the input
71 rectangles don't intersect.
72 """
73 xMin, yMin, xMax, yMax = (max(xMin1, xMin2), max(yMin1, yMin2),
74 min(xMax1, xMax2), min(yMax1, yMax2))
75 if xMin >= xMax or yMin >= yMax:
Justbe7163c2000-01-18 22:29:39 +000076 return 0, (0, 0, 0, 0)
jvrfc3879e2003-08-29 19:29:46 +000077 return 1, (xMin, yMin, xMax, yMax)
Justbe7163c2000-01-18 22:29:39 +000078
jvrfc3879e2003-08-29 19:29:46 +000079def unionRect((xMin1, yMin1, xMax1, yMax1), (xMin2, yMin2, xMax2, yMax2)):
80 """Return the smallest rectangle in which both input rectangles are fully
81 enclosed. In other words, return the total bounding rectangle of both input
82 rectangles.
83 """
84 xMin, yMin, xMax, yMax = (min(xMin1, xMin2), min(yMin1, yMin2),
85 max(xMax1, xMax2), max(yMax1, yMax2))
86 return (xMin, yMin, xMax, yMax)
Just02a739a2000-01-23 19:10:27 +000087
jvrfc3879e2003-08-29 19:29:46 +000088def rectCenter((xMin, yMin, xMax, yMax)):
89 """Return the center of the rectangle as an (x, y) coordinate."""
90 return (xMin+xMax)/2, (yMin+yMax)/2
Just02a739a2000-01-23 19:10:27 +000091
jvrfc3879e2003-08-29 19:29:46 +000092def intRect((xMin, yMin, xMax, yMax)):
93 """Return the rectangle, rounded off to integer values, but guaranteeing that
94 the resulting rectangle is NOT smaller than the original.
95 """
96 import math
97 xMin = int(math.floor(xMin))
98 yMin = int(math.floor(yMin))
99 xMax = int(math.ceil(xMax))
100 yMax = int(math.ceil(yMax))
101 return (xMin, yMin, xMax, yMax)
Justbe7163c2000-01-18 22:29:39 +0000102
jvrfc3879e2003-08-29 19:29:46 +0000103
jvr41144b92008-03-04 14:47:11 +0000104def _test():
105 """
106 >>> import math
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 [1, 1, 1, 0]
121 >>> vectorLength((3, 4))
122 5.0
123 >>> vectorLength((1, 1)) == math.sqrt(2)
124 True
125 >>> list(asInt16(Numeric.array([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 """
156
jvrfc3879e2003-08-29 19:29:46 +0000157if __name__ == "__main__":
jvr41144b92008-03-04 14:47:11 +0000158 import doctest
159 doctest.testmod()