Package topo :: Package base :: Module boundingregion
[hide private]
[frames] | no frames]

Source Code for Module topo.base.boundingregion

  1  """ 
  2  Bounding regions and bounding boxes. 
  3   
  4  $Id: boundingregion.py 10672 2009-10-28 01:00:50Z ceball $ 
  5  """ 
  6   
  7  __version__='$Revision: 10672 $' 
  8   
  9  from numpy import inf 
 10   
 11  ### JABALERT: The aarect information should probably be rewritten in 
 12  ### matrix notation, not list notation, so that it can be scaled, 
 13  ### translated, etc. easily. 
 14  ### 
 15  import param 
 16  from param.parameterized import get_all_slots 
 17   
18 -class BoundingRegion(object):
19 """ 20 Abstract bounding region class, for any portion of a 2D plane. 21 22 Only subclasses can be instantiated directly. 23 """ 24 __abstract = True 25 26 __slots__ = ['_aarect'] 27
28 - def contains(self,x,y):
29 raise NotImplementedError
30 - def scale(self,xs,ys):
31 raise NotImplementedError
32 - def translate(self,xoff,yoff):
33 l,b,r,t = self.aarect().lbrt() 34 self._aarect = AARectangle((l+xoff,b+yoff),(r+xoff,t+yoff))
35 - def rotate(self,theta):
36 raise NotImplementedError
37 - def aarect(self):
38 raise NotImplementedError
39 - def centroid(self):
40 """ 41 Return the coordinates of the center of this BoundingBox 42 """ 43 return self.aarect().centroid()
44
45 - def set(self,points):
46 self._aarect=AARectangle(*points)
47 48 # CEBALERT: same as methods on Parameter
49 - def __getstate__(self):
50 # BoundingRegions have slots, not a dict, so we have to 51 # support pickle and deepcopy ourselves. 52 state = {} 53 for slot in get_all_slots(type(self)): 54 state[slot] = getattr(self,slot) 55 return state
56
57 - def __setstate__(self,state):
58 for (k,v) in state.items(): 59 setattr(self,k,v)
60 61
62 -class BoundingBox(BoundingRegion):
63 """ 64 A rectangular bounding box defined either by two points forming 65 an axis-aligned rectangle (or simply a radius for a square). 66 """ 67 __slots__ = [] 68
69 - def __str__(self):
70 """ 71 Return BoundingBox(points=((left,bottom),(right,top))) 72 73 Reimplemented here so that 'print' for a BoundingBox 74 will display the bounds. 75 """ 76 l,b,r,t = self._aarect.lbrt() 77 if r == -l and t == -b and r == t: 78 return 'BoundingBox(radius=%s)'%(r) 79 else: 80 return 'BoundingBox(points=((%s,%s),(%s,%s)))'%(l,b,r,t)
81
82 - def __repr__(self):
83 return self.__str__()
84
85 - def script_repr(self,imports=[],prefix=" "):
86 # Generate import statement 87 cls = self.__class__.__name__ 88 mod = self.__module__ 89 imports.append("from %s import %s" % (mod,cls)) 90 return self.__str__()
91
92 - def __init__(self,**args):
93 """ 94 Create a BoundingBox. 95 96 Either 'radius' or 'points' can be specified for the 97 AARectangle. 98 99 If radius is passed in, the BoundingBox will use min_radius 100 (which defaults to 0.0) if it's larger than radius - so by 101 passing min_radius=1.25/density, a BoundingBox of at least 3x3 102 matrix units can be guaranteed. 103 104 If neither radius nor points is passed in, create a default 105 AARectangle defined by (-0.5,-0.5),(0.5,0.5). 106 """ 107 # if present, 'radius', 'min_radius', and 'points' are deleted from 108 # args before they're passed to the superclass (because they 109 # aren't parameters to be set) 110 if 'radius' in args: 111 radius = args['radius'] 112 del args['radius'] 113 114 if 'min_radius' in args: 115 min_radius = args['min_radius'] 116 del args['min_radius'] 117 else: 118 min_radius = 0.0 119 120 r=max(radius,min_radius) 121 self._aarect=AARectangle((-r,-r),(r,r)) 122 123 elif 'points' in args: 124 self._aarect = AARectangle(*args['points']) 125 del args['points'] 126 else: 127 self._aarect = AARectangle((-0.5,-0.5),(0.5,0.5)) 128 129 super(BoundingBox,self).__init__(**args)
130 131
132 - def contains(self,x,y):
133 """ 134 Returns true if the given point is contained within the 135 bounding box, where all boundaries of the box are 136 considered to be inclusive. 137 """ 138 left,bottom,right,top = self.aarect().lbrt() 139 return (left <= x <= right) and (bottom <= y <= top)
140
141 - def contains_exclusive(self,x,y):
142 """ 143 Return True if the given point is contained within the 144 bounding box, where the bottom and right boundaries are 145 considered exclusive. 146 """ 147 a=self._aarect 148 left,bottom,right,top = a._left,a._bottom,a._right,a._top 149 return (left <= x < right) and (bottom < y <= top)
150 151
152 - def containsbb_exclusive(self,x):
153 """ 154 Returns true if the given BoundingBox x is contained within the 155 bounding box, where at least one of the boundaries of the box has 156 to be exclusive. 157 """ 158 left,bottom,right,top = self.aarect().lbrt() 159 leftx,bottomx,rightx,topx = x.aarect().lbrt() 160 return (left <= leftx) and (bottom <= bottomx) and (right >= rightx) and (top >= topx) and (not ((left == leftx) and (bottom == bottomx) and (right == rightx) and (top == topx)))
161
162 - def containsbb_inclusive(self,x):
163 """ 164 Returns true if the given BoundingBox x is contained within the 165 bounding box, including cases of exact match. 166 """ 167 left,bottom,right,top = self.aarect().lbrt() 168 leftx,bottomx,rightx,topx = x.aarect().lbrt() 169 return (left <= leftx) and (bottom <= bottomx) and (right >= rightx) and (top >= topx)
170
171 - def upperexclusive_contains(self,x,y):
172 """ 173 Returns true if the given point is contained within the 174 bounding box, where the right and upper boundaries 175 are exclusive, and the left and lower boundaries are 176 inclusive. Useful for tiling a plane into non-overlapping 177 regions. 178 """ 179 left,bottom,right,top = self.aarect().lbrt() 180 return (left <= x < right) and (bottom <= y < top)
181
182 - def aarect(self):
183 return self._aarect
184
185 - def lbrt(self):
186 """ 187 return left,bottom,right,top values for the BoundingBox. 188 """ 189 return self._aarect.lbrt()
190 191 192
193 -class Cartesian2DPoint(param.Parameter):
194 """ 195 Parameter whose value represents a point in a 2D Cartesian plane. 196 """ 197 ### JABALERT: Should accept and respect a BoundingBox bounds.
198 - def __set__(self,obj,val):
199 try: ## Test that it is a 2-tuple 200 (x,y) = val 201 super(Cartesian2DPoint,self).__set__(obj,val) 202 except: 203 raise ValueError("Parameter must be a 2D point (an x,y tuple).")
204 205
206 -class BoundingEllipse(BoundingBox):
207 """ 208 Similar to BoundingBox, but the region is the ellipse 209 inscribed within the rectangle. 210 """ 211 __slots__ = [] 212
213 - def contains(self,x,y):
214 left,bottom,right,top = self.aarect().lbrt() 215 xr = (right-left)/2.0 216 yr = (top-bottom)/2.0 217 xc = left + xr 218 yc = bottom + yr 219 220 xd = x-xc 221 yd = y-yc 222 223 return (xd**2/xr**2 + yd**2/yr**2) <= 1
224
225 -class BoundingCircle(BoundingRegion):
226 """ 227 A circular BoundingRegion. 228 229 Takes parameters center (a single 2D point (x,y)) and radius (a 230 scalar radius). 231 """ 232 233 __slots__ = ['radius','center'] 234 235 #radius = param.Number(0.5,bounds=(0.0,None)) 236 #center = Cartesian2DPoint((0.0,0.0)) 237
238 - def __init__(self,center=(0.0,0.0),radius=0.5,**args):
239 self.center=center 240 self.radius=radius 241 super(BoundingCircle,self).__init__(**args)
242
243 - def contains(self,x,y):
244 xc,yc = self.center 245 xd = x-xc 246 yd = y-yc 247 return xd*xd + yd*yd <= self.radius*self.radius
248
249 - def aarect(self):
250 xc,yc = self.center 251 r = self.radius 252 return AARectangle((xc-r,yc-r),(xc+r,yc+r))
253 254
255 -class Unbounded(BoundingRegion):
256 - def contains(self,x,y):
257 return True
258 - def scale(self,xs,ys):
259 pass
260 - def translate(self,xoff,yoff):
261 pass
262 - def rotate(self,theta):
263 pass
264 - def aarect(self):
265 return AARectangle((-inf,-inf),(inf,inf))
266 - def centroid(self):
267 return 0.0,0.0
268 269 270 ### This class is valid only for BoundingBoxes, because it 271 ### only deals with the aarect(), ignoring arbitrarily shaped 272 ### BoundingRegions. To be a real Intersection(BoundingRegion) class, 273 ### it would need to have a contains() function that computes a 274 ### logical AND of the contains() for each of the regions supplied. 275 ### Scale, rotate, and translate would also need to be applied to the 276 ### individual regions each time. 277 ### Could be simpler to write this as a function, because it just 278 ### ends up with a simple BoundingBox after construction.
279 -class BoundingBoxIntersection(BoundingBox):
280 """A BoundingBox initialized as the intersection of the supplied list of BoundingBoxes.""" 281
282 - def __init__(self,*boxes,**params):
283 """ 284 Given a list of BoundingBoxes, computes a new BoundingBox that is 285 the intersection of all of the supplied boxes. 286 """ 287 super(BoundingBoxIntersection,self).__init__(**params) 288 289 bounds = [r.aarect().lbrt() for r in boxes] 290 left = max([l for (l,b,r,t) in bounds]) 291 bottom = max([b for (l,b,r,t) in bounds]) 292 right = min([r for (l,b,r,t) in bounds]) 293 top = min([t for (l,b,r,t) in bounds]) 294 295 # JABALERT: Why is this one __aarect, and BoundingBox 296 # _aarect? Probably should change this one to _aarect and 297 # eliminate aarect(self). 298 self.__aarect = AARectangle((left,bottom),(right,top))
299
300 - def aarect(self):
301 return self.__aarect
302 303 304 # JABALERT: Should probably remove top, bottom, etc. accessor functions, 305 # and use the slot itself instead. 306 ###################################################
307 -class AARectangle(object):
308 """ 309 Axis-aligned rectangle class. 310 311 Defines the smallest axis-aligned rectangle that encloses a set of 312 points. 313 314 Usage: aar = AARectangle( (x1,y1),(x2,y2), ... , (xN,yN) ) 315 """ 316 __slots__ = ['_left','_bottom','_right','_top']
317 - def __init__(self,*points):
318 self._top = max([y for x,y in points]) 319 self._bottom = min([y for x,y in points]) 320 self._left = min([x for x,y in points]) 321 self._right = max([x for x,y in points])
322 323 324 # support for pickling because this class has __slots__ rather 325 # than __dict__
326 - def __getstate__(self):
327 state={} 328 for k in self.__slots__: 329 state[k] = getattr(self,k) 330 return state
331
332 - def __setstate__(self,state):
333 for k,v in state.items(): 334 setattr(self,k,v)
335
336 - def top(self):
337 """ 338 Return the y-coordinate of the top of the rectangle. 339 """ 340 return self._top
341 - def bottom(self):
342 """ 343 Return the y-coordinate of the bottom of the rectangle. 344 """ 345 return self._bottom
346 - def left(self):
347 """ 348 Return the x-coordinate of the left side of the rectangle. 349 """ 350 return self._left
351 - def right(self):
352 """ 353 Return the x-coordinate of the right side of the rectangle. 354 """ 355 return self._right
356 - def lbrt(self):
357 """ 358 Return (left,bottom,right,top) as a tuple 359 """ 360 return (self._left, 361 self._bottom, 362 self._right, 363 self._top)
364 365
366 - def centroid(self):
367 """ 368 Return the centroid of the rectangle. 369 """ 370 left,bottom,right,top = self.lbrt() 371 return (right+left)/2.0,(top+bottom)/2.0
372 373
374 - def intersect(self,other):
375 376 l1,b1,r1,t1 = self.lbrt() 377 l2,b2,r2,t2 = other.lbrt() 378 379 l = max(l1,l2) 380 b = max(b1,b2) 381 r = min(r1,r2) 382 t = min(t1,t2) 383 384 return AARectangle(points=((l,b),(r,t)))
385
386 - def width(self):
387 return self._right - self._left
388 - def height(self):
389 return self._top - self._bottom
390
391 - def empty(self):
392 l,b,r,t = self.lbrt() 393 return (r <= l) or (t <= b)
394 395 396 ### JABALERT: Should classes like this inherit from something like 397 ### ClassInstanceParameter, which takes a class name and verifies that 398 ### the value is in that class? 399 ### 400 ### Do we also need a BoundingBoxParameter?
401 -class BoundingRegionParameter(param.Parameter):
402 """ 403 Parameter whose value can be any BoundingRegion instance, enclosing a region in a 2D plane. 404 """
405 - def __init__(self,default=BoundingBox(radius=0.5),**params):
407
408 - def __set__(self,obj,val):
409 if not isinstance(val,BoundingRegion): 410 raise ValueError("Parameter must be a BoundingRegion.") 411 else: 412 super(BoundingRegionParameter,self).__set__(obj,val)
413