Update to 2.0.0 tree from current Fremantle build
[opencv] / 3rdparty / include / OpenEXR / ImathFrustum.h
1 ///////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (c) 2002, Industrial Light & Magic, a division of Lucas
4 // Digital Ltd. LLC
5 // 
6 // All rights reserved.
7 // 
8 // Redistribution and use in source and binary forms, with or without
9 // modification, are permitted provided that the following conditions are
10 // met:
11 // *       Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 // *       Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
16 // distribution.
17 // *       Neither the name of Industrial Light & Magic nor the names of
18 // its contributors may be used to endorse or promote products derived
19 // from this software without specific prior written permission. 
20 // 
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 //
33 ///////////////////////////////////////////////////////////////////////////
34
35
36
37 #ifndef INCLUDED_IMATHFRUSTUM_H
38 #define INCLUDED_IMATHFRUSTUM_H
39
40
41 #include "ImathVec.h"
42 #include "ImathPlane.h"
43 #include "ImathLine.h"
44 #include "ImathMatrix.h"
45 #include "ImathLimits.h"
46 #include "ImathFun.h"
47 #include "IexMathExc.h"
48
49 #if defined _WIN32 || defined _WIN64
50     #ifdef near
51         #undef near
52     #endif
53     #ifdef far
54         #undef far
55     #endif
56 #endif
57
58 namespace Imath {
59
60 //
61 //      template class Frustum<T>
62 //
63 //      The frustum is always located with the eye point at the
64 //      origin facing down -Z. This makes the Frustum class 
65 //      compatable with OpenGL (or anything that assumes a camera
66 //      looks down -Z, hence with a right-handed coordinate system) 
67 //      but not with RenderMan which assumes the camera looks down
68 //      +Z. Additional functions are provided for conversion from
69 //      and from various camera coordinate spaces.
70 //
71
72
73 template<class T>
74 class Frustum
75 {
76   public:
77     Frustum();
78     Frustum(const Frustum &);
79     Frustum(T near, T far, T left, T right, T top, T bottom, bool ortho=false);
80     Frustum(T near, T far, T fovx, T fovy, T aspect);
81     virtual ~Frustum();
82
83     //--------------------
84     // Assignment operator
85     //--------------------
86
87     const Frustum &operator     = (const Frustum &);
88
89     //--------------------
90     //  Operators:  ==, !=
91     //--------------------
92     
93     bool                        operator == (const Frustum<T> &src) const;
94     bool                        operator != (const Frustum<T> &src) const;
95
96     //--------------------------------------------------------
97     //  Set functions change the entire state of the Frustum
98     //--------------------------------------------------------
99
100     void                set(T near, T far, 
101                             T left, T right, 
102                             T top, T bottom, 
103                             bool ortho=false);
104
105     void                set(T near, T far, T fovx, T fovy, T aspect);
106
107     //------------------------------------------------------
108     //  These functions modify an already valid frustum state
109     //------------------------------------------------------
110
111     void                modifyNearAndFar(T near, T far);
112     void                setOrthographic(bool);
113
114     //--------------
115     //  Access
116     //--------------
117
118     bool                orthographic() const    { return _orthographic; }
119     T                   near() const            { return _near;         }
120     T                   far() const             { return _far;          }
121     T                   left() const            { return _left;         }
122     T                   right() const           { return _right;        }
123     T                   bottom() const          { return _bottom;       }
124     T                   top() const             { return _top;          }
125
126     //-----------------------------------------------------------------------
127     //  Sets the planes in p to be the six bounding planes of the frustum, in
128     //  the following order: top, right, bottom, left, near, far.
129     //  Note that the planes have normals that point out of the frustum.
130     //  The version of this routine that takes a matrix applies that matrix
131     //  to transform the frustum before setting the planes.
132     //-----------------------------------------------------------------------
133
134     void                planes(Plane3<T> p[6]);
135     void                planes(Plane3<T> p[6], const Matrix44<T> &M);
136
137     //----------------------
138     //  Derived Quantities
139     //----------------------
140
141     T                   fovx() const;
142     T                   fovy() const;
143     T                   aspect() const;
144     Matrix44<T>         projectionMatrix() const;
145
146     //-----------------------------------------------------------------------
147     //  Takes a rectangle in the screen space (i.e., -1 <= left <= right <= 1 
148     //  and -1 <= bottom <= top <= 1) of this Frustum, and returns a new
149     //  Frustum whose near clipping-plane window is that rectangle in local
150     //  space.  
151     //-----------------------------------------------------------------------
152
153     Frustum<T>          window(T left, T right, T top, T bottom) const;
154
155     //----------------------------------------------------------
156     // Projection is in screen space / Conversion from Z-Buffer
157     //----------------------------------------------------------
158
159     Line3<T>            projectScreenToRay( const Vec2<T> & ) const;
160     Vec2<T>             projectPointToScreen( const Vec3<T> & ) const;
161
162     T                   ZToDepth(long zval, long min, long max) const;
163     T                   normalizedZToDepth(T zval) const;
164     long                DepthToZ(T depth, long zmin, long zmax) const;
165
166     T                   worldRadius(const Vec3<T> &p, T radius) const;
167     T                   screenRadius(const Vec3<T> &p, T radius) const;
168
169
170   protected:
171
172     Vec2<T>             screenToLocal( const Vec2<T> & ) const;
173     Vec2<T>             localToScreen( const Vec2<T> & ) const;
174
175   protected:
176     T                   _near;
177     T                   _far;
178     T                   _left;
179     T                   _right;
180     T                   _top;
181     T                   _bottom;
182     bool                _orthographic;
183 };
184
185
186 template<class T>
187 inline Frustum<T>::Frustum()
188 {
189     set(T (0.1),
190         T (1000.0),
191         T (-1.0),
192         T (1.0),
193         T (1.0),
194         T (-1.0),
195         false);
196 }
197
198 template<class T>
199 inline Frustum<T>::Frustum(const Frustum &f)
200 {
201     *this = f;
202 }
203
204 template<class T>
205 inline Frustum<T>::Frustum(T n, T f, T l, T r, T t, T b, bool o)
206 {
207     set(n,f,l,r,t,b,o);
208 }
209
210 template<class T>
211 inline Frustum<T>::Frustum(T near, T far, T fovx, T fovy, T aspect)
212 {
213     set(near,far,fovx,fovy,aspect);
214 }
215
216 template<class T>
217 Frustum<T>::~Frustum()
218 {
219 }
220
221 template<class T>
222 const Frustum<T> &
223 Frustum<T>::operator = (const Frustum &f)
224 {
225     _near         = f._near;
226     _far          = f._far;
227     _left         = f._left;
228     _right        = f._right;
229     _top          = f._top;
230     _bottom       = f._bottom;
231     _orthographic = f._orthographic;
232
233     return *this;
234 }
235
236 template <class T>
237 bool
238 Frustum<T>::operator == (const Frustum<T> &src) const
239 {
240     return
241         _near         == src._near   &&
242         _far          == src._far    &&
243         _left         == src._left   &&
244         _right        == src._right  &&
245         _top          == src._top    &&
246         _bottom       == src._bottom &&
247         _orthographic == src._orthographic;
248 }
249
250 template <class T>
251 inline bool
252 Frustum<T>::operator != (const Frustum<T> &src) const
253 {
254     return !operator== (src);
255 }
256
257 template<class T>
258 void Frustum<T>::set(T n, T f, T l, T r, T t, T b, bool o)
259 {
260     _near           = n;
261     _far            = f;
262     _left           = l;
263     _right          = r;
264     _bottom         = b;
265     _top            = t;
266     _orthographic   = o;
267 }
268
269 template<class T>
270 void Frustum<T>::modifyNearAndFar(T n, T f)
271 {
272     if ( _orthographic )
273     {
274         _near = n;
275     }
276     else
277     {
278         Line3<T>  lowerLeft( Vec3<T>(0,0,0), Vec3<T>(_left,_bottom,-_near) );
279         Line3<T> upperRight( Vec3<T>(0,0,0), Vec3<T>(_right,_top,-_near) );
280         Plane3<T> nearPlane( Vec3<T>(0,0,-1), n );
281
282         Vec3<T> ll,ur;
283         nearPlane.intersect(lowerLeft,ll);
284         nearPlane.intersect(upperRight,ur);
285
286         _left   = ll.x;
287         _right  = ur.x;
288         _top    = ur.y;
289         _bottom = ll.y;
290         _near   = n;
291         _far    = f;
292     }
293
294     _far = f;
295 }
296
297 template<class T>
298 void Frustum<T>::setOrthographic(bool ortho)
299 {
300     _orthographic   = ortho;
301 }
302
303 template<class T>
304 void Frustum<T>::set(T near, T far, T fovx, T fovy, T aspect)
305 {
306     if (fovx != 0 && fovy != 0)
307         throw Iex::ArgExc ("fovx and fovy cannot both be non-zero.");
308
309     if (fovx != 0)
310     {
311         _right      = near * Math<T>::tan(fovx/2.0);
312         _left       = -_right;
313         _top        = ((_right - _left)/aspect)/2.0;
314         _bottom     = -_top;
315     }
316     else
317     {
318         _top        = near * Math<T>::tan(fovy/2.0);
319         _bottom     = -_top;
320         _right      = (_top - _bottom) * aspect / 2.0;
321         _left       = -_right;
322     }
323     _near           = near;
324     _far            = far;
325     _orthographic   = false;
326 }
327
328 template<class T>
329 T Frustum<T>::fovx() const
330 {
331     return Math<T>::atan2(_right,_near) - Math<T>::atan2(_left,_near);
332 }
333
334 template<class T>
335 T Frustum<T>::fovy() const
336 {
337     return Math<T>::atan2(_top,_near) - Math<T>::atan2(_bottom,_near);
338 }
339
340 template<class T>
341 T Frustum<T>::aspect() const
342 {
343     T rightMinusLeft = _right-_left;
344     T topMinusBottom = _top-_bottom;
345
346     if (abs(topMinusBottom) < 1 &&
347         abs(rightMinusLeft) > limits<T>::max() * abs(topMinusBottom))
348     {
349         throw Iex::DivzeroExc ("Bad viewing frustum: "
350                                "aspect ratio cannot be computed.");
351     }
352
353     return rightMinusLeft / topMinusBottom;
354 }
355
356 template<class T>
357 Matrix44<T> Frustum<T>::projectionMatrix() const
358 {
359     T rightPlusLeft  = _right+_left;
360     T rightMinusLeft = _right-_left;
361
362     T topPlusBottom  = _top+_bottom;
363     T topMinusBottom = _top-_bottom;
364
365     T farPlusNear    = _far+_near;
366     T farMinusNear   = _far-_near;
367
368     if ((abs(rightMinusLeft) < 1 &&
369          abs(rightPlusLeft) > limits<T>::max() * abs(rightMinusLeft)) ||
370         (abs(topMinusBottom) < 1 &&
371          abs(topPlusBottom) > limits<T>::max() * abs(topMinusBottom)) ||
372         (abs(farMinusNear) < 1 &&
373          abs(farPlusNear) > limits<T>::max() * abs(farMinusNear)))
374     {
375         throw Iex::DivzeroExc ("Bad viewing frustum: "
376                                "projection matrix cannot be computed.");
377     }
378
379     if ( _orthographic )
380     {
381         T tx = -rightPlusLeft / rightMinusLeft;
382         T ty = -topPlusBottom / topMinusBottom;
383         T tz = -farPlusNear   / farMinusNear;
384
385         if ((abs(rightMinusLeft) < 1 &&
386              2 > limits<T>::max() * abs(rightMinusLeft)) ||
387             (abs(topMinusBottom) < 1 &&
388              2 > limits<T>::max() * abs(topMinusBottom)) ||
389             (abs(farMinusNear) < 1 &&
390              2 > limits<T>::max() * abs(farMinusNear)))
391         {
392             throw Iex::DivzeroExc ("Bad viewing frustum: "
393                                    "projection matrix cannot be computed.");
394         }
395
396         T A  =  2 / rightMinusLeft;
397         T B  =  2 / topMinusBottom;
398         T C  = -2 / farMinusNear;
399
400         return Matrix44<T>( A,  0,  0,  0,
401                             0,  B,  0,  0,
402                             0,  0,  C,  0,
403                             tx, ty, tz, 1.f );
404     }
405     else
406     {
407         T A =  rightPlusLeft / rightMinusLeft;
408         T B =  topPlusBottom / topMinusBottom;
409         T C = -farPlusNear   / farMinusNear;
410
411         T farTimesNear = -2 * _far * _near;
412         if (abs(farMinusNear) < 1 &&
413             abs(farTimesNear) > limits<T>::max() * abs(farMinusNear))
414         {
415             throw Iex::DivzeroExc ("Bad viewing frustum: "
416                                    "projection matrix cannot be computed.");
417         }
418
419         T D = farTimesNear / farMinusNear;
420
421         T twoTimesNear = 2 * _near;
422
423         if ((abs(rightMinusLeft) < 1 &&
424              abs(twoTimesNear) > limits<T>::max() * abs(rightMinusLeft)) ||
425             (abs(topMinusBottom) < 1 &&
426              abs(twoTimesNear) > limits<T>::max() * abs(topMinusBottom)))
427         {
428             throw Iex::DivzeroExc ("Bad viewing frustum: "
429                                    "projection matrix cannot be computed.");
430         }
431
432         T E = twoTimesNear / rightMinusLeft;
433         T F = twoTimesNear / topMinusBottom;
434
435         return Matrix44<T>( E,  0,  0,  0,
436                             0,  F,  0,  0,
437                             A,  B,  C, -1,
438                             0,  0,  D,  0 );
439     }
440 }
441
442 template<class T>
443 Frustum<T> Frustum<T>::window(T l, T r, T t, T b) const
444 {
445     // move it to 0->1 space
446
447     Vec2<T> bl = screenToLocal( Vec2<T>(l,b) );
448     Vec2<T> tr = screenToLocal( Vec2<T>(r,t) );
449
450     return Frustum<T>(_near, _far, bl.x, tr.x, tr.y, bl.y, _orthographic);
451 }
452
453
454 template<class T>
455 Vec2<T> Frustum<T>::screenToLocal(const Vec2<T> &s) const
456 {
457     return Vec2<T>( _left + (_right-_left) * (1.f+s.x) / 2.f,
458                     _bottom + (_top-_bottom) * (1.f+s.y) / 2.f );
459 }
460
461 template<class T>
462 Vec2<T> Frustum<T>::localToScreen(const Vec2<T> &p) const
463 {
464     T leftPlusRight  = _left - 2 * p.x + _right;
465     T leftMinusRight = _left-_right;
466     T bottomPlusTop  = _bottom - 2 * p.y + _top;
467     T bottomMinusTop = _bottom-_top;
468
469     if ((abs(leftMinusRight) < 1 &&
470          abs(leftPlusRight) > limits<T>::max() * abs(leftMinusRight)) ||
471         (abs(bottomMinusTop) < 1 &&
472          abs(bottomPlusTop) > limits<T>::max() * abs(bottomMinusTop)))
473     {
474         throw Iex::DivzeroExc
475             ("Bad viewing frustum: "
476              "local-to-screen transformation cannot be computed");
477     }
478
479     return Vec2<T>( leftPlusRight / leftMinusRight,
480                     bottomPlusTop / bottomMinusTop );
481 }
482
483 template<class T>
484 Line3<T> Frustum<T>::projectScreenToRay(const Vec2<T> &p) const
485 {
486     Vec2<T> point = screenToLocal(p);
487     if (orthographic())
488         return Line3<T>( Vec3<T>(point.x,point.y, 0.0),
489                          Vec3<T>(point.x,point.y,-_near));
490     else
491         return Line3<T>( Vec3<T>(0, 0, 0), Vec3<T>(point.x,point.y,-_near));
492 }
493
494 template<class T>
495 Vec2<T> Frustum<T>::projectPointToScreen(const Vec3<T> &point) const
496 {
497     if (orthographic() || point.z == 0)
498         return localToScreen( Vec2<T>( point.x, point.y ) );
499     else
500         return localToScreen( Vec2<T>( point.x * _near / -point.z, 
501                                        point.y * _near / -point.z ) );
502 }
503
504 template<class T>
505 T Frustum<T>::ZToDepth(long zval,long zmin,long zmax) const
506 {
507     int zdiff = zmax - zmin;
508
509     if (zdiff == 0)
510     {
511         throw Iex::DivzeroExc
512             ("Bad call to Frustum::ZToDepth: zmax == zmin");
513     }
514
515     if ( zval > zmax+1 ) zval -= zdiff;
516
517     T fzval = (T(zval) - T(zmin)) / T(zdiff);
518     return normalizedZToDepth(fzval);
519 }
520
521 template<class T>
522 T Frustum<T>::normalizedZToDepth(T zval) const
523 {
524     T Zp = zval * 2.0 - 1;
525
526     if ( _orthographic )
527     {
528         return   -(Zp*(_far-_near) + (_far+_near))/2;
529     }
530     else 
531     {
532         T farTimesNear = 2 * _far * _near;
533         T farMinusNear = Zp * (_far - _near) - _far - _near;
534
535         if (abs(farMinusNear) < 1 &&
536             abs(farTimesNear) > limits<T>::max() * abs(farMinusNear))
537         {
538             throw Iex::DivzeroExc
539                 ("Frustum::normalizedZToDepth cannot be computed.  The "
540                  "near and far clipping planes of the viewing frustum "
541                  "may be too close to each other");
542         }
543
544         return farTimesNear / farMinusNear;
545     }
546 }
547
548 template<class T>
549 long Frustum<T>::DepthToZ(T depth,long zmin,long zmax) const
550 {
551     long zdiff     = zmax - zmin;
552     T farMinusNear = _far-_near;
553
554     if ( _orthographic )
555     {
556         T farPlusNear = 2*depth + _far + _near;
557
558         if (abs(farMinusNear) < 1 &&
559             abs(farPlusNear) > limits<T>::max() * abs(farMinusNear))
560         {
561             throw Iex::DivzeroExc
562                 ("Bad viewing frustum: near and far clipping planes "
563                  "are too close to each other");
564         }
565
566         T Zp = -farPlusNear/farMinusNear;
567         return long(0.5*(Zp+1)*zdiff) + zmin;
568     }
569     else 
570     { 
571         // Perspective
572
573         T farTimesNear = 2*_far*_near;
574         if (abs(depth) < 1 &&
575             abs(farTimesNear) > limits<T>::max() * abs(depth))
576         {
577             throw Iex::DivzeroExc
578                 ("Bad call to DepthToZ function: value of `depth' "
579                  "is too small");
580         }
581
582         T farPlusNear = farTimesNear/depth + _far + _near;
583         if (abs(farMinusNear) < 1 &&
584             abs(farPlusNear) > limits<T>::max() * abs(farMinusNear))
585         {
586             throw Iex::DivzeroExc
587                 ("Bad viewing frustum: near and far clipping planes "
588                  "are too close to each other");
589         }
590
591         T Zp = farPlusNear/farMinusNear;
592         return long(0.5*(Zp+1)*zdiff) + zmin;
593     }
594 }
595
596 template<class T>
597 T Frustum<T>::screenRadius(const Vec3<T> &p, T radius) const
598 {
599     // Derivation:
600     // Consider X-Z plane.
601     // X coord of projection of p = xp = p.x * (-_near / p.z)
602     // Let q be p + (radius, 0, 0).
603     // X coord of projection of q = xq = (p.x - radius)  * (-_near / p.z)
604     // X coord of projection of segment from p to q = r = xp - xq
605     //         = radius * (-_near / p.z)
606     // A similar analysis holds in the Y-Z plane.
607     // So r is the quantity we want to return.
608
609     if (abs(p.z) > 1 || abs(-_near) < limits<T>::max() * abs(p.z))
610     {
611         return radius * (-_near / p.z);
612     }
613     else
614     {
615         throw Iex::DivzeroExc
616             ("Bad call to Frustum::screenRadius: the magnitude of `p' "
617              "is too small");
618     }
619
620     return radius * (-_near / p.z);
621 }
622
623 template<class T>
624 T Frustum<T>::worldRadius(const Vec3<T> &p, T radius) const
625 {
626     if (abs(-_near) > 1 || abs(p.z) < limits<T>::max() * abs(-_near))
627     {
628         return radius * (p.z / -_near);
629     }
630     else
631     {
632         throw Iex::DivzeroExc
633             ("Bad viewing frustum: the near clipping plane is too "
634              "close to zero");
635     }
636 }
637
638 template<class T>
639 void Frustum<T>::planes(Plane3<T> p[6])
640 {
641     //
642     //  Plane order: Top, Right, Bottom, Left, Near, Far.
643     //  Normals point outwards.
644     //
645
646     Vec3<T> a( _left,  _bottom, -_near);
647     Vec3<T> b( _left,  _top,    -_near);
648     Vec3<T> c( _right, _top,    -_near);
649     Vec3<T> d( _right, _bottom, -_near);
650     Vec3<T> o(0,0,0);
651
652     p[0].set( o, c, b );
653     p[1].set( o, d, c );
654     p[2].set( o, a, d );
655     p[3].set( o, b, a );
656     p[4].set( Vec3<T>(0, 0, 1), -_near );
657     p[5].set( Vec3<T>(0, 0,-1), _far );
658 }
659
660
661 template<class T>
662 void Frustum<T>::planes(Plane3<T> p[6], const Matrix44<T> &M)
663 {
664     //
665     //  Plane order: Top, Right, Bottom, Left, Near, Far.
666     //  Normals point outwards.
667     //
668
669     Vec3<T> a   = Vec3<T>( _left,  _bottom, -_near) * M;
670     Vec3<T> b   = Vec3<T>( _left,  _top,    -_near) * M;
671     Vec3<T> c   = Vec3<T>( _right, _top,    -_near) * M;
672     Vec3<T> d   = Vec3<T>( _right, _bottom, -_near) * M;
673     double s    = _far / double(_near);
674     T farLeft   = (T) (s * _left);
675     T farRight  = (T) (s * _right);
676     T farTop    = (T) (s * _top);
677     T farBottom = (T) (s * _bottom);
678     Vec3<T> e   = Vec3<T>( farLeft,  farBottom, -_far) * M;
679     Vec3<T> f   = Vec3<T>( farLeft,  farTop,    -_far) * M;
680     Vec3<T> g   = Vec3<T>( farRight, farTop,    -_far) * M;
681     Vec3<T> o   = Vec3<T>(0,0,0) * M;
682
683     p[0].set( o, c, b );
684     p[1].set( o, d, c );
685     p[2].set( o, a, d );
686     p[3].set( o, b, a );
687     p[4].set( a, d, c );
688     p[5].set( e, f, g );
689 }
690
691 typedef Frustum<float>  Frustumf;
692 typedef Frustum<double> Frustumd;
693
694
695 } // namespace Imath
696
697 #endif