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