root/trunk/RBRapier/RBRapier/Tools/ViewBox.py

Revision 704, 14.7 kB (checked in by sholloway, 5 years ago)

*** empty log message ***

Line 
1 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2 #~ Imports
3 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
5 import Numeric
6 from OpenGL import GL
7 from Geometry import Curves
8 from Vector import LinearMapping
9 from Transformations import TransformPrimitive3dh
10 from Transformations2d import TransformPrimitive2dh
11
12 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
13 #~ Definitions
14 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
15
16 class ViewBox(object):
17     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
18     #~ Constants / Variables / Etc.
19     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20
21     NumericType = Numeric.Float
22
23     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
24     #~ Public Methods
25     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
26
27     def __init__(self, pos0=(-1., -1.), pos1=(1., 1.)):
28         self.SetPts(pos0, pos1)
29
30     def __repr__(self):
31         (x0, y0), (x1, y1) = self.GetBox()
32         return '<%s [%r, %r]>' % (self.__class__.__name__, (x0, y0), (x1, y1))
33
34     def copy(self):
35         return self.fromViewBox(self)
36
37     def blend(self, factor, viewbox):
38         return self.fromBox((1-factor)*self.GetBox()+factor*viewbox.GetBox())
39
40     def UpdateProjection(self, proj):
41         _box = self.GetBox()
42         proj.Left, proj.Bottom = _box[0]
43         proj.Right, proj.Top = _box[1]
44
45     def fromViewBox(klass, viewbox):
46         result = ViewBox.__new__(ViewBox) # avoid constructor call
47         result.SetBox(viewbox.GetBox())
48         return result
49     fromViewBox = classmethod(fromViewBox)
50     def fromBox(klass, box):
51         result = ViewBox.__new__(ViewBox) # avoid constructor call
52         result.SetBox(box)
53         return result
54     fromBox = classmethod(fromBox)
55     def fromPts(klass, pos0, pos1):
56         return klass(pos0, pos1)
57     fromPts = classmethod(fromPts)
58     def fromRectangle(klass, pos0, dim):
59         pos0, dim = klass._ascoords(pos0, dim)
60         return klass(pos0, pos0+dim)
61     fromRectangle = classmethod(fromRectangle)
62     def fromCenter(klass, center, dim):
63         center, dim = klass._ascoords(center, dim)
64         dim = 0.5*dim
65         return klass(center-dim, center+dim)
66     fromCenter = classmethod(fromCenter)
67
68     def __iadd__(self, value):
69         if isinstance(value, ViewBox):
70             return self.SetBox(self.GetBox()+value.GetBox())
71         else:
72             return self.SetBox(self.GetBox()+value)
73     def __add__(self, value):
74         if isinstance(value, ViewBox):
75             return self.fromBox(self.GetBox()+value.GetBox())
76         else:
77             return self.fromBox(self.GetBox()+value)
78     def __radd__(self, value):
79         if isinstance(value, ViewBox):
80             return self.fromBox(value.GetBox()+self.GetBox())
81         else:
82             return self.fromBox(value+self.GetBox())
83
84     def __isub__(self, value):
85         if isinstance(value, ViewBox):
86             return self.SetBox(self.GetBox()-value.GetBox())
87         else:
88             return self.SetBox(self.GetBox()-value)
89     def __sub__(self, value):
90         if isinstance(value, ViewBox):
91             return self.fromBox(self.GetBox()-value.GetBox())
92         else:
93             return self.fromBox(self.GetBox()-value)
94     def __rsub__(self, value):
95         if isinstance(value, ViewBox):
96             return self.fromBox(value.GetBox()-self.GetBox())
97         else:
98             return self.fromBox(value-self.GetBox())
99
100     def __imul__(self, value):
101         return self.SetBox(self.GetBox()*value)
102     def __mul__(self, value):
103         return self.fromBox(self.GetBox()*value)
104     def __rmul__(self, value):
105         return self.fromBox(value*self.GetBox())
106
107     def __idiv__(self, value):
108         return self.SetBox(self.GetBox()/value)
109     def __div__(self, value):
110         return self.fromBox(self.GetBox()/value)
111
112     def GetP0(self):
113         return self.GetBox()[0]
114     def GetP1(self):
115         return self.GetBox()[1]
116     def GetBox(self):
117         return self._box
118     def GetRect(self):
119         _box = self.GetBox()
120         return _box[0], _box[1]-_box[0]
121     def GetRectangle(self):
122         _box = self.GetBox()
123         (x, y), (w, h) = _box[0], _box[1]-_box[0]
124         return x, y, w, h
125     def GetPts(self):
126         return self.GetBox()
127     def GetCenter(self):
128         _box = self.GetBox()
129         return 0.5*(_box[0]+_box[1])
130     def GetSize(self):
131         _box = self.GetBox()
132         return _box[1]-_box[0]
133     def GetAspectRatio(self):
134         size = self.GetSize()
135         return size[1]/size[0]
136     def GetWidth(self):
137         return self.GetSize()[0]
138     def GetHeight(self):
139         return self.GetSize()[1]
140     def GetXSpan(self):
141         box = self.GetBox()
142         return (box[0][0], box[1][0])
143     def GetYSpan(self):
144         box = self.GetBox()
145         return (box[0][1], box[1][1])
146     def GetXYSpan(self):
147         return Numeric.transpose(self.GetBox())
148
149     def SetP0(self, p0):
150         return self.SetBox((p0, self.GetP1()))
151     def SetP1(self, p1):
152         return self.SetBox((self.GetP0(), p1))
153     def SetBox(self, box):
154         self._box = Numeric.asarray(box, self.NumericType)
155         return self._box
156     def SetRect(self, pos, dim):
157         pos, dim = self._ascoords(pos, dim)
158         return self.SetPts(pos, pos+dim)
159     def SetRectangle(self, rect):
160         pos, dim = self._ascoords(rect[:2], rect[2:4])
161         return self.SetPts(pos, pos+dim)
162     def SetPts(self, pos0, pos1):
163         pos0, pos1 = self._ascoords(pos0, pos1)
164         pos0, pos1 = Numeric.minimum(pos0, pos1), Numeric.maximum(pos0, pos1)
165         return self.SetBox((pos0, pos1))
166     def SetCenterAndSize(self, center, dim):
167         center, dim = self._ascoords(center, dim)
168         dim = 0.5*dim
169         return self.SetBox((center-dim, center+dim))
170     def SetSize(self, dim):
171         dim, = self._ascoords(dim)
172         _box = self.GetBox()
173         return self.SetBox((_box[0], _box[0] + dim))
174     def SetWidth(self, width):
175         return self.SetSize((width, self.GetHeight()))
176     def SetHeight(self, height):
177         return self.SetSize((self.GetWidth(), height))
178        
179     def SetAspectRatio(self, aspectYX=1.0, largest=True):
180         return self.ViewBoxSize(self.GetSize(), aspectYX, largest)
181
182     def ZoomCenter(self, factor, center=(0., 0.), relative=True):
183         center, = self._ascoords(center)
184         halfsize = 0.5*self.GetSize()/factor
185         if relative: center += self.GetCenter()
186         return self.SetPts(center-halfsize, center+halfsize)
187
188     def Move(self, pos, relative=True):
189         _box = self.GetBox()
190         pos, = self._ascoords(pos)
191         if not relative:
192             pos = pos-_box[0] # make it relative
193         return self.SetPts(_box[0]+pos, _box[1]+pos)
194
195     def MoveHorizontal(self, pos, relative=True):
196         if relative:
197             return self.Move((pos, 0.), True)
198         else:
199             return self.Move((pos, self.GetBox()[0][1]), False)
200
201     def MoveVertical(self, pos, relative=True):
202         if relative:
203             return self.Move((0., pos), True)
204         else:
205             return self.Move((self.GetBox()[0][0], pos), False)
206
207     def ViewBoxSize(self, dim, aspectYX=None, largest=True):
208         size = self._GetViewBoxSize(dim, aspectYX, largest)
209         return self.SetCenterAndSize(self.GetCenter(), size)
210
211     def ViewBox(self, pos, dim, aspectYX=None, largest=True):
212         size = self._GetViewBoxSize(dim, aspectYX, largest)
213         return self.SetCenterAndSize(pos, size)
214
215     def ViewBoxPts(self, pos0, pos1, aspectYX=None, largest=True):
216         pos0, pos1 = self._ascoords(pos0, pos1)
217         pos0, pos1 = Numeric.minimum(pos0, pos1), Numeric.maximum(pos0, pos1)
218         size = self._GetViewBoxSize(abs(pos1-pos0), aspectYX, largest)
219         return self.SetCenterAndSize(0.5*(pos0+pos1), size)
220
221     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
222
223     def MapPointFrom0to1(self, posIn0to1):
224         blend, = self._ascoords(posIn0to1)
225         _box = self.GetBox()
226         result = (1-blend)*_box[0]+blend*_box[1]
227         return result
228
229     def MapPointTo0to1(self, pos):
230         pos, = self._ascoords(pos)
231         _box = self.GetBox()
232         return (pos-_box[0])/(_box[1]-_box[0])
233
234     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
235
236     def MapPointTo(self, point, *args, **kw):
237         return self.MapPointsTo([point], *args, **kw)[0]
238
239     def MapPointsTo(self, points, xspan=(-1., 1.), yspan=None, flipx=False, flipy=False):
240         if yspan is None and hasattr(xspan, 'GetXYSpan'):
241             xspan, yspan = xspan.GetXYSpan()
242         xspan, yspan = xspan or yspan, yspan or xspan
243         if flipx: xspan = xspan[1], xspan[0]
244         if flipy: yspan = yspan[1], yspan[0]
245         span = Numeric.transpose(Numeric.asarray([xspan, yspan], self.NumericType))
246         linearfn = LinearMapping(self.GetBox(), span)
247         points = Numeric.asarray(points, self.NumericType)
248         return Numeric.asarray(map(linearfn, points), self.NumericType)
249
250     def MapPointFrom(self, point, *args, **kw):
251         return self.MapPointsFrom([point], *args, **kw)[0]
252
253     def MapPointsFrom(self, points, xspan=(-1., 1.), yspan=None, flipx=False, flipy=False):
254         if yspan is None and hasattr(xspan, 'GetXYSpan'):
255             xspan, yspan = xspan.GetXYSpan()
256         xspan, yspan = xspan or yspan, yspan or xspan
257         if flipx: xspan = xspan[1], xspan[0]
258         if flipy: yspan = yspan[1], yspan[0]
259         span = Numeric.transpose(Numeric.asarray([xspan, yspan], self.NumericType))
260         linearfn = LinearMapping(span, self.GetBox())
261         points = Numeric.asarray(points, self.NumericType)
262         return Numeric.asarray(map(linearfn, points), self.NumericType)
263
264     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
265
266     def Draw(self, style=GL.GL_LINE_LOOP):
267         (x0, y0), (x1, y1) = self.GetBox()
268         GL.glBegin(style)
269         GL.glVertex2f(x0,y0)
270         GL.glVertex2f(x0,y1)
271         GL.glVertex2f(x1,y1)
272         GL.glVertex2f(x1,y0)
273         GL.glEnd()
274
275     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
276     #~ Protected Methods
277     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
278
279     def _ascoords(klass, *args):
280         return Numeric.asarray(args, klass.NumericType)
281     _ascoords = classmethod(_ascoords)
282
283     def _GetViewBoxSize(self, dim, aspectYX=None, largest=True):
284         if aspectYX is None: aspectYX = self.GetAspectRatio()
285
286         width, height = map(float, dim)
287         if largest: # scale by larger dimension
288             if height/width > aspectYX: # height is greater -- scale height
289                 width = height/aspectYX
290             else: # width is greater -- scale width
291                 height = width*aspectYX
292         else:
293             if height/width < aspectYX: # width is greater -- scale height
294                 width = height/aspectYX
295             else: # height is greater -- scale width
296                 height = width*aspectYX
297         return width, height
298
299 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
300
301 class BlendedViewBox(ViewBox):
302     def __init__(self, viewbox0, viewbox1, blend=0.):
303         self.SetupBlend(viewbox0, viewbox1, blend)
304
305     def SetupBlend(self, viewbox0, viewbox1, blend=0.):
306         self.blend = blend
307         self.viewbox0 = viewbox0
308         self.viewbox1 = viewbox1
309
310     def GetBlend(self):
311         return self.blend
312
313     def SetBlend(self, blend):
314         self.blend = blend
315
316     def GetBox(self):
317         b = self.blend
318         if b == 0.:
319             return self.viewbox0.GetBox()
320         elif b == 1.:
321             return self.viewbox1.GetBox()
322         else:
323             return (1-b)*self.viewbox0.GetBox()+b*self.viewbox1.GetBox()
324
325     def SetBox(self, box):
326         raise NotImplementedError
327
328     def Reduce(self):
329         b = self.blend
330         if b == 0.: return self.viewbox0
331         elif b == 1.: return self.viewbox1
332         else: return self
333
334 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
335 #~ ViewBoxMappingMatrix
336 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
337
338 class ViewBoxMappingBase(object):
339     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
340     #~ Constants / Variables / Etc.
341     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
342
343     _fromrect = ViewBox()
344     _torect = ViewBox()
345
346     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
347     #~ Public Methods
348     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
349
350     def __init__(self, fromrect=None, torect=None):
351         if isinstance(fromrect, (list, tuple)):
352             self._fromrect = ViewBox(fromrect)
353         elif fromrect is not None:
354             self._fromrect = fromrect
355         if isinstance(torect, (list, tuple)):
356             self._torect = ViewBox(torect)
357         elif torect is not None:
358             self._torect = torect
359
360     def __repr__(self):
361         return "<%s from:%r to:%r>" % (self.__class__.__name__, self.MapFrom, self.MapTo)
362
363     def GetMapTo(self):
364         return self._torect
365     def SetMapTo(self, torect):
366         self._torect = torect
367     def DelMapTo(self):
368         del self._torect
369     MapTo = property(GetMapTo, SetMapTo, DelMapTo)
370
371     def GetMapFrom(self):
372         return self._fromrect
373     def SetMapFrom(self, fromrect):
374         self._fromrect = fromrect
375     def DelMapFrom(self):
376         del self._fromrect
377     MapFrom = property(GetMapFrom, SetMapFrom, DelMapFrom)
378
379     def _GetMapping(self, frombox, tobox):
380         (x0f, y0f), (x1f, y1f) = frombox
381         (x0t, y0t), (x1t, y1t) = tobox
382         sx, tx = LinearMapping((x0f, x1f), (x0t, x1t), False)
383         sy, ty = LinearMapping((y0f, y1f), (y0t, y1t), False)
384         return (sx, tx), (sy,ty)
385
386 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
387
388 class ViewBoxMappingMatrix3dh(ViewBoxMappingBase, TransformPrimitive3dh):
389     def asArray4x4(self):
390         (sx, tx), (sy, ty) = self._GetMapping(self.GetMapFrom().GetBox(), self.GetMapTo().GetBox())
391         result = Numeric.asarray([
392             [sx,  0,  0, tx],
393             [ 0, sy,  0, ty],
394             [ 0,  0,  1,  0],
395             [ 0,  0,  0,  1]], self.NumericType)
396         return result
397
398     def asInverse4x4_(self):
399         (sx, tx), (sy, ty) = self._GetMapping(self.GetMapTo().GetBox(), self.GetMapFrom().GetBox())
400         result = Numeric.asarray([
401             [sx,  0,  0, tx],
402             [ 0, sy,  0, ty],
403             [ 0,  0,  1,  0],
404             [ 0,  0,  0,  1]], self.NumericType)
405         return result
406
407 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
408
409 class ViewBoxMappingMatrix2dh(ViewBoxMappingBase, TransformPrimitive2dh):
410     def asArray4x4(self):
411         (sx, tx), (sy, ty) = self._GetMapping(self.GetMapFrom().GetBox(), self.GetMapTo().GetBox())
412         result = Numeric.asarray([
413             [sx,  0, tx],
414             [ 0, sy, ty],
415             [ 0,  0,  1]], self.NumericType)
416         return result
417
418     def asInverse4x4_(self):
419         (sx, tx), (sy, ty) = self._GetMapping(self.GetMapTo().GetBox(), self.GetMapFrom().GetBox())
420         result = Numeric.asarray([
421             [sx,  0, tx],
422             [ 0, sy, ty],
423             [ 0,  0,  1]], self.NumericType)
424         return result
425
Note: See TracBrowser for help on using the browser.