root/trunk/RBRapier/RBRapier/Tools/Projections.py

Revision 732, 11.4 kB (checked in by sholloway, 5 years ago)

*** empty log message ***

Line 
1 #!/usr/bin/env python
2 ##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3 ##~ License
4 ##~
5 ##- The RuneBlade Foundation library is intended to ease some
6 ##- aspects of writing intricate Jabber, XML, and User Interface (wxPython, etc.)
7 ##- applications, while providing the flexibility to modularly change the
8 ##- architecture. Enjoy.
9 ##~
10 ##~ Copyright (C) 2002  TechGame Networks, LLC.
11 ##~
12 ##~ This library is free software; you can redistribute it and/or
13 ##~ modify it under the terms of the BSD style License as found in the
14 ##~ LICENSE file included with this distribution.
15 ##~
16 ##~ TechGame Networks, LLC can be reached at:
17 ##~ 3578 E. Hartsel Drive #211
18 ##~ Colorado Springs, Colorado, USA, 80920
19 ##~
20 ##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
21
22 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
23 #~ Imports
24 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
25
26 import math
27 import Numeric
28 from Transformations import TransformPrimitive3dh
29
30 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
31 #~ Definitions
32 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
33
34 class ProjectionTransform(TransformPrimitive3dh):
35     """
36     >>> proj = ProjectionTransform(-2, 3, 5,8, -9, -2)
37     >>> proj.Width
38     5.0
39     >>> proj.Height
40     3.0
41     >>> proj.Deprojh
42     7.0
43     >>> proj.Center
44     (0.5, 6.5, -5.5)
45     >>> proj.Dimensions
46     (5.0, 3.0, 7.0)
47     >>> proj.HCenter
48     0.5
49     >>> proj.VCenter
50     6.5
51     >>> proj.DCenter
52     -5.5
53     """
54     def __init__(self, left=-1.0, right=1.0, bottom=-1.0, top=1.0, near=-1.0, far=1.0):
55         self.Left = float(left)
56         self.Right = float(right)
57         self.Bottom = float(bottom)
58         self.Top = float(top)
59         self.Near = float(near)
60         self.Far = float(far)
61
62     def SetCorners(self, Lower, Higher):
63         self.Left, self.Bottom, self.Near = Lower
64         self.Right, self.Top, self.Far = Higher
65
66     def GetWidth(self):
67         return self.Right - self.Left
68     def SetWidth(self, value):
69         delta = (self.Width - value) * 0.5
70         self.Right -= delta
71         self.Left += delta
72     Width = property(GetWidth, SetWidth)
73     def GetHCenter(self, mult=0.5):
74         return (self.Left + self.Right) * mult
75     def SetHCenter(self, value):
76         hw = self.Width * 0.5
77         self.Right = value + hw
78         self.Left = value - hw
79     HCenter = property(GetHCenter, SetHCenter)
80
81     def GetHeight(self):
82         return self.Top - self.Bottom
83     def SetHeight(self, value):
84         delta = (self.Height - value) * 0.5
85         self.Top -= delta
86         self.Bottom += delta
87     Height = property(GetHeight, SetHeight)
88     def GetVCenter(self, mult=0.5):
89         return (self.Top + self.Bottom) * mult
90     def SetVCenter(self, value):
91         hw = self.Height * 0.5
92         self.Top = value + hw
93         self.Bottom = value - hw
94     VCenter = property(GetVCenter, SetVCenter)
95
96     def GetDeprojh(self):
97         return self.Far - self.Near
98     def SetDeprojh(self, value):
99         delta = (self.Deprojh - value) * 0.5
100         self.Far -= delta
101         self.Near += delta
102     Deprojh = property(GetDeprojh, SetDeprojh)
103     def GetDCenter(self, mult=0.5):
104         return (self.Far + self.Near) * mult
105     def SetDCenter(self, value):
106         hw = self.Deprojh * 0.5
107         self.Far = value + hw
108         self.Near = value - hw
109     DCenter = property(GetDCenter, SetDCenter)
110
111     def GetCenter(self):
112         return (self.HCenter, self.VCenter, self.DCenter)
113     def SetCenter(self, value):
114         self.HCenter, self.VCenter, self.DCenter = value
115     Center = property(GetCenter, SetCenter)
116     def GetDimensions(self):
117         return (self.Width, self.Height, self.Deprojh)
118     def SetDimensions(self, value):
119         self.Width, self.Height, self.Deprojh = value
120     Dimensions = property(GetDimensions, SetDimensions)
121
122     def GetAspectRatio(self):
123         return self.Height / self.Width
124     def SetAspectRatio(self, value, byWidth=None):
125         self.SetDimensionsAspectRatio(self.Width, self.Height, value)
126     AspectRatio = property(GetAspectRatio, SetAspectRatio)
127
128     def SetDimensionsAspectRatio(self, width, height, aspectYX=1., largest=True):
129         width, height, aspectYX = map(float, (width, height, aspectYX))
130         if largest: # scale by larger dimension
131             if height/width > aspectYX: # height is greater -- scale width
132                 width = height/aspectYX
133             else: # width is greater -- scale height
134                 height = width*aspectYX
135         else: # scale by smaller dimension
136             if height/width < aspectYX: # width is greater -- scale height
137                 width = height/aspectYX
138             else: # height is greater -- scale width
139                 height = width*aspectYX
140         self.Width, self.Height = width, height
141         return width, height
142
143     def GetTuple(self):
144         return self.Left, self.Right, self.Bottom, self.Top, self.Near, self.Far
145     def SetTuple(self, value):
146         self.Left, self.Right, self.Bottom, self.Top, self.Near, self.Far = value
147     asTuple = property(GetTuple, SetTuple)
148
149 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
150
151 class Orthographic(ProjectionTransform):
152     """
153     >>> proj = Orthographic(-2, 3, 5,8, -9, -2); proj
154     <Orthographic: l=-2.0, r=3.0, b=5.0, t=8.0, n=-9.0, f=-2.0 >
155     >>> proj.asArray4x4()
156     array([[ 0.40000001,  0.        ,  0.        ,  0.2       ],
157            [ 0.        ,  0.66666669,  0.        ,  4.33333349],
158            [ 0.        ,  0.        , -0.2857143 , -1.57142854],
159            [ 0.        ,  0.        ,  0.        ,  1.        ]],'f')
160     """
161
162     def asArray4x4(self):
163         """As defined by the OpenGL Red Bock"""
164         width = self.Width
165         height = self.Height
166         deprojh = self.Deprojh
167         xoff = self.GetHCenter(1.) / width
168         yoff = self.GetVCenter(1.) / height
169         zoff = self.GetDCenter(1.) / deprojh
170
171         result = Numeric.asarray([
172             [2./width, 0., 0., xoff],
173             [0., 2./height, 0., yoff],
174             [0., 0., -2./deprojh, zoff],
175             [0., 0., 0., 1.]],
176             self.NumericType)
177         return result
178
179     def __repr__(self):
180         return "<Orthographic: l=%s, r=%s, b=%s, t=%s, n=%s, f=%s >" % self.asTuple
181
182 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
183
184 class Viewport(Orthographic):
185     """
186     >>> proj = Viewport(0, 10, 0, 10, -1, 1); proj
187     <Viewport: l=0.0, r=10.0, b=0.0, t=10.0, n=-1.0, f=1.0 >
188     >>> proj.asArray4x4()
189     array([[ 5.,  0.,  0.,  5.],
190            [ 0.,  5.,  0.,  5.],
191            [ 0.,  0.,  1.,  0.],
192            [ 0.,  0.,  0.,  1.]])
193     >>> proj.asInverse4x4()
194     array([[ 0.2,  0. ,  0. , -1. ],
195            [ 0. ,  0.2,  0. , -1. ],
196            [ 0. ,  0. ,  1. ,  0. ],
197            [ 0. ,  0. ,  0. ,  1. ]])
198     >>> # Create "screen" coordinates"
199     >>> v = Numeric.transpose(Numeric.asarray([(4.,4.,0.,1.),(6.,6.,0.,1.)])); v
200     array([[ 4.,  6.],
201            [ 4.,  6.],
202            [ 0.,  0.],
203            [ 1.,  1.]])
204     >>> # Convert to v to [-1,1] nominal coordinates
205     >>> vn = Numeric.dot(proj.asInverse4x4(), v); vn
206     array([[-0.2,  0.2],
207            [-0.2,  0.2],
208            [ 0. ,  0. ],
209            [ 1. ,  1. ]])
210     >>> # Convert back to "screen" coordinates
211     >>> Numeric.dot(proj.asArray4x4(), vn)
212     array([[ 4.,  6.],
213            [ 4.,  6.],
214            [ 0.,  0.],
215            [ 1.,  1.]])
216     """
217
218     def __init__(self, left=0., right=1., bottom=0., top=1., near=-1., far=1.):
219         self.Left = float(left)
220         self.Right = float(right)
221         self.Bottom = float(bottom)
222         self.Top = float(top)
223         self.Near = float(near)
224         self.Far = float(far)
225
226     def asArray4x4(self):
227         """As defined by the OpenGL Red Bock"""
228         halfwidth = 0.5 * self.Width
229         halfheight = 0.5 * self.Height
230         halfdeprojh = 0.5 * self.Deprojh
231         xoff = self.GetHCenter()
232         yoff = self.GetVCenter()
233         zoff = self.GetDCenter()
234
235         result = Numeric.asarray([
236             [halfwidth, 0, 0, xoff],
237             [0, halfheight, 0, yoff],
238             [0, 0, halfdeprojh, zoff],
239             [0, 0, 0, 1]])
240         return result
241
242     def __repr__(self):
243         return "<Viewport: l=%s, r=%s, b=%s, t=%s, n=%s, f=%s >" % self.asTuple
244
245 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
246
247 class Frustum(ProjectionTransform):
248     """
249     >>> proj = Frustum(-2, 3, 5,8, -9, -2); proj
250     <Frustm: l=-2.0, r=3.0, b=5.0, t=8.0, n=-9.0, f=-2.0 >
251     >>> proj.asArray4x4()
252     array([[-3.5999999 ,  0.        ,  0.2       ,  0.        ],
253            [ 0.        , -6.        ,  4.33333349,  0.        ],
254            [ 0.        ,  0.        ,  1.57142854, -2.57142854],
255            [ 0.        ,  0.        , -1.        ,  0.        ]],'f')
256     """
257
258     def __init__(self, left=-1.0, right=1.0, bottom=-1.0, top=1.0, near=1., far=10.):
259         ProjectionTransform.__init__(self, left, right, bottom, top, near, far)
260
261     def asArray4x4(self):
262         """As defined by the OpenGL Red Bock"""
263         width = self.Width
264         height = self.Height
265         deprojh = self.Deprojh
266         xoff = self.GetHCenter(1.) / width
267         yoff = self.GetVCenter(1.) / height
268         zoff = self.GetDCenter(1.) / deprojh
269         near2 = self.Near * 2.
270         ugly = -self.Near * self.Far / deprojh
271
272         result = Numeric.asarray([
273             [near2/width, 0., xoff, 0.],
274             [0., near2/height, yoff, 0.],
275             [0., 0., -zoff, ugly],
276             [0., 0., -1., 0.]],
277             self.NumericType)
278         return result
279
280     def __repr__(self):
281         return "<Frustm: l=%s, r=%s, b=%s, t=%s, n=%s, f=%s >" % self.asTuple
282
283 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
284
285 class Perspective(Frustum):
286     """
287     >>> proj = Perspective(30., 1.234, 0.5, 23.2)
288     >>> proj
289     <Persepctive: ViewAngle=30.0, Aspect=1.234, n=0.5, f=23.2 >
290     >>> proj.asArray4x4()
291     array([[ 4.60535049,  0.        ,  0.        ,  0.        ],
292            [ 0.        ,  3.7320509 ,  0.        ,  0.        ],
293            [ 0.        ,  0.        , -1.04405284, -0.51101321],
294            [ 0.        ,  0.        , -1.        ,  0.        ]],'f')
295     >>> proj.AspectRatio
296     1.2339999999999995
297     >>> proj.ViewAngle
298     30.000000000000011
299     >>> proj.ViewRadians
300     0.52359877559829904
301     """
302
303     def __init__(self, viewangle=45., aspectratio=1., near=1., far=10.):
304         """Derived from OpenGL Red Book"""
305         Frustum.__init__(self,-1.,1.,-1.,1.,near,far)
306         self.SetViewAngleAndRatio(math.radians(viewangle), float(aspectratio))
307
308     def __repr__(self):
309         return "<Persepctive: ViewAngle=%s, Aspect=%s, n=%s, f=%s >" % (self.ViewAngle, self.AspectRatio, self.Near, self.Far)
310
311     def GetViewAngle(self):
312         return math.degrees(2.*math.atan(self.Height/(2.*self.Near)))
313     def SetViewAngle(self, value):
314         self.SetViewRadians(math.radians(value))
315     ViewAngle = property(GetViewAngle, SetViewAngle)
316
317     def GetViewRadians(self):
318         return 2.*math.atan(self.Height/(2.*self.Near))
319     def SetViewRadians(self, value):
320         ar = self.AspectRatio
321         self.SetViewAngleAndRatio(float(value), ar)
322     ViewRadians = property(GetViewRadians, SetViewRadians)
323
324     def SetViewAngleAndRatio(self, viewradians, aspectratio):
325         self.Height = math.tan(0.5*viewradians) * 2. * self.Near
326         self.Width = self.Height / aspectratio
327
328 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
329 #~ Testing
330 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
331
332 if __name__=='__main__':
333     print "Testing..."
334     import doctest, Projections
335     doctest.testmod(Projections)
336     print "Test complete."
337
Note: See TracBrowser for help on using the browser.