root/trunk/RBRapier/RBRapier/Tools/Transformations.py

Revision 732, 26.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 import LinearAlgebra
29 from Vector import *
30
31 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
32 #~ Definitions
33 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
34
35 class TransformPrimitive3dh(object):
36     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
37     #~ Constants / Variables / Etc.
38     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
39
40     NumericType = Numeric.Float
41
42     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
43     #~ Public Methods
44     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
45
46     def asArray4x4(self):
47         """Returns the transformation in 4x4 Numeric array form.
48         """
49         return Numeric.identity(4)
50
51     def asInverse4x4(self):
52         """Returns the inverse transformation in 4x4 Numeric array form.
53         """
54         return LinearAlgebra.inverse(self.asArray4x4())
55
56     def Collapse(self):
57         return Matrix(self.asArray3x3())
58
59     def __mul__(self, other):
60         if isinstance(other, TransformPrimitive3dh):
61             return Composite([self, other])
62         elif isinstance(other, Numeric.ArrayType):
63             return Numeric.dot(self.asArray4x4(), other)
64         else: return self.asArray4x4() * other
65
66     def __rmul__(self, other):
67         if isinstance(other, TransformPrimitive3dh):
68             return Composite([other, self])
69         elif isinstance(other, Numeric.ArrayType):
70             return Numeric.dot(other, self.asArray4x4())
71         else: return other * self.asArray4x4()
72
73     def asPoints(self, points, includeDimRestore=False):
74         dims = len(points[0])
75         if dims not in (2,3,4):
76             raise ValueError, "Points are not of right dimension -- must be 2, 3 or 4, but found %d" % dims
77         if dims == 2:
78             ones = Numeric.ones((len(points), 1), self.NumericType)
79             zeros = Numeric.zeros((len(points), 1), self.NumericType)
80             points = Numeric.concatenate((points, zeros, ones), 1)
81             correctdims = lambda pts: pts[:,:-2] # trim the z and homogenous coordinates -- assumes that they are all still 1.
82         elif dims == 3:
83             # add the homogeneous coordinate
84             ones = Numeric.ones((len(points), 1), self.NumericType)
85             points = Numeric.concatenate((points, ones), 1)
86             correctdims = lambda pts: pts[:,:-1] # trim the homogenous coordinate -- assumes that they are all still 1.
87         else:
88             correctdims = lambda pts: pts
89
90         if includeDimRestore:
91             return points, correctdims
92         else: return points
93
94     def TransformPoints(self, points, bInverse=False):
95         if bInverse:
96             matrix = self.asInverse4x4()
97         else:
98             matrix = self.asArray4x4()
99        
100         points, correctdims = self.asPoints(points, True)
101         result = Numeric.transpose(Numeric.dot(matrix, Numeric.transpose(points)))
102         return correctdims(result)
103
104 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
105
106 class Composite(TransformPrimitive3dh):
107     """
108     # Non-sensical test -- detects changes more than anything
109     >>> from Projections import *
110     >>> c = Composite()
111     >>> n = c.Add(Identity())
112     >>> n = c.Add(Translate((1,2,3)))
113     >>> n = c.Add(Scale((2,3,4)))
114     >>> n = c.Add(Rotate(23, (7,11,13)))
115     >>> c
116     <Composite: [<Identity>, <Translation: [1.0, 2.0, 3.0, 1.0]>, <Scale: [2.0, 3.0, 4.0, 1.0]>, <Rotation: Angle=23.0, Axis=[0.38018780946731567, 0.59743797779083252, 0.70606309175491333, 1.0]>] >
117     >>> c.Inverse()
118     <Composite: [<Rotation: Angle=23.0, Axis=[-0.38018780946731567, -0.59743797779083252, -0.70606309175491333, 1.0]>, <Scale: [0.5, 0.3333333432674408, 0.25, 1.0]>, <Translation: [-1.0, -2.0, -3.0, 1.0]>, <Identity>] >
119     >>> (c * c.Inverse()).asArray4x4()
120     array([[ 9.99999999e-001,  1.24926286e-010,  1.19565441e-010,  1.52539981e-010],
121            [ 2.81084066e-010,  1.00000003e+000, -1.15097085e-009, -5.50221717e-008],
122            [ 4.78261764e-010, -2.04617046e-009,  9.99999998e-001,  8.76708395e-009],
123            [ 0.00000000e+000,  0.00000000e+000,  0.00000000e+000,  1.00000000e+000]])
124     >>> n = c.Add(Skew(2,3,4,5,6,7))
125     >>> n = c.Add(Shear(10,20,30,40,50,60))
126     >>> n = c.Add(Orthographic())
127     >>> n = c.Add(Frustum())
128     >>> n = c.Add(Perspective())
129     >>> c.asArray4x4()[0][0]
130     44.29636824137652
131     >>> c.asInverse4x4()[0][0]
132     -0.010173620849422499
133     """
134
135     def __init__(self, collection=[]):
136         self.collection = collection[:]
137
138     def __repr__(self):
139         return "<Composite: %s >" % self.collection
140
141     def __contains__(self, *args, **kw):
142         return self.collection.__contains__(self, *args, **kw)
143     def __iter__(self):
144         return iter(self.collection)
145     def __len__(self):
146         return len(self.collection)
147     def __getitem__(self, *args, **kw):
148         return self.collection.__getitem__(*args, **kw)
149     def __setitem__(self, *args, **kw):
150         return self.collection.__setitem__(*args, **kw)
151     def __delitem__(self, *args, **kw):
152         return self.collection.__delitem__(*args, **kw)
153
154     def __imul__(self, other):
155         if isinstance(other, TransformPrimitive3dh):
156             if isinstance(other, Composite):
157                 self.collection.extend(other.collection)
158             else:
159                 self.collection.append(other)
160             return self
161         else:
162             raise TypeError, "Can only inline multiply with other TransformPrimitive3dh"
163
164     def __mul__(self, other):
165         if isinstance(other, TransformPrimitive3dh):
166             if isinstance(other, Composite):
167                 return Composite(self.collection + other.collection)
168             else:
169                 return Composite(self.collection + [other])
170         else: TransformPrimitive3dh.__mul__(self, other)
171
172     def __rmul__(self, other):
173         if isinstance(other, TransformPrimitive3dh):
174             if isinstance(other, Composite):
175                 return Composite(other.collection + self.collection)
176             else:
177                 return Composite([other] + self.collection)
178         else: TransformPrimitive3dh.__rmul__(self, other)
179
180     def Add(self, Transform):
181         assert isinstance(Transform, TransformPrimitive3dh)
182         self.collection.append(Transform)
183         return self.collection[-1]
184     Append=Add
185
186     def Insert(self, idx, Transform):
187         self.collection.insert(idx, Transform)
188         return self.collection[idx]
189
190     def Clear(self):
191         self.collection[:] = []
192    
193     def asArray4x4(self):
194         """Returns the transformation in 4x4 Numeric array form"""
195         r = Numeric.identity(4, self.NumericType)
196         for xform in self.collection:
197             r = Numeric.dot(r, xform.asArray4x4())
198         return r
199
200     def Inverse(self):
201         result = self.__class__()
202         result.collection = [x.Inverse() for x in self.collection]
203         result.collection.reverse()
204         return result
205
206 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
207
208 class Matrix(TransformPrimitive3dh):
209     """
210     >>> a = Numeric.arange(1, 17)
211     >>> a.shape = 4,4
212     >>> m = Matrix(a)
213     >>> m
214     <Matrix: [[1.0, 2.0, 3.0, 4.0], [5.0, 6.0, 7.0, 8.0], [9.0, 10.0, 11.0, 12.0], [13.0, 14.0, 15.0, 16.0]] >
215     >>> m.asArray4x4()
216     array([[  1.,   2.,   3.,   4.],
217            [  5.,   6.,   7.,   8.],
218            [  9.,  10.,  11.,  12.],
219            [ 13.,  14.,  15.,  16.]])
220     >>> m.asInverse4x4()
221     array([[ 3.94064967e+015, -4.50359963e+015, -2.81474977e+015,  3.37769972e+015],
222            [-4.12829966e+015,  4.50359963e+015,  3.37769972e+015, -3.75299969e+015],
223            [-3.56534971e+015,  4.50359963e+015,  1.68884986e+015, -2.62709978e+015],
224            [ 3.75299969e+015, -4.50359963e+015, -2.25179981e+015,  3.00239975e+015]])
225     """
226
227     def __init__(self, matrix=None):
228         if matrix: self.matrix = Numeric.asarray(matrix, self.NumericType)
229         else: self.matrix = Numeric.identity(4, self.NumericType)
230         assert self.matrix.shape == (4,4)
231
232     def __repr__(self):
233         return "<Matrix: %s >" % self.matrix.tolist()
234
235     def __imul__(self, other):
236         if isinstance(other, TransformPrimitive3dh):
237             self.matrix = Numeric.dot(self.matrix, other.asArray4x4())
238             assert self.matrix.shape == (4,4)
239             return self
240         else:
241             raise TypeError, "Can only inline multiply with other TransformPrimitive3dh"
242
243     def asArray4x4(self):
244         return self.matrix
245
246     def Inverse(self):
247         return self.__class__(self.asInverse4x4())
248
249 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
250
251 class Identity(TransformPrimitive3dh):
252     """
253     >>> i = Identity()
254     >>> i
255     <Identity>
256     >>> i.asArray4x4()
257     array([[ 1.,  0.,  0.,  0.],
258            [ 0.,  1.,  0.,  0.],
259            [ 0.,  0.,  1.,  0.],
260            [ 0.,  0.,  0.,  1.]])
261     >>> i.asInverse4x4()
262     array([[ 1.,  0.,  0.,  0.],
263            [ 0.,  1.,  0.,  0.],
264            [ 0.,  0.,  1.,  0.],
265            [ 0.,  0.,  0.,  1.]])
266
267     >>> i.TransformPoints([(2., 3., 4., 1.)])
268     array([       [ 2.,  3.,  4.,  1.]])
269     >>> i.TransformPoints([(2., 3., 4., 1.)], True)
270     array([       [ 2.,  3.,  4.,  1.]])
271
272     >>> i.TransformPoints([(2., 3., 4.)])
273     array([       [ 2.,  3.,  4.]])
274     >>> i.TransformPoints([(2., 3., 4.)], True)
275     array([       [ 2.,  3.,  4.]])
276
277     >>> i.TransformPoints([(2., 3.)])
278     array([       [ 2.,  3.]])
279     >>> i.TransformPoints([(2., 3.)], True)
280     array([       [ 2.,  3.]])
281     """
282
283     def __init__(self):
284         pass
285
286     def __repr__(self):
287         return "<Identity>"
288
289     def asArray4x4(self):
290         return Numeric.identity(4, self.NumericType)
291
292     def asInverse4x4(self):
293         return Numeric.identity(4, self.NumericType)
294
295     def Inverse(self):
296         return self.__class__()
297
298 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
299
300 class Translate(TransformPrimitive3dh):
301     """
302     >>> t = Translate([-1,-2,-3])
303     >>> t
304     <Translation: [-1.0, -2.0, -3.0, 1.0]>
305     >>> t.asArray4x4()
306     array([[ 1.,  0.,  0., -1.],
307            [ 0.,  1.,  0., -2.],
308            [ 0.,  0.,  1., -3.],
309            [ 0.,  0.,  0.,  1.]])
310     >>> t.asInverse4x4()
311     array([[ 1.,  0.,  0.,  1.],
312            [ 0.,  1.,  0.,  2.],
313            [ 0.,  0.,  1.,  3.],
314            [ 0.,  0.,  0.,  1.]])
315     """
316
317     Direction = Vector3HProperty('Direction', NumericType=TransformPrimitive3dh.NumericType)
318
319     def __init__(self, Direction=(0,0,0)):
320         self.Direction = Direction
321
322     def __repr__(self):
323         return "<Translation: %s>" % (self.Direction.tolist(),)
324
325     def asArray4x4(self):
326         result = Numeric.identity(4, self.NumericType)
327         result[:, 3] = self.Direction
328         return result
329
330     def asInverse4x4(self):
331         result = Numeric.identity(4, self.NumericType)
332         result[:, 3] = -self.Direction
333         return result
334
335     def Inverse(self):
336         return self.__class__(-self.Direction)
337
338 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
339
340 class Scale(TransformPrimitive3dh):
341     """
342     >>> s = Scale([2,3,4])
343     >>> s
344     <Scale: [2.0, 3.0, 4.0, 1.0]>
345     >>> s.asArray4x4()
346     array([[ 2.,  0.,  0.,  0.],
347            [ 0.,  3.,  0.,  0.],
348            [ 0.,  0.,  4.,  0.],
349            [ 0.,  0.,  0.,  1.]])
350     >>> s.asInverse4x4()
351     array([[ 0.5       ,  0.        ,  0.        ,  0.        ],
352            [ 0.        ,  0.33333333,  0.        ,  0.        ],
353            [ 0.        ,  0.        ,  0.25      ,  0.        ],
354            [ 0.        ,  0.        ,  0.        ,  1.        ]])
355     """
356
357     Scale = Vector3HProperty('Scale', (1.,1.,1.,1.), NumericType=TransformPrimitive3dh.NumericType)
358
359     def __init__(self, Scale=1.0):
360         if not isinstance(Scale, (tuple,list)):
361             # uniform scaling
362             self.Scale = (Scale,) * 3
363         else:
364             # make sure it is length 3
365             self.Scale = Scale
366
367     def __repr__(self):
368         return "<Scale: %s>" % (self.Scale.tolist(),)
369
370     def asArray4x4(self):
371         result = Numeric.identity(4, self.NumericType)
372         for idx in range(4-1): result[idx,idx] = self.Scale[idx]
373         return result
374
375     def asInverse4x4(self):
376         result = Numeric.identity(4, self.NumericType)
377         for idx in range(4-1): result[idx,idx] = 1./self.Scale[idx]
378         return result
379
380     def Inverse(self):
381         return self.__class__([1./x for x in self.Scale])
382
383 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
384
385 class Rotate(TransformPrimitive3dh):
386     """
387     >>> Rotate(30).asArray4x4()
388     array([[ 0.8660254, -0.5      ,  0.       ,  0.       ],
389            [ 0.5      ,  0.8660254,  0.       ,  0.       ],
390            [ 0.       ,  0.       ,  1.       ,  0.       ],
391            [ 0.       ,  0.       ,  0.       ,  1.       ]])
392     >>> Rotate(30, [1, 0, 0]).asArray4x4()
393     array([[ 1.       ,  0.       ,  0.       ,  0.       ],
394            [ 0.       ,  0.8660254, -0.5      ,  0.       ],
395            [ 0.       ,  0.5      ,  0.8660254,  0.       ],
396            [ 0.       ,  0.       ,  0.       ,  1.       ]])
397     >>> Rotate(30, [0, 1, 0]).asArray4x4()
398     array([[ 0.8660254,  0.       ,  0.5      ,  0.       ],
399            [ 0.       ,  1.       ,  0.       ,  0.       ],
400            [-0.5      ,  0.       ,  0.8660254,  0.       ],
401            [ 0.       ,  0.       ,  0.       ,  1.       ]])
402     >>> Rotate(30, [0, 0, 1]).asArray4x4()
403     array([[ 0.8660254, -0.5      ,  0.       ,  0.       ],
404            [ 0.5      ,  0.8660254,  0.       ,  0.       ],
405            [ 0.       ,  0.       ,  1.       ,  0.       ],
406            [ 0.       ,  0.       ,  0.       ,  1.       ]])
407     >>> Rotate(37, [2, 3, 5]).asArray4x4()
408     array([[ 0.81983177, -0.45634205,  0.34587252,  0.        ],
409            [ 0.51993083,  0.8463271 , -0.11576859,  0.        ],
410            [-0.23989122,  0.27474055,  0.93111215,  0.        ],
411            [ 0.        ,  0.        ,  0.        ,  1.        ]])
412     >>> Rotate(37, [2, 3, 5]).asInverse4x4()
413     array([[ 0.81983178,  0.51993083, -0.23989121,  0.        ],
414            [-0.45634205,  0.84632711,  0.27474056,  0.        ],
415            [ 0.34587252, -0.11576859,  0.93111215,  0.        ],
416            [ 0.        ,  0.        ,  0.        ,  1.        ]])
417     >>> Rotate(37, [1., 2., 3.]).Axis
418     <[0.26726123690605164, 0.53452247381210327, 0.80178368091583252, 1.0]>
419     >>> Rotate(30, [1., 2., 3.]).Angle
420     30.0
421     >>> Rotate(30, [1., 2., 3.]).Radians
422     0.52359877559829882
423     >>> Rotate(30, [1., 2., 3.])
424     <Rotation: Angle=30.0, Axis=[0.26726123690605164, 0.53452247381210327, 0.80178368091583252, 1.0]>
425     >>> a = Rotate(30, [1., 2., 3.]); a
426     <Rotation: Angle=30.0, Axis=[0.26726123690605164, 0.53452247381210327, 0.80178368091583252, 1.0]>
427     >>> ai = a.Inverse(); ai
428     <Rotation: Angle=30.0, Axis=[-0.26726126670837402, -0.53452253341674805, -0.80178374052047729, 1.0]>
429     >>> (a * ai).asArray4x4()
430     array([[ 1.00000000e+000,  2.39797163e-008, -2.63590731e-008,  0.00000000e+000],
431            [-2.55925432e-008,  9.99999998e-001,  9.66679894e-009,  0.00000000e+000],
432            [ 2.70206916e-008, -1.20927169e-008,  1.00000000e+000,  0.00000000e+000],
433            [ 0.00000000e+000,  0.00000000e+000,  0.00000000e+000,  1.00000000e+000]])
434     >>> (ai * a).asArray4x4()
435     array([[ 1.00000000e+000,  2.34966624e-008, -2.58865884e-008,  0.00000000e+000],
436            [-2.60755971e-008,  9.99999999e-001,  1.43609231e-008,  0.00000000e+000],
437            [ 2.35488830e-008, -1.52871791e-008,  1.00000000e+000,  0.00000000e+000],
438            [ 0.00000000e+000,  0.00000000e+000,  0.00000000e+000,  1.00000000e+000]])
439     """
440
441     Axis = UnitVector3HProperty('Axis', NumericType=TransformPrimitive3dh.NumericType)
442
443     def __init__(self, angle=0.0, axis=(0.0, 0.0, 1.0)):
444         self.Angle = float(angle)
445         self.Axis = axis
446
447     def __repr__(self):
448         return "<Rotation: Angle=%s, Axis=%s>" % (self.Angle, self.Axis.tolist(),)
449
450     def _getRadians(self):
451         return math.radians(self.Angle)
452     def _setRadians(self, value):
453         self.Angle = math.degrees(value)
454     Radians = property(_getRadians, _setRadians)
455
456     def asArray4x4(self):
457         u = self.Axis[:-1]
458         uut = Numeric.outerproduct(u, u)
459         M = Numeric.identity(3, self.NumericType) - uut
460         S = Numeric.asarray([[0., -u[2], u[1]], [u[2], 0., -u[0]], [-u[1], u[0], 0.]], self.NumericType)
461         radians = self.Radians
462         R = uut + Numeric.cos(radians) * M + Numeric.sin(radians) * S
463         result = Numeric.identity(4, self.NumericType)
464         result[:3,:3] = R
465         return result
466
467     def Inverse(self):
468         return self.__class__(self.Angle, -self.Axis)
469
470 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
471
472 class LookAt(TransformPrimitive3dh):
473     """
474     >>> l = LookAt((13.,-12.,11.), (1.,2.,3.), (1.,1.,1.)); l
475     <LookAt: Center=[1.0, 2.0, 3.0] Eye=[13.0, -12.0, 11.0] Up=[0.57735025882720947, 0.57735025882720947, 0.57735025882720947]>
476     >>> l.asArray4x4()
477     array([[  0.64153302,   0.11664237,  -0.75817537,   1.39970833],
478            [  0.4816635 ,   0.70798737,   0.51648253,  -3.44708487],
479            [  0.59702235,  -0.69652605,   0.3980149 , -20.49776715],
480            [  0.        ,   0.        ,   0.        ,   1.        ]])
481     """
482
483     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
484     #~ Constants / Variables / Etc.
485     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
486
487     Eye = Vector3Property('Eye', (0.,0.,1.), NumericType=TransformPrimitive3dh.NumericType)
488     Center = Vector3Property('Center', (0.,0.,0.), NumericType=TransformPrimitive3dh.NumericType)
489     Up = UnitVector3Property('Up', (0.,1.,0.), NumericType=TransformPrimitive3dh.NumericType)
490
491     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
492     #~ Public Methods
493     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~$
494
495     def __init__(self, Eye=None, Center=None, Up=None):
496         TransformPrimitive3dh.__init__(self)
497         if Eye is not None: self.Eye = Eye
498         if Center is not None: self.Center = Center
499         if Up is not None: self.Up = Up
500
501     def __repr__(self):
502         return "<LookAt: Center=%s Eye=%s Up=%s>" % (self.Center.tolist(), self.Eye.tolist(), self.Up.tolist())
503
504     def asArray4x4(self):
505         E = self.Eye
506         C = self.Center
507         U = self.Up
508         L = C - E
509         L.iNormalize()
510         S = L.Cross3(U)
511         S.iNormalize()
512         U_ = S.Cross3(L)
513
514         result = Numeric.identity(4, self.NumericType)
515         result[0,:-1] = S
516         result[1,:-1] = U_
517         result[2,:-1] = -L
518         xlate = Numeric.identity(4, self.NumericType)
519         xlate[:-1,3] = -E
520         return Numeric.dot(result, xlate)
521
522 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
523
524 class SphericalLookAt(LookAt):
525     """
526     >>> s = SphericalLookAt((10.,45.,45.)); s
527     <Spherical LookAt: Center=[0.0, 0.0, 0.0] RhoThetaPhi=(10.0, 45.0, 45.0) Up=[0.0, 1.0, 0.0]>
528     >>> s.asArray4x4()
529     array([[ 7.07106769e-001,  0.00000000e+000, -7.07106769e-001,  0.00000000e+000],
530            [-4.99999970e-001,  7.07106769e-001, -4.99999970e-001, -2.11174211e-007],
531            [ 5.00000000e-001,  7.07106769e-001,  5.00000000e-001, -9.99999991e+000],
532            [ 0.00000000e+000,  0.00000000e+000,  0.00000000e+000,  1.00000000e+000]])
533     >>> s.Eye
534     <[5.0, 7.0710678100585937, 5.0]>
535     """
536
537     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
538     #~ Constants / Variables / Etc.
539     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
540
541     _RhoThetaPhi = 1., 0., 90
542     Center = Vector3Property('Center', (0.,0.,0.), NumericType=TransformPrimitive3dh.NumericType)
543     Spherical = Vector3Property('Spherical', (0.,0.,1.), NumericType=TransformPrimitive3dh.NumericType)
544     Up = UnitVector3Property('Up', (0.,1.,0.), NumericType=TransformPrimitive3dh.NumericType)
545
546     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
547     #~ Public Methods
548     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
549
550     def __init__(self, RhoThetaPhi=None, Center=None, Up=None):
551         LookAt.__init__(self, None, Center=Center, Up=Up)
552         if RhoThetaPhi is not None: self.RhoThetaPhi = RhoThetaPhi
553
554     def __repr__(self):
555         return "<Spherical LookAt: Center=%s RhoThetaPhi=%s Up=%s>" % (self.Center.tolist(), self.RhoThetaPhi, self.Up.tolist())
556
557     def _SetRhoThetaPhi(self, (row,theta,fi)):
558         self._RhoThetaPhi = row,theta,fi
559         rowsinfi = row*Numeric.sin(fi)
560         x,y,z = (rowsinfi*Numeric.cos(theta),row*Numeric.cos(fi),rowsinfi*Numeric.sin(theta))
561         self.Spherical = x,y,z
562     def _GetRhoThetaPhi(self):
563         return self._RhoThetaPhi
564     RhoThetaPhiRadians = property(_GetRhoThetaPhi, _SetRhoThetaPhi)
565
566     def _SetRhoThetaPhi(self, (row,theta,fi)):
567         self.RhoThetaPhiRadians = row, math.radians(theta), math.radians(fi)
568     def _GetRhoThetaPhi(self):
569         row, theta, fi = self.RhoThetaPhiRadians
570         return row, math.degrees(theta), math.degrees(fi)
571     RhoThetaPhi = property(_GetRhoThetaPhi, _SetRhoThetaPhi)
572
573     def _GetEye(self):
574         return self.Center + self.Spherical
575     Eye = property(_GetEye)
576
577 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
578
579 class Shear(Matrix):
580     """
581     >>> s = Shear(1,2,3,4,5,6)
582     >>> s
583     <Shear: [[1.0, 1.0, 2.0, 0.0], [3.0, 1.0, 4.0, 0.0], [5.0, 6.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]] >
584     >>> s.asArray4x4()
585     array([[ 1.,  1.,  2.,  0.],
586            [ 3.,  1.,  4.,  0.],
587            [ 5.,  6.,  1.,  0.],
588            [ 0.,  0.,  0.,  1.]])
589     >>> s.asInverse4x4()
590     array([[-1.15,  0.55,  0.1 ,  0.  ],
591            [ 0.85, -0.45,  0.1 ,  0.  ],
592            [ 0.65, -0.05, -0.1 ,  0.  ],
593            [ 0.  ,  0.  ,  0.  ,  1.  ]])
594     """
595
596     def __init__(self, xy=0.0, xz=0.0, yx=0.0, yz = 0.0, zx=0.0, zy=0.0):
597         Matrix.__init__(self)
598         self.matrix[0, 1] = xy
599         self.matrix[0, 2] = xz
600         self.matrix[1, 0] = yx
601         self.matrix[1, 2] = yz
602         self.matrix[2, 0] = zx
603         self.matrix[2, 1] = zy
604
605     def __repr__(self):
606         return "<Shear: %s >" % self.matrix.tolist()
607
608     def Inverse(self):
609         result = Matrix(self.asInverse4x4())
610         result.__class__ = self.__class__ # a little black magic never hurt too much ;)
611         return result
612
613 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
614
615 class Skew(Shear):
616     """
617     >>> s = Skew(10,20,30,40,50,60)
618     >>> s
619     <Skew: [[1.0, 0.17632698070846498, 0.36397023426620234, 0.0], [0.57735026918962573, 1.0, 0.83909963117727993, 0.0], [1.19175359259421, 1.7320508075688767, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]] >
620     >>> s.asArray4x4()
621     array([[ 1.        ,  0.17632698,  0.36397023,  0.        ],
622            [ 0.57735027,  1.        ,  0.83909963,  0.        ],
623            [ 1.19175359,  1.73205081,  1.        ,  0.        ],
624            [ 0.        ,  0.        ,  0.        ,  1.        ]])
625     >>> s.asInverse4x4()
626     array([[ 1.01054753, -1.01216303,  0.4814964 ,  0.        ],
627            [-0.94208715, -1.26214385,  1.40195612,  0.        ],
628            [ 0.42741917,  3.39234621, -2.00208431,  0.        ],
629            [ 0.        ,  0.        ,  0.        ,  1.        ]])
630     """
631
632     def __init__(self, xy=0.0, xz=0.0, yx=0.0, yz=0.0, zx=0.0, zy=0.0):
633         kw = self._ConvertArgs(xy=xy, xz=xz, yx=yx, yz=yz, zx=zx, zy=zy)
634         Shear.__init__(self, **kw)
635
636     def __repr__(self):
637         return "<Skew: %s >" % self.matrix.tolist()
638
639     def _ConvertArgs(self, **kw):
640         result = {}
641         for key,value in kw.iteritems():
642             result[key] = Numeric.tan(math.radians(value))
643         return result
644
645     def Inverse(self):
646         result = Matrix(self.asInverse4x4())
647         result.__class__ = self.__class__ # a little black magic never hurt too much ;)
648         return result
649
650 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
651
652 class LinearMappingMatrix(Matrix):
653     """
654     >>> M = LinearMappingMatrix()
655     >>> M.toX = [0., 100.]
656     >>> M.toY = [10., 20.]
657     >>> M.fromZ = [10., 20.]
658     >>> M
659     <LinearMappingMatrix: [([-1.0, 1.0], [0.0, 100.0]), ([-1.0, 1.0], [10.0, 20.0]), ([10.0, 20.0], [-1.0, 1.0])] >
660     >>> M.asArray4x4()
661     array([[ 50. ,   0. ,   0. ,  50. ],
662            [  0. ,   5. ,   0. ,  15. ],
663            [  0. ,   0. ,   0.2,  -3. ],
664            [  0. ,   0. ,   0. ,   1. ]])
665     """
666     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
667     #~ Definitions
668     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
669
670     fromX = [-1., 1.]
671     fromY = [-1., 1.]
672     fromZ = [-1., 1.]
673
674     toX = [-1., 1.]
675     toY = [-1., 1.]
676     toZ = [-1., 1.]
677
678     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
679     #~ Public Methods
680     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
681
682     def __repr__(self):
683         return "<LinearMappingMatrix: %s >" % ([(self.fromX, self.toX), (self.fromY, self.toY), (self.fromZ, self.toZ)],)
684
685     def SetFromRect(self, rect):
686         self.fromX = [rect[0], rect[0] + rect[2]]
687         self.fromY = [rect[1], rect[1] + rect[3]]
688
689     def SetToRect(self, rect):
690         self.toX = [rect[0], rect[0] + rect[2]]
691         self.toY = [rect[1], rect[1] + rect[3]]
692
693     def asArray4x4(self):
694         Xs,Xt = LinearMapping(self.fromX, self.toX, False)
695         Ys,Yt = LinearMapping(self.fromY, self.toY, False)
696         Zs,Zt = LinearMapping(self.fromZ, self.toZ, False)
697         result = Numeric.asarray([
698             [Xs, 0, 0, Xt],
699             [0, Ys, 0, Yt],
700             [0, 0, Zs, Zt],
701             [0, 0, 0, 1]],
702             self.NumericType)
703         return result
704
705     def asInverse4x4(self):
706         Xs,Xt = LinearMapping(self.toX, self.fromX, False)
707         Ys,Yt = LinearMapping(self.toY, self.fromY, False)
708         Zs,Zt = LinearMapping(self.toZ, self.fromZ, False)
709         result = Numeric.asarray([
710             [Xs, 0, 0, Xt],
711             [0, Ys, 0, Yt],
712             [0, 0, Zs, Zt],
713             [0, 0, 0, 1]],
714             self.NumericType)
715         return result
716
717 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
718 #~ Testing
719 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
720
721 if __name__=='__main__':
722     print "Testing..."
723     import doctest, Transformations
724     doctest.testmod(Transformations)
725     print "Test complete."
726
727
Note: See TracBrowser for help on using the browser.