0030833: Visualization - Graphic3d_Camera constructor's exceptions
[occt.git] / src / Graphic3d / Graphic3d_Camera.cxx
1 // Created on: 2013-05-29
2 // Created by: Anton POLETAEV
3 // Copyright (c) 1999-2014 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 #include <Graphic3d_Camera.hxx>
17
18 #include <gp_Pln.hxx>
19 #include <gp_QuaternionNLerp.hxx>
20 #include <gp_QuaternionSLerp.hxx>
21 #include <Graphic3d_Vec4.hxx>
22 #include <Graphic3d_WorldViewProjState.hxx>
23 #include <NCollection_Sequence.hxx>
24 #include <Standard_ShortReal.hxx>
25 #include <Standard_Atomic.hxx>
26 #include <Standard_Assert.hxx>
27
28 IMPLEMENT_STANDARD_RTTIEXT(Graphic3d_Camera,Standard_Transient)
29
30 namespace
31 {
32   // (degrees -> radians) * 0.5
33   static const Standard_Real DTR_HALF = 0.5 * 0.0174532925;
34
35   // default property values
36   static const Standard_Real DEFAULT_ZNEAR = 0.001;
37   static const Standard_Real DEFAULT_ZFAR  = 3000.0;
38
39   // atomic state counter
40   static volatile Standard_Integer THE_STATE_COUNTER = 0;
41
42   // minimum camera distance
43   static const Standard_Real MIN_DISTANCE = Pow (0.1, ShortRealDigits() - 2);
44
45   // z-range tolerance compatible with for floating point.
46   static Standard_Real zEpsilon()
47   {
48     return FLT_EPSILON;
49   }
50
51   // relative z-range tolerance compatible with for floating point.
52   static Standard_Real zEpsilon (const Standard_Real theValue)
53   {
54     Standard_Real anAbsValue = Abs (theValue);
55     if (anAbsValue <= (double)FLT_MIN)
56     {
57       return FLT_MIN;
58     }
59     Standard_Real aLogRadix = Log10 (anAbsValue) / Log10 (FLT_RADIX);
60     Standard_Real aExp = Floor (aLogRadix);
61     return FLT_EPSILON * Pow (FLT_RADIX, aExp);
62   }
63
64   //! Convert camera definition to Ax3
65   gp_Ax3 cameraToAx3 (const Graphic3d_Camera& theCamera)
66   {
67     const gp_Dir aBackDir(gp_Vec(theCamera.Center(), theCamera.Eye()));
68     const gp_Dir anXAxis (theCamera.Up().Crossed (aBackDir));
69     const gp_Dir anYAxis (aBackDir      .Crossed (anXAxis));
70     const gp_Dir aZAxis  (anXAxis       .Crossed (anYAxis));
71     return gp_Ax3 (gp_Pnt (0.0, 0.0, 0.0), aZAxis, anXAxis);
72   }
73 }
74
75 // =======================================================================
76 // function : Graphic3d_Camera
77 // purpose  :
78 // =======================================================================
79 Graphic3d_Camera::Graphic3d_Camera()
80 : myUp (0.0, 1.0, 0.0),
81   myEye (0.0, 0.0, -1500.0),
82   myCenter (0.0, 0.0, 0.0),
83   myAxialScale (1.0, 1.0, 1.0),
84   myProjType (Projection_Orthographic),
85   myFOVy (45.0),
86   myFOVyTan (Tan (DTR_HALF * 45.0)),
87   myZNear (DEFAULT_ZNEAR),
88   myZFar (DEFAULT_ZFAR),
89   myAspect (1.0),
90   myScale (1000.0),
91   myZFocus (1.0),
92   myZFocusType (FocusType_Relative),
93   myIOD (0.05),
94   myIODType (IODType_Relative)
95 {
96   myWorldViewProjState.Initialize ((Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER),
97                                    (Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER),
98                                    this);
99 }
100
101 // =======================================================================
102 // function : Graphic3d_Camera
103 // purpose  :
104 // =======================================================================
105 Graphic3d_Camera::Graphic3d_Camera (const Handle(Graphic3d_Camera)& theOther)
106 : myUp (0.0, 1.0, 0.0),
107   myEye (0.0, 0.0, -1500.0),
108   myCenter (0.0, 0.0, 0.0),
109   myAxialScale (1.0, 1.0, 1.0),
110   myProjType (Projection_Orthographic),
111   myFOVy (45.0),
112   myFOVyTan (Tan (DTR_HALF * 45.0)),
113   myZNear (DEFAULT_ZNEAR),
114   myZFar (DEFAULT_ZFAR),
115   myAspect (1.0),
116   myScale (1000.0),
117   myZFocus (1.0),
118   myZFocusType (FocusType_Relative),
119   myIOD (0.05),
120   myIODType (IODType_Relative)
121 {
122   myWorldViewProjState.Initialize (this);
123
124   Copy (theOther);
125 }
126
127 // =======================================================================
128 // function : CopyMappingData
129 // purpose  :
130 // =======================================================================
131 void Graphic3d_Camera::CopyMappingData (const Handle(Graphic3d_Camera)& theOtherCamera)
132 {
133   SetFOVy           (theOtherCamera->FOVy());
134   SetZRange         (theOtherCamera->ZNear(), theOtherCamera->ZFar());
135   SetAspect         (theOtherCamera->Aspect());
136   SetScale          (theOtherCamera->Scale());
137   SetZFocus         (theOtherCamera->ZFocusType(), theOtherCamera->ZFocus());
138   SetIOD            (theOtherCamera->GetIODType(), theOtherCamera->IOD());
139   SetProjectionType (theOtherCamera->ProjectionType());
140   SetTile           (theOtherCamera->myTile);
141 }
142
143 // =======================================================================
144 // function : CopyOrientationData
145 // purpose  :
146 // =======================================================================
147 void Graphic3d_Camera::CopyOrientationData (const Handle(Graphic3d_Camera)& theOtherCamera)
148 {
149   SetUp         (theOtherCamera->Up());
150   SetEye        (theOtherCamera->Eye());
151   SetCenter     (theOtherCamera->Center());
152   SetAxialScale (theOtherCamera->AxialScale());
153 }
154
155 // =======================================================================
156 // function : Copy
157 // purpose  :
158 // =======================================================================
159 void Graphic3d_Camera::Copy (const Handle(Graphic3d_Camera)& theOther)
160 {
161   CopyMappingData (theOther);
162   CopyOrientationData (theOther);
163 }
164
165 // =======================================================================
166 // function : SetEye
167 // purpose  :
168 // =======================================================================
169 void Graphic3d_Camera::SetEye (const gp_Pnt& theEye)
170 {
171   if (Eye().IsEqual (theEye, 0.0))
172   {
173     return;
174   }
175
176   myEye = theEye;
177   InvalidateOrientation();
178 }
179
180 // =======================================================================
181 // function : SetCenter
182 // purpose  :
183 // =======================================================================
184 void Graphic3d_Camera::SetCenter (const gp_Pnt& theCenter)
185 {
186   if (Center().IsEqual (theCenter, 0.0))
187   {
188     return;
189   }
190
191   myCenter = theCenter;
192   InvalidateOrientation();
193 }
194
195 // =======================================================================
196 // function : SetUp
197 // purpose  :
198 // =======================================================================
199 void Graphic3d_Camera::SetUp (const gp_Dir& theUp)
200 {
201   if (Up().IsEqual (theUp, 0.0))
202   {
203     return;
204   }
205
206   myUp = theUp;
207   InvalidateOrientation();
208 }
209
210 // =======================================================================
211 // function : SetAxialScale
212 // purpose  :
213 // =======================================================================
214 void Graphic3d_Camera::SetAxialScale (const gp_XYZ& theAxialScale)
215 {
216   if (AxialScale().IsEqual (theAxialScale, 0.0))
217   {
218     return;
219   }
220
221   myAxialScale = theAxialScale;
222   InvalidateOrientation();
223 }
224
225 // =======================================================================
226 // function : SetDistance
227 // purpose  :
228 // =======================================================================
229 void Graphic3d_Camera::SetDistance (const Standard_Real theDistance)
230 {
231   if (Distance() == theDistance)
232   {
233     return;
234   }
235
236   gp_Vec aCenter2Eye (Direction());
237   aCenter2Eye.Reverse();
238
239   // Camera should have non-zero distance.
240   aCenter2Eye.Scale (Max (theDistance, MIN_DISTANCE));
241   SetEye (Center().Translated (aCenter2Eye));
242 }
243
244 // =======================================================================
245 // function : Distance
246 // purpose  :
247 // =======================================================================
248 Standard_Real Graphic3d_Camera::Distance() const
249 {
250   return myEye.Distance (myCenter);
251 }
252
253 // =======================================================================
254 // function : SetDirection
255 // purpose  :
256 // =======================================================================
257 void Graphic3d_Camera::SetDirection (const gp_Dir& theDir)
258 {
259   if (Direction().IsEqual (theDir, 0.0))
260   {
261     return;
262   }
263
264   gp_Vec aScaledDir (theDir);
265   aScaledDir.Scale (Distance());
266   aScaledDir.Reverse();
267   SetEye (Center().Translated (aScaledDir));
268 }
269
270 // =======================================================================
271 // function : Direction
272 // purpose  :
273 // =======================================================================
274 gp_Dir Graphic3d_Camera::Direction() const
275 {
276   return gp_Dir (gp_Vec (myEye, myCenter));
277 }
278
279 // =======================================================================
280 // function : SetScale
281 // purpose  :
282 // =======================================================================
283 void Graphic3d_Camera::SetScale (const Standard_Real theScale)
284 {
285   if (Scale() == theScale)
286   {
287     return;
288   }
289
290   myScale = theScale;
291
292   switch (myProjType)
293   {
294     case Projection_Perspective  :
295     case Projection_Stereo       :
296     case Projection_MonoLeftEye  :
297     case Projection_MonoRightEye :
298     {
299       Standard_Real aDistance = theScale * 0.5 / myFOVyTan;
300       SetDistance (aDistance);
301     }
302
303     default :
304       break;
305   }
306
307   InvalidateProjection();
308 }
309
310 // =======================================================================
311 // function : Scale
312 // purpose  :
313 // =======================================================================
314 Standard_Real Graphic3d_Camera::Scale() const
315 {
316   switch (myProjType)
317   {
318     case Projection_Orthographic :
319       return myScale;
320
321     // case Projection_Perspective  :
322     // case Projection_Stereo       :
323     // case Projection_MonoLeftEye  :
324     // case Projection_MonoRightEye :
325     default :
326       return Distance() * 2.0 * myFOVyTan;
327   }
328 }
329
330 // =======================================================================
331 // function : SetProjectionType
332 // purpose  :
333 // =======================================================================
334 void Graphic3d_Camera::SetProjectionType (const Projection theProjectionType)
335 {
336   Projection anOldType = ProjectionType();
337
338   if (anOldType == theProjectionType)
339   {
340     return;
341   }
342
343   if (anOldType == Projection_Orthographic)
344   {
345     if (myZNear <= RealEpsilon())
346     {
347       myZNear = DEFAULT_ZNEAR;
348     }
349     if (myZFar <= RealEpsilon())
350     {
351       myZFar = DEFAULT_ZFAR;
352     }
353   }
354
355   myProjType = theProjectionType;
356
357   InvalidateProjection();
358 }
359
360 // =======================================================================
361 // function : SetFOVy
362 // purpose  :
363 // =======================================================================
364 void Graphic3d_Camera::SetFOVy (const Standard_Real theFOVy)
365 {
366   if (FOVy() == theFOVy)
367   {
368     return;
369   }
370
371   myFOVy = theFOVy;
372   myFOVyTan = Tan(DTR_HALF * myFOVy);
373
374   InvalidateProjection();
375 }
376
377 // =======================================================================
378 // function : SetZRange
379 // purpose  :
380 // =======================================================================
381 void Graphic3d_Camera::SetZRange (const Standard_Real theZNear,
382                                   const Standard_Real theZFar)
383 {
384   Standard_ASSERT_RAISE (theZFar > theZNear, "ZFar should be greater than ZNear");
385   if (!IsOrthographic())
386   {
387     Standard_ASSERT_RAISE (theZNear > 0.0, "Only positive Z-Near is allowed for perspective camera");
388     Standard_ASSERT_RAISE (theZFar  > 0.0, "Only positive Z-Far is allowed for perspective camera");
389   }
390
391   if (ZNear() == theZNear
392    && ZFar () == theZFar)
393   {
394     return;
395   }
396
397   myZNear = theZNear;
398   myZFar  = theZFar;
399
400   InvalidateProjection();
401 }
402
403 // =======================================================================
404 // function : SetAspect
405 // purpose  :
406 // =======================================================================
407 void Graphic3d_Camera::SetAspect (const Standard_Real theAspect)
408 {
409   if (Aspect() == theAspect)
410   {
411     return;
412   }
413
414   myAspect = theAspect;
415
416   InvalidateProjection();
417 }
418
419 // =======================================================================
420 // function : SetZFocus
421 // purpose  :
422 // =======================================================================
423 void Graphic3d_Camera::SetZFocus(const FocusType theType, const Standard_Real theZFocus)
424 {
425   if (ZFocusType() == theType
426    && ZFocus    () == theZFocus)
427   {
428     return;
429   }
430
431   myZFocusType = theType;
432   myZFocus     = theZFocus;
433
434   InvalidateProjection();
435 }
436
437 // =======================================================================
438 // function : SetIOD
439 // purpose  :
440 // =======================================================================
441 void Graphic3d_Camera::SetIOD (const IODType theType, const Standard_Real theIOD)
442 {
443   if (GetIODType() == theType
444    && IOD       () == theIOD)
445   {
446     return;
447   }
448
449   myIODType = theType;
450   myIOD     = theIOD;
451
452   InvalidateProjection();
453 }
454
455 // =======================================================================
456 // function : SetTile
457 // purpose  :
458 // =======================================================================
459 void Graphic3d_Camera::SetTile (const Graphic3d_CameraTile& theTile)
460 {
461   if (myTile == theTile)
462   {
463     return;
464   }
465
466   myTile = theTile;
467   InvalidateProjection();
468 }
469
470 // =======================================================================
471 // function : OrthogonalizeUp
472 // purpose  :
473 // =======================================================================
474 void Graphic3d_Camera::OrthogonalizeUp()
475 {
476   SetUp (OrthogonalizedUp());
477 }
478
479 // =======================================================================
480 // function : OrthogonalizedUp
481 // purpose  :
482 // =======================================================================
483 gp_Dir Graphic3d_Camera::OrthogonalizedUp() const
484 {
485   gp_Dir aDir  = Direction();
486   gp_Dir aLeft = aDir.Crossed (Up());
487
488   // recompute up as: up = left x direction
489   return aLeft.Crossed (aDir);
490 }
491
492 // =======================================================================
493 // function : Transform
494 // purpose  :
495 // =======================================================================
496 void Graphic3d_Camera::Transform (const gp_Trsf& theTrsf)
497 {
498   if (theTrsf.Form() == gp_Identity)
499   {
500     return;
501   }
502
503   SetUp     (myUp.Transformed (theTrsf));
504   SetEye    (myEye.Transformed (theTrsf));
505   SetCenter (myCenter.Transformed (theTrsf));
506 }
507
508 // =======================================================================
509 // function : safePointCast
510 // purpose  :
511 // =======================================================================
512 static Graphic3d_Vec4d safePointCast (const gp_Pnt& thePnt)
513 {
514   Standard_Real aLim = 1e15f;
515
516   // have to deal with values greater then max float
517   gp_Pnt aSafePoint = thePnt;
518   const Standard_Real aBigFloat = aLim * 0.1f;
519   if (Abs (aSafePoint.X()) > aLim)
520     aSafePoint.SetX (aSafePoint.X() >= 0 ? aBigFloat : -aBigFloat);
521   if (Abs (aSafePoint.Y()) > aLim)
522     aSafePoint.SetY (aSafePoint.Y() >= 0 ? aBigFloat : -aBigFloat);
523   if (Abs (aSafePoint.Z()) > aLim)
524     aSafePoint.SetZ (aSafePoint.Z() >= 0 ? aBigFloat : -aBigFloat);
525
526   // convert point
527   Graphic3d_Vec4d aPnt (aSafePoint.X(), aSafePoint.Y(), aSafePoint.Z(), 1.0);
528
529   return aPnt;
530 }
531
532 // =======================================================================
533 // function : Project
534 // purpose  :
535 // =======================================================================
536 gp_Pnt Graphic3d_Camera::Project (const gp_Pnt& thePnt) const
537 {
538   const Graphic3d_Mat4d& aViewMx = OrientationMatrix();
539   const Graphic3d_Mat4d& aProjMx = ProjectionMatrix();
540
541   // use compatible type of point
542   Graphic3d_Vec4d aPnt = safePointCast (thePnt);
543
544   aPnt = aViewMx * aPnt; // convert to view coordinate space
545   aPnt = aProjMx * aPnt; // convert to projection coordinate space
546
547   const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
548
549   return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
550 }
551
552 // =======================================================================
553 // function : UnProject
554 // purpose  :
555 // =======================================================================
556 gp_Pnt Graphic3d_Camera::UnProject (const gp_Pnt& thePnt) const
557 {
558   const Graphic3d_Mat4d& aViewMx = OrientationMatrix();
559   const Graphic3d_Mat4d& aProjMx = ProjectionMatrix();
560
561   Graphic3d_Mat4d aInvView;
562   Graphic3d_Mat4d aInvProj;
563
564   // this case should never happen
565   if (!aViewMx.Inverted (aInvView) || !aProjMx.Inverted (aInvProj))
566   {
567     return gp_Pnt (0.0, 0.0, 0.0);
568   }
569
570   // use compatible type of point
571   Graphic3d_Vec4d aPnt = safePointCast (thePnt);
572
573   aPnt = aInvProj * aPnt; // convert to view coordinate space
574   aPnt = aInvView * aPnt; // convert to world coordinate space
575
576   const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
577
578   return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
579 }
580
581 // =======================================================================
582 // function : ConvertView2Proj
583 // purpose  :
584 // =======================================================================
585 gp_Pnt Graphic3d_Camera::ConvertView2Proj (const gp_Pnt& thePnt) const
586 {
587   const Graphic3d_Mat4d& aProjMx = ProjectionMatrix();
588
589   // use compatible type of point
590   Graphic3d_Vec4d aPnt = safePointCast (thePnt);
591
592   aPnt = aProjMx * aPnt; // convert to projection coordinate space
593
594   const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
595
596   return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
597 }
598
599 // =======================================================================
600 // function : ConvertProj2View
601 // purpose  :
602 // =======================================================================
603 gp_Pnt Graphic3d_Camera::ConvertProj2View (const gp_Pnt& thePnt) const
604 {
605   const Graphic3d_Mat4d& aProjMx = ProjectionMatrix();
606
607   Graphic3d_Mat4d aInvProj;
608
609   // this case should never happen, but...
610   if (!aProjMx.Inverted (aInvProj))
611   {
612     return gp_Pnt (0, 0, 0);
613   }
614
615   // use compatible type of point
616   Graphic3d_Vec4d aPnt = safePointCast (thePnt);
617
618   aPnt = aInvProj * aPnt; // convert to view coordinate space
619
620   const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
621
622   return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
623 }
624
625 // =======================================================================
626 // function : ConvertWorld2View
627 // purpose  :
628 // =======================================================================
629 gp_Pnt Graphic3d_Camera::ConvertWorld2View (const gp_Pnt& thePnt) const
630 {
631   const Graphic3d_Mat4d& aViewMx = OrientationMatrix();
632
633   // use compatible type of point
634   Graphic3d_Vec4d aPnt = safePointCast (thePnt);
635
636   aPnt = aViewMx * aPnt; // convert to view coordinate space
637
638   const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
639
640   return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
641 }
642
643 // =======================================================================
644 // function : ConvertView2World
645 // purpose  :
646 // =======================================================================
647 gp_Pnt Graphic3d_Camera::ConvertView2World (const gp_Pnt& thePnt) const
648 {
649   const Graphic3d_Mat4d& aViewMx = OrientationMatrix();
650
651   Graphic3d_Mat4d aInvView;
652
653   if (!aViewMx.Inverted (aInvView))
654   {
655     return gp_Pnt(0, 0, 0);
656   }
657
658   // use compatible type of point
659   Graphic3d_Vec4d aPnt = safePointCast (thePnt);
660
661   aPnt = aInvView * aPnt; // convert to world coordinate space
662
663   const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
664
665   return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
666 }
667
668 // =======================================================================
669 // function : ViewDimensions
670 // purpose  :
671 // =======================================================================
672 gp_XYZ Graphic3d_Camera::ViewDimensions (const Standard_Real theZValue) const
673 {
674   // view plane dimensions
675   Standard_Real aSize = IsOrthographic() ? myScale : (2.0 * theZValue * myFOVyTan);
676   Standard_Real aSizeX, aSizeY;
677   if (myAspect > 1.0)
678   {
679     aSizeX = aSize * myAspect;
680     aSizeY = aSize;
681   }
682   else
683   {
684     aSizeX = aSize;
685     aSizeY = aSize / myAspect;
686   }
687
688   // and frustum depth
689   return gp_XYZ (aSizeX, aSizeY, myZFar - myZNear);
690 }
691
692 // =======================================================================
693 // function : Frustum
694 // purpose  :
695 // =======================================================================
696 void Graphic3d_Camera::Frustum (gp_Pln& theLeft,
697                                 gp_Pln& theRight,
698                                 gp_Pln& theBottom,
699                                 gp_Pln& theTop,
700                                 gp_Pln& theNear,
701                                 gp_Pln& theFar) const
702 {
703   gp_Vec aProjection = gp_Vec (Direction());
704   gp_Vec anUp        = OrthogonalizedUp();
705   gp_Vec aSide       = aProjection ^ anUp;
706
707   Standard_ASSERT_RAISE (
708     !aProjection.IsParallel (anUp, Precision::Angular()),
709      "Can not derive SIDE = PROJ x UP - directions are parallel");
710
711   theNear = gp_Pln (Eye().Translated (aProjection * ZNear()), aProjection);
712   theFar  = gp_Pln (Eye().Translated (aProjection * ZFar()), -aProjection);
713
714   Standard_Real aHScaleHor = 0.0, aHScaleVer = 0.0;
715   if (Aspect() >= 1.0)
716   {
717     aHScaleHor = Scale() * 0.5 * Aspect();
718     aHScaleVer = Scale() * 0.5;
719   }
720   else
721   {
722     aHScaleHor = Scale() * 0.5;
723     aHScaleVer = Scale() * 0.5 / Aspect();
724   }
725
726   gp_Pnt aPntLeft   = Center().Translated (aHScaleHor * -aSide);
727   gp_Pnt aPntRight  = Center().Translated (aHScaleHor *  aSide);
728   gp_Pnt aPntBottom = Center().Translated (aHScaleVer * -anUp);
729   gp_Pnt aPntTop    = Center().Translated (aHScaleVer *  anUp);
730
731   gp_Vec aDirLeft   =  aSide;
732   gp_Vec aDirRight  = -aSide;
733   gp_Vec aDirBottom =  anUp;
734   gp_Vec aDirTop    = -anUp;
735   if (!IsOrthographic())
736   {
737     Standard_Real aHFOVHor = ATan (Tan (DTR_HALF * FOVy()) * Aspect());
738     Standard_Real aHFOVVer = DTR_HALF * FOVy();
739     aDirLeft.Rotate   (gp_Ax1 (gp::Origin(), anUp),   aHFOVHor);
740     aDirRight.Rotate  (gp_Ax1 (gp::Origin(), anUp),  -aHFOVHor);
741     aDirBottom.Rotate (gp_Ax1 (gp::Origin(), aSide), -aHFOVVer);
742     aDirTop.Rotate    (gp_Ax1 (gp::Origin(), aSide),  aHFOVVer);
743   }
744
745   theLeft   = gp_Pln (aPntLeft,   aDirLeft);
746   theRight  = gp_Pln (aPntRight,  aDirRight);
747   theBottom = gp_Pln (aPntBottom, aDirBottom);
748   theTop    = gp_Pln (aPntTop,    aDirTop);
749 }
750
751 // =======================================================================
752 // function : OrientationMatrix
753 // purpose  :
754 // =======================================================================
755 const Graphic3d_Mat4d& Graphic3d_Camera::OrientationMatrix() const
756 {
757   return UpdateOrientation (myMatricesD).Orientation;
758 }
759
760 // =======================================================================
761 // function : OrientationMatrixF
762 // purpose  :
763 // =======================================================================
764 const Graphic3d_Mat4& Graphic3d_Camera::OrientationMatrixF() const
765 {
766   return UpdateOrientation (myMatricesF).Orientation;
767 }
768
769 // =======================================================================
770 // function : ProjectionMatrix
771 // purpose  :
772 // =======================================================================
773 const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionMatrix() const
774 {
775   return UpdateProjection (myMatricesD).MProjection;
776 }
777
778 // =======================================================================
779 // function : ProjectionMatrixF
780 // purpose  :
781 // =======================================================================
782 const Graphic3d_Mat4& Graphic3d_Camera::ProjectionMatrixF() const
783 {
784   return UpdateProjection (myMatricesF).MProjection;
785 }
786
787 // =======================================================================
788 // function : ProjectionStereoLeft
789 // purpose  :
790 // =======================================================================
791 const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionStereoLeft() const
792 {
793   return UpdateProjection (myMatricesD).LProjection;
794 }
795
796 // =======================================================================
797 // function : ProjectionStereoLeftF
798 // purpose  :
799 // =======================================================================
800 const Graphic3d_Mat4& Graphic3d_Camera::ProjectionStereoLeftF() const
801 {
802   return UpdateProjection (myMatricesF).LProjection;
803 }
804
805 // =======================================================================
806 // function : ProjectionStereoRight
807 // purpose  :
808 // =======================================================================
809 const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionStereoRight() const
810 {
811   return UpdateProjection (myMatricesD).RProjection;
812 }
813
814 // =======================================================================
815 // function : ProjectionStereoRightF
816 // purpose  :
817 // =======================================================================
818 const Graphic3d_Mat4& Graphic3d_Camera::ProjectionStereoRightF() const
819 {
820   return UpdateProjection (myMatricesF).RProjection;
821 }
822
823 // =======================================================================
824 // function : UpdateProjection
825 // purpose  :
826 // =======================================================================
827 template <typename Elem_t>
828 Graphic3d_Camera::TransformMatrices<Elem_t>&
829   Graphic3d_Camera::UpdateProjection (TransformMatrices<Elem_t>& theMatrices) const
830 {
831   if (theMatrices.IsProjectionValid())
832   {
833     return theMatrices; // for inline accessors
834   }
835
836   theMatrices.InitProjection();
837
838   // sets top of frustum based on FOVy and near clipping plane
839   Elem_t aScale   = static_cast<Elem_t> (myScale);
840   Elem_t aZNear   = static_cast<Elem_t> (myZNear);
841   Elem_t aZFar    = static_cast<Elem_t> (myZFar);
842   Elem_t anAspect = static_cast<Elem_t> (myAspect);
843   Elem_t aDXHalf = 0.0, aDYHalf = 0.0;
844   if (IsOrthographic())
845   {
846     aDXHalf = aScale * Elem_t (0.5);
847     aDYHalf = aScale * Elem_t (0.5);
848   }
849   else
850   {
851     aDXHalf = aZNear * Elem_t (myFOVyTan);
852     aDYHalf = aZNear * Elem_t (myFOVyTan);
853   }
854
855   if (anAspect > 1.0)
856   {
857     aDXHalf *= anAspect;
858   }
859   else
860   {
861     aDYHalf /= anAspect;
862   }
863
864   // sets right of frustum based on aspect ratio
865   Elem_t aLeft   = -aDXHalf;
866   Elem_t aRight  =  aDXHalf;
867   Elem_t aBot    = -aDYHalf;
868   Elem_t aTop    =  aDYHalf;
869
870   Elem_t aIOD  = myIODType == IODType_Relative 
871     ? static_cast<Elem_t> (myIOD * Distance())
872     : static_cast<Elem_t> (myIOD);
873
874   Elem_t aFocus = myZFocusType == FocusType_Relative 
875     ? static_cast<Elem_t> (myZFocus * Distance())
876     : static_cast<Elem_t> (myZFocus);
877
878   if (myTile.IsValid())
879   {
880     const Elem_t aDXFull = Elem_t(2) * aDXHalf;
881     const Elem_t aDYFull = Elem_t(2) * aDYHalf;
882     const Graphic3d_Vec2i anOffset = myTile.OffsetLowerLeft();
883     aLeft  = -aDXHalf + aDXFull * static_cast<Elem_t> (anOffset.x())                       / static_cast<Elem_t> (myTile.TotalSize.x());
884     aRight = -aDXHalf + aDXFull * static_cast<Elem_t> (anOffset.x() + myTile.TileSize.x()) / static_cast<Elem_t> (myTile.TotalSize.x());
885     aBot   = -aDYHalf + aDYFull * static_cast<Elem_t> (anOffset.y())                       / static_cast<Elem_t> (myTile.TotalSize.y());
886     aTop   = -aDYHalf + aDYFull * static_cast<Elem_t> (anOffset.y() + myTile.TileSize.y()) / static_cast<Elem_t> (myTile.TotalSize.y());
887   }
888
889   switch (myProjType)
890   {
891     case Projection_Orthographic :
892       OrthoProj (aLeft, aRight, aBot, aTop, aZNear, aZFar, theMatrices.MProjection);
893       break;
894
895     case Projection_Perspective :
896       PerspectiveProj (aLeft, aRight, aBot, aTop, aZNear, aZFar, theMatrices.MProjection);
897       break;
898
899     case Projection_MonoLeftEye :
900     {
901       StereoEyeProj (aLeft, aRight, aBot, aTop,
902                      aZNear, aZFar, aIOD, aFocus,
903                      Standard_True, theMatrices.MProjection);
904       theMatrices.LProjection = theMatrices.MProjection;
905       break;
906     }
907
908     case Projection_MonoRightEye :
909     {
910       StereoEyeProj (aLeft, aRight, aBot, aTop,
911                      aZNear, aZFar, aIOD, aFocus,
912                      Standard_False, theMatrices.MProjection);
913       theMatrices.RProjection = theMatrices.MProjection;
914       break;
915     }
916
917     case Projection_Stereo :
918     {
919       PerspectiveProj (aLeft, aRight, aBot, aTop, aZNear, aZFar, theMatrices.MProjection);
920
921       StereoEyeProj (aLeft, aRight, aBot, aTop,
922                      aZNear, aZFar, aIOD, aFocus,
923                      Standard_True,
924                      theMatrices.LProjection);
925
926       StereoEyeProj (aLeft, aRight, aBot, aTop,
927                      aZNear, aZFar, aIOD, aFocus,
928                      Standard_False,
929                      theMatrices.RProjection);
930       break;
931     }
932   }
933
934   return theMatrices; // for inline accessors
935 }
936
937 // =======================================================================
938 // function : UpdateOrientation
939 // purpose  :
940 // =======================================================================
941 template <typename Elem_t>
942 Graphic3d_Camera::TransformMatrices<Elem_t>&
943   Graphic3d_Camera::UpdateOrientation (TransformMatrices<Elem_t>& theMatrices) const
944 {
945   if (theMatrices.IsOrientationValid())
946   {
947     return theMatrices; // for inline accessors
948   }
949
950   theMatrices.InitOrientation();
951
952   NCollection_Vec3<Elem_t> anEye (static_cast<Elem_t> (myEye.X()),
953                                   static_cast<Elem_t> (myEye.Y()),
954                                   static_cast<Elem_t> (myEye.Z()));
955
956   NCollection_Vec3<Elem_t> aCenter (static_cast<Elem_t> (myCenter.X()),
957                                     static_cast<Elem_t> (myCenter.Y()),
958                                     static_cast<Elem_t> (myCenter.Z()));
959
960   NCollection_Vec3<Elem_t> anUp (static_cast<Elem_t> (myUp.X()),
961                                  static_cast<Elem_t> (myUp.Y()),
962                                  static_cast<Elem_t> (myUp.Z()));
963
964   NCollection_Vec3<Elem_t> anAxialScale (static_cast<Elem_t> (myAxialScale.X()),
965                                          static_cast<Elem_t> (myAxialScale.Y()),
966                                          static_cast<Elem_t> (myAxialScale.Z()));
967
968   LookOrientation (anEye, aCenter, anUp, anAxialScale, theMatrices.Orientation);
969
970   return theMatrices; // for inline accessors
971 }
972
973 // =======================================================================
974 // function : InvalidateProjection
975 // purpose  :
976 // =======================================================================
977 void Graphic3d_Camera::InvalidateProjection()
978 {
979   myMatricesD.ResetProjection();
980   myMatricesF.ResetProjection();
981   myWorldViewProjState.ProjectionState() = (Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER);
982 }
983
984 // =======================================================================
985 // function : InvalidateOrientation
986 // purpose  :
987 // =======================================================================
988 void Graphic3d_Camera::InvalidateOrientation()
989 {
990   myMatricesD.ResetOrientation();
991   myMatricesF.ResetOrientation();
992   myWorldViewProjState.WorldViewState() = (Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER);
993 }
994
995 // =======================================================================
996 // function : OrthoProj
997 // purpose  :
998 // =======================================================================
999 template <typename Elem_t>
1000 void Graphic3d_Camera::OrthoProj (const Elem_t theLeft,
1001                                   const Elem_t theRight,
1002                                   const Elem_t theBottom,
1003                                   const Elem_t theTop,
1004                                   const Elem_t theNear,
1005                                   const Elem_t theFar,
1006                                   NCollection_Mat4<Elem_t>& theOutMx)
1007 {
1008   // row 0
1009   theOutMx.ChangeValue (0, 0) = Elem_t (2.0) / (theRight - theLeft);
1010   theOutMx.ChangeValue (0, 1) = Elem_t (0.0);
1011   theOutMx.ChangeValue (0, 2) = Elem_t (0.0);
1012   theOutMx.ChangeValue (0, 3) = - (theRight + theLeft) / (theRight - theLeft);
1013
1014   // row 1
1015   theOutMx.ChangeValue (1, 0) = Elem_t (0.0);
1016   theOutMx.ChangeValue (1, 1) = Elem_t (2.0) / (theTop - theBottom);
1017   theOutMx.ChangeValue (1, 2) = Elem_t (0.0);
1018   theOutMx.ChangeValue (1, 3) = - (theTop + theBottom) / (theTop - theBottom);
1019
1020   // row 2
1021   theOutMx.ChangeValue (2, 0) = Elem_t (0.0);
1022   theOutMx.ChangeValue (2, 1) = Elem_t (0.0);
1023   theOutMx.ChangeValue (2, 2) = Elem_t (-2.0) / (theFar - theNear);
1024   theOutMx.ChangeValue (2, 3) = - (theFar + theNear) / (theFar - theNear);
1025
1026   // row 3
1027   theOutMx.ChangeValue (3, 0) = Elem_t (0.0);
1028   theOutMx.ChangeValue (3, 1) = Elem_t (0.0);
1029   theOutMx.ChangeValue (3, 2) = Elem_t (0.0);
1030   theOutMx.ChangeValue (3, 3) = Elem_t (1.0);
1031 }
1032
1033 // =======================================================================
1034 // function : PerspectiveProj
1035 // purpose  :
1036 // =======================================================================
1037 template <typename Elem_t>
1038 void Graphic3d_Camera::PerspectiveProj (const Elem_t theLeft,
1039                                         const Elem_t theRight,
1040                                         const Elem_t theBottom,
1041                                         const Elem_t theTop,
1042                                         const Elem_t theNear,
1043                                         const Elem_t theFar,
1044                                         NCollection_Mat4<Elem_t>& theOutMx)
1045 {
1046   // column 0
1047   theOutMx.ChangeValue (0, 0) = (Elem_t (2.0) * theNear) / (theRight - theLeft);
1048   theOutMx.ChangeValue (1, 0) = Elem_t (0.0);
1049   theOutMx.ChangeValue (2, 0) = Elem_t (0.0);
1050   theOutMx.ChangeValue (3, 0) = Elem_t (0.0);
1051
1052   // column 1
1053   theOutMx.ChangeValue (0, 1) = Elem_t (0.0);
1054   theOutMx.ChangeValue (1, 1) = (Elem_t (2.0) * theNear) / (theTop - theBottom);
1055   theOutMx.ChangeValue (2, 1) = Elem_t (0.0);
1056   theOutMx.ChangeValue (3, 1) = Elem_t (0.0);
1057
1058   // column 2
1059   theOutMx.ChangeValue (0, 2) = (theRight + theLeft) / (theRight - theLeft);
1060   theOutMx.ChangeValue (1, 2) = (theTop + theBottom) / (theTop - theBottom);
1061   theOutMx.ChangeValue (2, 2) = -(theFar + theNear) / (theFar - theNear);
1062   theOutMx.ChangeValue (3, 2) = Elem_t (-1.0);
1063
1064   // column 3
1065   theOutMx.ChangeValue (0, 3) = Elem_t (0.0);
1066   theOutMx.ChangeValue (1, 3) = Elem_t (0.0);
1067   theOutMx.ChangeValue (2, 3) = -(Elem_t (2.0) * theFar * theNear) / (theFar - theNear);
1068   theOutMx.ChangeValue (3, 3) = Elem_t (0.0);
1069 }
1070
1071 // =======================================================================
1072 // function : StereoEyeProj
1073 // purpose  :
1074 // =======================================================================
1075 template <typename Elem_t>
1076 void Graphic3d_Camera::StereoEyeProj (const Elem_t theLeft,
1077                                       const Elem_t theRight,
1078                                       const Elem_t theBottom,
1079                                       const Elem_t theTop,
1080                                       const Elem_t theNear,
1081                                       const Elem_t theFar,
1082                                       const Elem_t theIOD,
1083                                       const Elem_t theZFocus,
1084                                       const Standard_Boolean theIsLeft,
1085                                       NCollection_Mat4<Elem_t>& theOutMx)
1086 {
1087   Elem_t aDx = theIsLeft ? Elem_t (0.5) * theIOD : Elem_t (-0.5) * theIOD;
1088   Elem_t aDXStereoShift = aDx * theNear / theZFocus;
1089
1090   // construct eye projection matrix
1091   PerspectiveProj (theLeft  + aDXStereoShift,
1092                    theRight + aDXStereoShift,
1093                    theBottom, theTop, theNear, theFar,
1094                    theOutMx);
1095
1096   if (theIOD != Elem_t (0.0))
1097   {
1098     // X translation to cancel parallax
1099     theOutMx.Translate (NCollection_Vec3<Elem_t> (aDx, Elem_t (0.0), Elem_t (0.0)));
1100   }
1101 }
1102
1103 // =======================================================================
1104 // function : LookOrientation
1105 // purpose  :
1106 // =======================================================================
1107 template <typename Elem_t>
1108 void Graphic3d_Camera::LookOrientation (const NCollection_Vec3<Elem_t>& theEye,
1109                                         const NCollection_Vec3<Elem_t>& theLookAt,
1110                                         const NCollection_Vec3<Elem_t>& theUpDir,
1111                                         const NCollection_Vec3<Elem_t>& theAxialScale,
1112                                         NCollection_Mat4<Elem_t>& theOutMx)
1113 {
1114   NCollection_Vec3<Elem_t> aForward = theLookAt - theEye;
1115   aForward.Normalize();
1116
1117   // side = forward x up
1118   NCollection_Vec3<Elem_t> aSide = NCollection_Vec3<Elem_t>::Cross (aForward, theUpDir);
1119   aSide.Normalize();
1120
1121   // recompute up as: up = side x forward
1122   NCollection_Vec3<Elem_t> anUp = NCollection_Vec3<Elem_t>::Cross (aSide, aForward);
1123
1124   NCollection_Mat4<Elem_t> aLookMx;
1125   aLookMx.SetRow (0, aSide);
1126   aLookMx.SetRow (1, anUp);
1127   aLookMx.SetRow (2, -aForward);
1128
1129   theOutMx.InitIdentity();
1130   theOutMx.Multiply (aLookMx);
1131   theOutMx.Translate (-theEye);
1132
1133   NCollection_Mat4<Elem_t> anAxialScaleMx;
1134   anAxialScaleMx.ChangeValue (0, 0) = theAxialScale.x();
1135   anAxialScaleMx.ChangeValue (1, 1) = theAxialScale.y();
1136   anAxialScaleMx.ChangeValue (2, 2) = theAxialScale.z();
1137
1138   theOutMx.Multiply (anAxialScaleMx);
1139 }
1140
1141 //=============================================================================
1142 //function : ZFitAll
1143 //purpose  :
1144 //=============================================================================
1145 bool Graphic3d_Camera::ZFitAll (const Standard_Real theScaleFactor,
1146                                 const Bnd_Box&      theMinMax,
1147                                 const Bnd_Box&      theGraphicBB,
1148                                 Standard_Real&      theZNear,
1149                                 Standard_Real&      theZFar) const
1150 {
1151   Standard_ASSERT_RAISE (theScaleFactor > 0.0, "Zero or negative scale factor is not allowed.");
1152
1153   // Method changes zNear and zFar parameters of camera so as to fit graphical structures
1154   // by their graphical boundaries. It precisely fits min max boundaries of primary application
1155   // objects (second argument), while it can sacrifice the real graphical boundaries of the
1156   // scene with infinite or helper objects (third argument) for the sake of perspective projection.
1157   if (theGraphicBB.IsVoid())
1158   {
1159     theZNear = DEFAULT_ZNEAR;
1160     theZFar  = DEFAULT_ZFAR;
1161     return false;
1162   }
1163
1164   // Measure depth of boundary points from camera eye.
1165   NCollection_Sequence<gp_Pnt> aPntsToMeasure;
1166
1167   Standard_Real aGraphicBB[6];
1168   theGraphicBB.Get (aGraphicBB[0], aGraphicBB[1], aGraphicBB[2], aGraphicBB[3], aGraphicBB[4], aGraphicBB[5]);
1169
1170   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[1], aGraphicBB[2]));
1171   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[1], aGraphicBB[5]));
1172   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[4], aGraphicBB[2]));
1173   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[4], aGraphicBB[5]));
1174   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[1], aGraphicBB[2]));
1175   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[1], aGraphicBB[5]));
1176   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[4], aGraphicBB[2]));
1177   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[4], aGraphicBB[5]));
1178
1179   Standard_Boolean isFiniteMinMax = !theMinMax.IsVoid() && !theMinMax.IsWhole();
1180
1181   if (isFiniteMinMax)
1182   {
1183     Standard_Real aMinMax[6];
1184     theMinMax.Get (aMinMax[0], aMinMax[1], aMinMax[2], aMinMax[3], aMinMax[4], aMinMax[5]);
1185
1186     aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[1], aMinMax[2]));
1187     aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[1], aMinMax[5]));
1188     aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[4], aMinMax[2]));
1189     aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[4], aMinMax[5]));
1190     aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[1], aMinMax[2]));
1191     aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[1], aMinMax[5]));
1192     aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[4], aMinMax[2]));
1193     aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[4], aMinMax[5]));
1194   }
1195
1196   // Camera eye plane.
1197   gp_Dir aCamDir = Direction();
1198   gp_Pnt aCamEye = myEye;
1199   gp_Pln aCamPln (aCamEye, aCamDir);
1200
1201   Standard_Real aModelMinDist = RealLast();
1202   Standard_Real aModelMaxDist = RealFirst();
1203   Standard_Real aGraphMinDist = RealLast();
1204   Standard_Real aGraphMaxDist = RealFirst();
1205
1206   const gp_XYZ& anAxialScale = myAxialScale;
1207
1208   // Get minimum and maximum distances to the eye plane.
1209   Standard_Integer aCounter = 0;
1210   NCollection_Sequence<gp_Pnt>::Iterator aPntIt(aPntsToMeasure);
1211   for (; aPntIt.More(); aPntIt.Next())
1212   {
1213     gp_Pnt aMeasurePnt = aPntIt.Value();
1214
1215     aMeasurePnt = gp_Pnt (aMeasurePnt.X() * anAxialScale.X(),
1216                           aMeasurePnt.Y() * anAxialScale.Y(),
1217                           aMeasurePnt.Z() * anAxialScale.Z());
1218
1219     Standard_Real aDistance = aCamPln.Distance (aMeasurePnt);
1220
1221     // Check if the camera is intruded into the scene.
1222     if (aCamDir.IsOpposite (gp_Vec (aCamEye, aMeasurePnt), M_PI * 0.5))
1223     {
1224       aDistance *= -1;
1225     }
1226
1227     // The first eight points are from theGraphicBB, the last eight points are from theMinMax (can be absent).
1228     Standard_Real& aChangeMinDist = aCounter >= 8 ? aModelMinDist : aGraphMinDist;
1229     Standard_Real& aChangeMaxDist = aCounter >= 8 ? aModelMaxDist : aGraphMaxDist;
1230     aChangeMinDist = Min (aDistance, aChangeMinDist);
1231     aChangeMaxDist = Max (aDistance, aChangeMaxDist);
1232     aCounter++;
1233   }
1234
1235   // Compute depth of bounding box center.
1236   Standard_Real aMidDepth  = (aGraphMinDist + aGraphMaxDist) * 0.5;
1237   Standard_Real aHalfDepth = (aGraphMaxDist - aGraphMinDist) * 0.5;
1238
1239   // Compute enlarged or shrank near and far z ranges.
1240   Standard_Real aZNear  = aMidDepth - aHalfDepth * theScaleFactor;
1241   Standard_Real aZFar   = aMidDepth + aHalfDepth * theScaleFactor;
1242
1243   if (!IsOrthographic())
1244   {
1245     // Everything is behind the perspective camera.
1246     if (aZFar < zEpsilon())
1247     {
1248       theZNear = DEFAULT_ZNEAR;
1249       theZFar  = DEFAULT_ZFAR;
1250       return false;
1251     }
1252   }
1253
1254   //
1255   // Consider clipping errors due to double to single precision floating-point conversion.
1256   //
1257
1258   // Model to view transformation performs translation of points against eye position
1259   // in three dimensions. Both point coordinate and eye position values are converted from
1260   // double to single precision floating point numbers producing conversion errors. 
1261   // Epsilon (Mod) * 3.0 should safely compensate precision error for z coordinate after
1262   // translation assuming that the:
1263   // Epsilon (Eye.Mod()) * 3.0 > Epsilon (Eye.X()) + Epsilon (Eye.Y()) + Epsilon (Eye.Z()).
1264   Standard_Real aEyeConf = 3.0 * zEpsilon (myEye.XYZ().Modulus());
1265
1266   // Model to view transformation performs rotation of points according to view direction.
1267   // New z coordinate is computed as a multiplication of point's x, y, z coordinates by the
1268   // "forward" direction vector's x, y, z coordinates. Both point's and "z" direction vector's
1269   // values are converted from double to single precision floating point numbers producing
1270   // conversion errors.
1271   // Epsilon (Mod) * 6.0 should safely compensate the precision errors for the multiplication
1272   // of point coordinates by direction vector.
1273   gp_Pnt aGraphicMin = theGraphicBB.CornerMin();
1274   gp_Pnt aGraphicMax = theGraphicBB.CornerMax();
1275
1276   Standard_Real aModelConf = 6.0 * zEpsilon (aGraphicMin.XYZ().Modulus()) +
1277                              6.0 * zEpsilon (aGraphicMax.XYZ().Modulus());
1278
1279   // Compensate floating point conversion errors by increasing zNear, zFar to avoid clipping.
1280   aZNear -= zEpsilon (aZNear) + aEyeConf + aModelConf;
1281   aZFar  += zEpsilon (aZFar)  + aEyeConf + aModelConf;
1282
1283   if (!IsOrthographic())
1284   {
1285     // For perspective projection, the value of z in normalized device coordinates is non-linear
1286     // function of eye z coordinate. For fixed-point depth representation resolution of z in
1287     // model-view space will grow towards zFar plane and its scale depends mostly on how far is zNear
1288     // against camera's eye. The purpose of the code below is to select most appropriate zNear distance
1289     // to balance between clipping (less zNear, more chances to observe closely small models without clipping)
1290     // and resolution of depth. A well applicable criteria to this is a ratio between resolution of z at center
1291     // of model boundaries and the distance to that center point. The ratio is chosen empirically and validated
1292     // by tests database. It is considered to be ~0.001 (0.1%) for 24 bit depth buffer, for less depth bitness
1293     // the zNear will be placed similarly giving lower resolution.
1294     // Approximation of the formula for respectively large z range is:
1295     // zNear = [z * (1 + k) / (k * c)],
1296     // where:
1297     // z - distance to center of model boundaries;
1298     // k - chosen ratio, c - capacity of depth buffer;
1299     // k = 0.001, k * c = 1677.216, (1 + k) / (k * c) ~ 5.97E-4
1300     //
1301     // The function uses center of model boundaries computed from "theMinMax" boundaries (instead of using real
1302     // graphical boundaries of all displayed objects). That means that it can sacrifice resolution of presentation
1303     // of non primary ("infinite") application graphical objects in favor of better perspective projection of the
1304     // small applicative objects measured with "theMinMax" values.
1305     Standard_Real aZRange   = isFiniteMinMax ? aModelMaxDist - aModelMinDist : aGraphMaxDist - aGraphMinDist;
1306     Standard_Real aZMin     = isFiniteMinMax ? aModelMinDist : aGraphMinDist;
1307     Standard_Real aZ        = aZMin < 0 ? aZRange / 2.0 : aZRange / 2.0 + aZMin;
1308     Standard_Real aZNearMin = aZ * 5.97E-4;
1309     if (aZNear < aZNearMin)
1310     {
1311       // Clip zNear according to the minimum value matching the quality.
1312       aZNear = aZNearMin;
1313     }
1314     else
1315     {
1316       // Compensate zNear conversion errors for perspective projection.
1317       aZNear -= aZFar * zEpsilon (aZNear) / (aZFar - zEpsilon (aZNear));
1318     }
1319
1320     // Compensate zFar conversion errors for perspective projection.
1321     aZFar += zEpsilon (aZFar);
1322
1323     // Ensure that after all the zNear is not a negative value.
1324     if (aZNear < zEpsilon())
1325     {
1326       aZNear = zEpsilon();
1327     }
1328   }
1329
1330   theZNear = aZNear;
1331   theZFar  = aZFar;
1332   return true;
1333 }
1334
1335 //=============================================================================
1336 //function : Interpolate
1337 //purpose  :
1338 //=============================================================================
1339 template<>
1340 Standard_EXPORT void NCollection_Lerp<Handle(Graphic3d_Camera)>::Interpolate (const double theT,
1341                                                                               Handle(Graphic3d_Camera)& theCamera) const
1342 {
1343   if (Abs (theT - 1.0) < Precision::Confusion())
1344   {
1345     // just copy end-point transformation
1346     theCamera->Copy (myEnd);
1347     return;
1348   }
1349
1350   theCamera->Copy (myStart);
1351   if (Abs (theT - 0.0) < Precision::Confusion())
1352   {
1353     return;
1354   }
1355
1356   // apply rotation
1357   {
1358     gp_Ax3 aCamStart = cameraToAx3 (*myStart);
1359     gp_Ax3 aCamEnd   = cameraToAx3 (*myEnd);
1360     gp_Trsf aTrsfStart, aTrsfEnd;
1361     aTrsfStart.SetTransformation (aCamStart, gp::XOY());
1362     aTrsfEnd  .SetTransformation (aCamEnd,   gp::XOY());
1363
1364     gp_Quaternion aRotStart = aTrsfStart.GetRotation();
1365     gp_Quaternion aRotEnd   = aTrsfEnd  .GetRotation();
1366     gp_Quaternion aRotDelta = aRotEnd * aRotStart.Inverted();
1367     gp_Quaternion aRot = gp_QuaternionNLerp::Interpolate (gp_Quaternion(), aRotDelta, theT);
1368     gp_Trsf aTrsfRot;
1369     aTrsfRot.SetRotation (aRot);
1370     theCamera->Transform (aTrsfRot);
1371   }
1372
1373   // apply translation
1374   {
1375     gp_XYZ aCenter  = NCollection_Lerp<gp_XYZ>::Interpolate (myStart->Center().XYZ(), myEnd->Center().XYZ(), theT);
1376     gp_XYZ anEye    = NCollection_Lerp<gp_XYZ>::Interpolate (myStart->Eye().XYZ(),    myEnd->Eye().XYZ(),    theT);
1377     gp_XYZ anAnchor = aCenter;
1378     Standard_Real aKc = 0.0;
1379
1380     const Standard_Real aDeltaCenter = myStart->Center().Distance (myEnd->Center());
1381     const Standard_Real aDeltaEye    = myStart->Eye()   .Distance (myEnd->Eye());
1382     if (aDeltaEye <= gp::Resolution())
1383     {
1384       anAnchor = anEye;
1385       aKc = 1.0;
1386     }
1387     else if (aDeltaCenter > gp::Resolution())
1388     {
1389       aKc = aDeltaCenter / (aDeltaCenter + aDeltaEye);
1390
1391       const gp_XYZ anAnchorStart = NCollection_Lerp<gp_XYZ>::Interpolate (myStart->Center().XYZ(), myStart->Eye().XYZ(), aKc);
1392       const gp_XYZ anAnchorEnd   = NCollection_Lerp<gp_XYZ>::Interpolate (myEnd  ->Center().XYZ(), myEnd  ->Eye().XYZ(), aKc);
1393       anAnchor = NCollection_Lerp<gp_XYZ>::Interpolate (anAnchorStart, anAnchorEnd, theT);
1394     }
1395
1396     const gp_Vec        aDirEyeToCenter     = theCamera->Direction();
1397     const Standard_Real aDistEyeCenterStart = myStart->Eye().Distance (myStart->Center());
1398     const Standard_Real aDistEyeCenterEnd   = myEnd  ->Eye().Distance (myEnd  ->Center());
1399     const Standard_Real aDistEyeCenter      = NCollection_Lerp<Standard_Real>::Interpolate (aDistEyeCenterStart, aDistEyeCenterEnd, theT);
1400     aCenter = anAnchor + aDirEyeToCenter.XYZ() * aDistEyeCenter * aKc;
1401     anEye   = anAnchor - aDirEyeToCenter.XYZ() * aDistEyeCenter * (1.0 - aKc);
1402
1403     theCamera->SetCenter (aCenter);
1404     theCamera->SetEye    (anEye);
1405   }
1406
1407   // apply scaling
1408   if (Abs(myStart->Scale() - myEnd->Scale()) > Precision::Confusion()
1409    && myStart->IsOrthographic())
1410   {
1411     const Standard_Real aScale = NCollection_Lerp<Standard_Real>::Interpolate (myStart->Scale(), myEnd->Scale(), theT);
1412     theCamera->SetScale (aScale);
1413   }
1414 }
1415
1416 //=======================================================================
1417 //function : FrustumPoints
1418 //purpose  :
1419 //=======================================================================
1420 void Graphic3d_Camera::FrustumPoints (NCollection_Array1<Graphic3d_Vec3d>& thePoints) const
1421 {
1422   if (thePoints.Length() != FrustumVerticesNB)
1423   {
1424     thePoints.Resize (0, FrustumVerticesNB, Standard_False);
1425   }
1426
1427   const Graphic3d_Mat4d& aProjectionMat = ProjectionMatrix();
1428   const Graphic3d_Mat4d& aWorldViewMat  = OrientationMatrix();
1429
1430   Standard_Real nLeft = 0.0, nRight = 0.0, nTop = 0.0, nBottom = 0.0;
1431   Standard_Real fLeft = 0.0, fRight = 0.0, fTop = 0.0, fBottom = 0.0;
1432   Standard_Real aNear = 0.0, aFar = 0.0;
1433   if (!IsOrthographic())
1434   {
1435     // handle perspective projection
1436     aNear = aProjectionMat.GetValue (2, 3) / (-1.0 + aProjectionMat.GetValue (2, 2));
1437     aFar  = aProjectionMat.GetValue (2, 3) / ( 1.0 + aProjectionMat.GetValue (2, 2));
1438     // Near plane
1439     nLeft   = aNear * (aProjectionMat.GetValue (0, 2) - 1.0) / aProjectionMat.GetValue (0, 0);
1440     nRight  = aNear * (aProjectionMat.GetValue (0, 2) + 1.0) / aProjectionMat.GetValue (0, 0);
1441     nTop    = aNear * (aProjectionMat.GetValue (1, 2) + 1.0) / aProjectionMat.GetValue (1, 1);
1442     nBottom = aNear * (aProjectionMat.GetValue (1, 2) - 1.0) / aProjectionMat.GetValue (1, 1);
1443     // Far plane
1444     fLeft   = aFar  * (aProjectionMat.GetValue (0, 2) - 1.0) / aProjectionMat.GetValue (0, 0);
1445     fRight  = aFar  * (aProjectionMat.GetValue (0, 2) + 1.0) / aProjectionMat.GetValue (0, 0);
1446     fTop    = aFar  * (aProjectionMat.GetValue (1, 2) + 1.0) / aProjectionMat.GetValue (1, 1);
1447     fBottom = aFar  * (aProjectionMat.GetValue (1, 2) - 1.0) / aProjectionMat.GetValue (1, 1);
1448   }
1449   else
1450   {
1451     // handle orthographic projection
1452     aNear = (1.0 / aProjectionMat.GetValue (2, 2)) * (aProjectionMat.GetValue (2, 3) + 1.0);
1453     aFar  = (1.0 / aProjectionMat.GetValue (2, 2)) * (aProjectionMat.GetValue (2, 3) - 1.0);
1454     // Near plane
1455     nLeft   = ( 1.0 + aProjectionMat.GetValue (0, 3)) / (-aProjectionMat.GetValue (0, 0));
1456     fLeft   = nLeft;
1457     nRight  = ( 1.0 - aProjectionMat.GetValue (0, 3)) /   aProjectionMat.GetValue (0, 0);
1458     fRight  = nRight;
1459     nTop    = ( 1.0 - aProjectionMat.GetValue (1, 3)) /   aProjectionMat.GetValue (1, 1);
1460     fTop    = nTop;
1461     nBottom = (-1.0 - aProjectionMat.GetValue (1, 3)) /   aProjectionMat.GetValue (1, 1);
1462     fBottom = nBottom;
1463   }
1464
1465   Graphic3d_Vec4d aLeftTopNear     (nLeft,  nTop,    -aNear, 1.0), aRightBottomFar (fRight, fBottom, -aFar, 1.0);
1466   Graphic3d_Vec4d aLeftBottomNear  (nLeft,  nBottom, -aNear, 1.0), aRightTopFar    (fRight, fTop,    -aFar, 1.0);
1467   Graphic3d_Vec4d aRightBottomNear (nRight, nBottom, -aNear, 1.0), aLeftTopFar     (fLeft,  fTop,    -aFar, 1.0);
1468   Graphic3d_Vec4d aRightTopNear    (nRight, nTop,    -aNear, 1.0), aLeftBottomFar  (fLeft,  fBottom, -aFar, 1.0);
1469
1470   Graphic3d_Mat4d anInvWorldView;
1471   aWorldViewMat.Inverted (anInvWorldView);
1472
1473   Graphic3d_Vec4d aTmpPnt;
1474   aTmpPnt = anInvWorldView * aLeftTopNear;
1475   thePoints.SetValue (FrustumVert_LeftTopNear,     aTmpPnt.xyz() / aTmpPnt.w());
1476   aTmpPnt = anInvWorldView * aRightBottomFar;
1477   thePoints.SetValue (FrustumVert_RightBottomFar,  aTmpPnt.xyz() / aTmpPnt.w());
1478   aTmpPnt = anInvWorldView * aLeftBottomNear;
1479   thePoints.SetValue (FrustumVert_LeftBottomNear,  aTmpPnt.xyz() / aTmpPnt.w());
1480   aTmpPnt = anInvWorldView * aRightTopFar;
1481   thePoints.SetValue (FrustumVert_RightTopFar,     aTmpPnt.xyz() / aTmpPnt.w());
1482   aTmpPnt = anInvWorldView * aRightBottomNear;
1483   thePoints.SetValue (FrustumVert_RightBottomNear, aTmpPnt.xyz() / aTmpPnt.w());
1484   aTmpPnt = anInvWorldView * aLeftTopFar;
1485   thePoints.SetValue (FrustumVert_LeftTopFar,      aTmpPnt.xyz() / aTmpPnt.w());
1486   aTmpPnt = anInvWorldView * aRightTopNear;
1487   thePoints.SetValue (FrustumVert_RightTopNear,    aTmpPnt.xyz() / aTmpPnt.w());
1488   aTmpPnt = anInvWorldView * aLeftBottomFar;
1489   thePoints.SetValue (FrustumVert_LeftBottomFar,   aTmpPnt.xyz() / aTmpPnt.w());
1490 }