~brian-sidebotham/wxwidgets-cmake/wxpython-2.9.4

« back to all changes in this revision

Viewing changes to wxPython/wx/lib/floatcanvas/Utilities/BBox.py

  • Committer: Brian Sidebotham
  • Date: 2013-08-03 14:30:08 UTC
  • Revision ID: brian.sidebotham@gmail.com-20130803143008-c7806tkych1tp6fc
Initial import into Bazaar

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
 
 
3
"""
 
4
A Bounding Box object and assorted utilities , subclassed from a numpy array
 
5
 
 
6
"""
 
7
 
 
8
import numpy as N
 
9
 
 
10
class BBox(N.ndarray):
 
11
    """
 
12
    A Bounding Box object:
 
13
    
 
14
    Takes Data as an array. Data is any python sequence that can be turned into a 
 
15
    2x2 numpy array of floats:
 
16
 
 
17
    [[MinX, MinY ],
 
18
     [MaxX, MaxY ]]
 
19
 
 
20
    It is a subclass of numpy.ndarray, so for the most part it can be used as 
 
21
    an array, and arrays that fit the above description can be used in its place.
 
22
    
 
23
    Usually created by the factory functions:
 
24
    
 
25
        asBBox
 
26
        
 
27
        and 
 
28
        
 
29
        fromPoints
 
30
    
 
31
    """
 
32
    def __new__(subtype, data):
 
33
        """
 
34
        Takes Data as an array. Data is any python sequence that can be turned into a 
 
35
        2x2 numpy array of floats:
 
36
 
 
37
        [[MinX, MinY ],
 
38
        [MaxX, MaxY ]]
 
39
 
 
40
        You don't usually call this directly. BBox objects are created with the factory functions:
 
41
        
 
42
        asBBox
 
43
        
 
44
        and 
 
45
        
 
46
        fromPoints
 
47
 
 
48
        """
 
49
        arr = N.array(data, N.float)
 
50
        arr.shape = (2,2)
 
51
        if arr[0,0] > arr[1,0] or arr[0,1] > arr[1,1]:
 
52
            # note: zero sized BB OK.
 
53
            raise ValueError("BBox values not aligned: \n minimum values must be less that maximum values")
 
54
        return N.ndarray.__new__(subtype, shape=arr.shape, dtype=arr.dtype, buffer=arr)
 
55
 
 
56
    def Overlaps(self, BB):
 
57
        """
 
58
        Overlap(BB):
 
59
 
 
60
        Tests if the given Bounding Box overlaps with this one.
 
61
        Returns True is the Bounding boxes overlap, False otherwise
 
62
        If they are just touching, returns True
 
63
        """
 
64
 
 
65
        if N.isinf(self).all() or N.isinf(BB).all():
 
66
            return True
 
67
        if ( (self[1,0] >= BB[0,0]) and (self[0,0] <= BB[1,0]) and
 
68
             (self[1,1] >= BB[0,1]) and (self[0,1] <= BB[1,1]) ):
 
69
            return True
 
70
        else:
 
71
            return False
 
72
 
 
73
    def Inside(self, BB):
 
74
        """
 
75
        Inside(BB):
 
76
 
 
77
        Tests if the given Bounding Box is entirely inside this one.
 
78
 
 
79
        Returns True if it is entirely inside, or touching the
 
80
        border.
 
81
 
 
82
        Returns False otherwise
 
83
        """
 
84
        if ( (BB[0,0] >= self[0,0]) and (BB[1,0] <= self[1,0]) and
 
85
             (BB[0,1] >= self[0,1]) and (BB[1,1] <= self[1,1]) ):
 
86
            return True
 
87
        else:
 
88
            return False
 
89
    
 
90
    def PointInside(self, Point):
 
91
        """
 
92
        Inside(BB):
 
93
 
 
94
        Tests if the given Point is entirely inside this one.
 
95
 
 
96
        Returns True if it is entirely inside, or touching the
 
97
        border.
 
98
 
 
99
        Returns False otherwise
 
100
        
 
101
        Point is any length-2 sequence (tuple, list, array) or two numbers
 
102
        """
 
103
        if Point[0] >= self[0,0] and \
 
104
               Point[0] <= self[1,0] and \
 
105
               Point[1] <= self[1,1] and \
 
106
               Point[1] >= self[0,1]:
 
107
            return True
 
108
        else:
 
109
            return False
 
110
    
 
111
    def Merge(self, BB):
 
112
        """
 
113
        Joins this bounding box with the one passed in, maybe making this one bigger
 
114
 
 
115
        """ 
 
116
        if self.IsNull():
 
117
            self[:] = BB
 
118
        elif N.isnan(BB).all(): ## BB may be a regular array, so I can't use IsNull
 
119
            pass
 
120
        else:
 
121
            if BB[0,0] < self[0,0]: self[0,0] = BB[0,0]
 
122
            if BB[0,1] < self[0,1]: self[0,1] = BB[0,1]
 
123
            if BB[1,0] > self[1,0]: self[1,0] = BB[1,0]
 
124
            if BB[1,1] > self[1,1]: self[1,1] = BB[1,1]
 
125
        
 
126
        return None
 
127
    
 
128
    def IsNull(self):
 
129
        return N.isnan(self).all()
 
130
 
 
131
    ## fixme: it would be nice to add setter, too.
 
132
    def _getLeft(self):
 
133
        return self[0,0]
 
134
    Left = property(_getLeft)
 
135
    def _getRight(self):
 
136
        return self[1,0]
 
137
    Right = property(_getRight)
 
138
    def _getBottom(self):
 
139
        return self[0,1]
 
140
    Bottom = property(_getBottom)
 
141
    def _getTop(self):
 
142
        return self[1,1]
 
143
    Top = property(_getTop)
 
144
 
 
145
    def _getWidth(self):
 
146
        return self[1,0] - self[0,0]
 
147
    Width = property(_getWidth)
 
148
 
 
149
    def _getHeight(self):
 
150
        return self[1,1] - self[0,1]
 
151
    Height = property(_getHeight)
 
152
    
 
153
    def _getCenter(self):
 
154
        return self.sum(0) / 2.0
 
155
    Center = property(_getCenter)
 
156
    ### This could be used for a make BB from a bunch of BBs
 
157
 
 
158
    #~ def _getboundingbox(bboxarray): # lrk: added this
 
159
        #~ # returns the bounding box of a bunch of bounding boxes
 
160
        #~ upperleft = N.minimum.reduce(bboxarray[:,0])
 
161
        #~ lowerright = N.maximum.reduce(bboxarray[:,1])
 
162
        #~ return N.array((upperleft, lowerright), N.float)
 
163
    #~ _getboundingbox = staticmethod(_getboundingbox)
 
164
 
 
165
 
 
166
    ## Save the ndarray __eq__ for internal use.
 
167
    Array__eq__ = N.ndarray.__eq__
 
168
    def __eq__(self, BB):
 
169
        """
 
170
        __eq__(BB) The equality operator
 
171
 
 
172
        A == B if and only if all the entries are the same
 
173
 
 
174
        """
 
175
        if self.IsNull() and N.isnan(BB).all(): ## BB may be a regular array, so I can't use IsNull
 
176
            return True
 
177
        else:
 
178
            return self.Array__eq__(BB).all()
 
179
        
 
180
   
 
181
def asBBox(data):
 
182
    """
 
183
    returns a BBox object.
 
184
 
 
185
    If object is a BBox, it is returned unaltered
 
186
 
 
187
    If object is a numpy array, a BBox object is returned that shares a
 
188
    view of the data with that array. The numpy array should be of the correct
 
189
    format: a 2x2 numpy array of floats:
 
190
 
 
191
    [[MinX, MinY ],
 
192
     [MaxX, MaxY ]]
 
193
    
 
194
    """
 
195
 
 
196
    if isinstance(data, BBox):
 
197
        return data
 
198
    arr = N.asarray(data, N.float)
 
199
    return N.ndarray.__new__(BBox, shape=arr.shape, dtype=arr.dtype, buffer=arr)
 
200
 
 
201
def fromPoints(Points):
 
202
    """
 
203
    fromPoints (Points).
 
204
 
 
205
    reruns the bounding box of the set of points in Points. Points can
 
206
    be any python object that can be turned into a numpy NX2 array of Floats.
 
207
 
 
208
    If a single point is passed in, a zero-size Bounding Box is returned.
 
209
    
 
210
    """
 
211
    Points = N.asarray(Points, N.float).reshape(-1,2)
 
212
 
 
213
    arr = N.vstack( (Points.min(0), Points.max(0)) )
 
214
    return N.ndarray.__new__(BBox, shape=arr.shape, dtype=arr.dtype, buffer=arr)
 
215
 
 
216
def fromBBArray(BBarray):
 
217
   """
 
218
   Builds a BBox object from an array of Bounding Boxes. 
 
219
   The resulting Bounding Box encompases all the included BBs.
 
220
   
 
221
   The BBarray is in the shape: (Nx2x2) where BBarray[n] is a 2x2 array that represents a BBox
 
222
   """
 
223
   
 
224
   #upperleft = N.minimum.reduce(BBarray[:,0])
 
225
   #lowerright = N.maximum.reduce(BBarray[:,1])
 
226
 
 
227
#   BBarray = N.asarray(BBarray, N.float).reshape(-1,2)
 
228
#   arr = N.vstack( (BBarray.min(0), BBarray.max(0)) )
 
229
   BBarray = N.asarray(BBarray, N.float).reshape(-1,2,2)
 
230
   arr = N.vstack( (BBarray[:,0,:].min(0), BBarray[:,1,:].max(0)) )
 
231
   return asBBox(arr)
 
232
   #return asBBox( (upperleft, lowerright) ) * 2
 
233
   
 
234
def NullBBox():
 
235
    """
 
236
    Returns a BBox object with all NaN entries.
 
237
    
 
238
    This represents a Null BB box;
 
239
    
 
240
    BB merged with it will return BB.
 
241
    
 
242
    Nothing is inside it.
 
243
 
 
244
    """
 
245
 
 
246
    arr = N.array(((N.nan, N.nan),(N.nan, N.nan)), N.float)
 
247
    return N.ndarray.__new__(BBox, shape=arr.shape, dtype=arr.dtype, buffer=arr)
 
248
 
 
249
def InfBBox():
 
250
    """
 
251
    Returns a BBox object with all -inf and inf entries
 
252
 
 
253
    """
 
254
 
 
255
    arr = N.array(((-N.inf, -N.inf),(N.inf, N.inf)), N.float)
 
256
    return N.ndarray.__new__(BBox, shape=arr.shape, dtype=arr.dtype, buffer=arr)
 
257
 
 
258
class RectBBox(BBox):
 
259
    """
 
260
    subclass of a BBox that can be used for a rotated Rectangle
 
261
    
 
262
    contributed by MArco Oster (marco.oster@bioquant.uni-heidelberg.de)
 
263
 
 
264
    """
 
265
    
 
266
    def __new__(self, data, edges=None):
 
267
        return BBox.__new__(self, data)
 
268
 
 
269
    def __init__(self, data, edges=None):
 
270
        ''' assume edgepoints are ordered such you can walk along all edges with left rotation sense
 
271
            This may be:
 
272
            left-top
 
273
            left-bottom
 
274
            right-bottom
 
275
            right-top
 
276
 
 
277
            or any rotation.
 
278
        '''
 
279
        BBox.BBox(data)
 
280
        self.edges = np.asarray(edges)
 
281
 
 
282
        print "new rectbbox created"
 
283
 
 
284
 
 
285
    def ac_leftOf_ab(self, a, b, c):
 
286
        ab = np.array(b) - np.array(a)
 
287
        ac = np.array(c) - np.array(a)
 
288
 
 
289
        return (ac[0]*ab[1] - ac[1]*ab[0]) <= 0
 
290
 
 
291
    def PointInside(self, point):
 
292
        print "point inside called"
 
293
 
 
294
        for edge in xrange(4):
 
295
            if self.ac_leftOf_ab(self.edges[edge],
 
296
                                 self.edges[(edge+1)%4],
 
297
                                 point):
 
298
                continue
 
299
            else:
 
300
                return False
 
301
        return True 
 
302
    
 
303
   
 
 
b'\\ No newline at end of file'