0030058: Visualization, Select3D_SensitivePrimitiveArray - the selection is not fast...
[occt.git] / src / SelectMgr / SelectMgr_RectangularFrustum.cxx
index a59892e..d972e19 100644 (file)
 
 #include <SelectMgr_RectangularFrustum.hxx>
 
-#define DOT(A, B) (A.x() * B.x() + A.y() * B.y() + A.z() * B.z())
-#define DOTp(A, B) (A.x() * B.X() + A.y() * B.Y() + A.z() * B.Z())
-#define DISTANCE(A, B) (std::sqrt ((A.x() - B.x()) * (A.x() - B.x()) + (A.y() - B.y()) * (A.y() - B.y()) + (A.z() - B.z()) * (A.z() - B.z())))
-#define DISTANCEp(A, B) (std::sqrt ((A.x() - B.X()) * (A.x() - B.X()) + (A.y() - B.Y()) * (A.y() - B.Y()) + (A.z() - B.Z()) * (A.z() - B.Z())))
-
 // =======================================================================
 // function : segmentSegmentDistance
 // purpose  :
 // =======================================================================
 void SelectMgr_RectangularFrustum::segmentSegmentDistance (const gp_Pnt& theSegPnt1,
                                                            const gp_Pnt& theSegPnt2,
-                                                           Standard_Real& theDepth)
+                                                           SelectBasics_PickResult& thePickResult) const
 {
-  SelectMgr_Vec3 anU = SelectMgr_Vec3 (theSegPnt2.X() - theSegPnt1.X(),
-                                       theSegPnt2.Y() - theSegPnt1.Y(),
-                                       theSegPnt2.Z() - theSegPnt1.Z());
-  SelectMgr_Vec3 aV = myViewRayDir;
-  SelectMgr_Vec3 aW = SelectMgr_Vec3 (theSegPnt1.X() - myNearPickedPnt.x(),
-                                      theSegPnt1.Y() - myNearPickedPnt.y(),
-                                      theSegPnt1.Z() - myNearPickedPnt.z());
-  Standard_Real anA = DOT (anU, anU);
-  Standard_Real aB = DOT (anU, aV);
-  Standard_Real aC = DOT (aV, aV);
-  Standard_Real aD = DOT (anU, aW);
-  Standard_Real anE = DOT (aV, aW);
+  gp_XYZ anU = theSegPnt2.XYZ() - theSegPnt1.XYZ();
+  gp_XYZ aV = myViewRayDir.XYZ();
+  gp_XYZ aW = theSegPnt1.XYZ() - myNearPickedPnt.XYZ();
+
+  Standard_Real anA = anU.Dot (anU);
+  Standard_Real aB = anU.Dot (aV);
+  Standard_Real aC = aV.Dot (aV);
+  Standard_Real aD = anU.Dot (aW);
+  Standard_Real anE = aV.Dot (aW);
   Standard_Real aCoef = anA * aC - aB * aB;
-  Standard_Real aSc, aSn, aSd = aCoef;
+  Standard_Real aSn = aCoef;
   Standard_Real aTc, aTn, aTd = aCoef;
 
-  if (aCoef < Precision::Confusion())
+  if (aCoef < gp::Resolution())
   {
-    aSn = 0.0;
-    aSd = 1.0;
     aTn = anE;
     aTd = aC;
   }
@@ -60,13 +50,11 @@ void SelectMgr_RectangularFrustum::segmentSegmentDistance (const gp_Pnt& theSegP
     aTn = (anA * anE - aB * aD);
     if (aSn < 0.0)
     {
-      aSn = 0.0;
       aTn = anE;
       aTd = aC;
     }
-    else if (aSn > aSd)
+    else if (aSn > aCoef)
     {
-      aSn = aSd;
       aTn = anE + aB;
       aTd = aC;
     }
@@ -75,197 +63,238 @@ void SelectMgr_RectangularFrustum::segmentSegmentDistance (const gp_Pnt& theSegP
   if (aTn < 0.0)
   {
     aTn = 0.0;
-    if (-aD < 0.0)
-      aSn = 0.0;
-    else if (-aD > anA)
-      aSn = aSd;
-    else {
-      aSn = -aD;
-      aSd = anA;
-    }
   }
   else if (aTn > aTd)
   {
     aTn = aTd;
-    if ((-aD + aB) < 0.0)
-      aSn = 0;
-    else if ((-aD + aB) > anA)
-      aSn = aSd;
-    else {
-      aSn = (-aD +  aB);
-      aSd = anA;
-    }
   }
-  aSc = (Abs (aSn) < Precision::Confusion() ? 0.0 : aSn / aSd);
-  aTc = (Abs (aTn) < Precision::Confusion() ? 0.0 : aTn / aTd);
+  aTc = (Abs (aTd) < gp::Resolution() ? 0.0 : aTn / aTd);
+
+  const gp_Pnt aClosestPnt = myNearPickedPnt.XYZ() + myViewRayDir.XYZ() * aTc;
+  thePickResult.SetDepth (myNearPickedPnt.Distance (aClosestPnt) * myScale);
+
+  const gp_Vec aPickedVec = aClosestPnt.XYZ() - theSegPnt1.XYZ();
+  const gp_Vec aFigureVec = theSegPnt2.XYZ()  - theSegPnt1.XYZ();
+  const Standard_Real aPickedVecMod = aPickedVec.Magnitude();
+  const Standard_Real aFigureVecMod = aFigureVec.Magnitude();
+  if (aPickedVecMod <= gp::Resolution()
+   || aFigureVecMod <= gp::Resolution())
+  {
+    thePickResult.SetPickedPoint (aClosestPnt);
+    return;
+  }
 
-  SelectMgr_Vec3 aDiff = aW + (anU * aSc) - (aV * aTc);
-  SelectMgr_Vec3 aClosestPnt = myNearPickedPnt + myViewRayDir * aTc;
-  theDepth = DISTANCE (myNearPickedPnt, aClosestPnt);
+  const Standard_Real aCosOfAngle = aFigureVec.Dot (aPickedVec) / (aPickedVecMod * aFigureVecMod);
+  const Standard_Real aSegPntShift = Min(aFigureVecMod, Max(0.0, aCosOfAngle * aPickedVecMod));
+  thePickResult.SetPickedPoint (theSegPnt1.XYZ() + aFigureVec.XYZ() * (aSegPntShift / aFigureVecMod));
 }
 
 // =======================================================================
 // function : segmentPlaneIntersection
 // purpose  :
 // =======================================================================
-void SelectMgr_RectangularFrustum::segmentPlaneIntersection (const SelectMgr_Vec3& thePlane,
+bool SelectMgr_RectangularFrustum::segmentPlaneIntersection (const gp_Vec& thePlane,
                                                              const gp_Pnt& thePntOnPlane,
-                                                             Standard_Real& theDepth)
+                                                             SelectBasics_PickResult& thePickResult) const
 {
-  SelectMgr_Vec3 anU = myViewRayDir;
-  SelectMgr_Vec3 aW = SelectMgr_Vec3 (myNearPickedPnt.x() - thePntOnPlane.X(),
-                                      myNearPickedPnt.y() - thePntOnPlane.Y(),
-                                      myNearPickedPnt.z() - thePntOnPlane.Z());
-  Standard_Real aD = DOT (thePlane, anU);
-  Standard_Real aN = -DOT (thePlane, aW);
+  gp_XYZ anU = myViewRayDir.XYZ();
+  gp_XYZ aW = myNearPickedPnt.XYZ() - thePntOnPlane.XYZ();
+  Standard_Real aD = thePlane.Dot (anU);
+  Standard_Real aN = -thePlane.Dot (aW);
 
   if (Abs (aD) < Precision::Confusion())
   {
     if (Abs (aN) < Precision::Angular())
     {
-      theDepth = DBL_MAX;
-      return;
+      thePickResult.Invalidate();
+      return false;
     }
     else
     {
-      theDepth = DBL_MAX;
-      return;
+      thePickResult.Invalidate();
+      return false;
     }
   }
 
   Standard_Real aParam = aN / aD;
   if (aParam < 0.0 || aParam > 1.0)
   {
-    theDepth = DBL_MAX;
-    return;
+    thePickResult.Invalidate();
+    return false;
+  }
+
+  gp_Pnt aClosestPnt = myNearPickedPnt.XYZ() + anU * aParam;
+  thePickResult.SetDepth (myNearPickedPnt.Distance (aClosestPnt) * myScale);
+  return true;
+}
+
+namespace
+{
+  // =======================================================================
+  // function : computeFrustum
+  // purpose  : Computes base frustum data: its vertices and edge directions
+  // =======================================================================
+  void computeFrustum (const gp_Pnt2d theMinPnt, const gp_Pnt2d& theMaxPnt,
+                       const Handle(SelectMgr_FrustumBuilder)& theBuilder,
+                       gp_Pnt* theVertices, gp_Vec* theEdges)
+  {
+    // LeftTopNear
+    theVertices[0] = theBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
+                                                        theMaxPnt.Y(),
+                                                        0.0);
+    // LeftTopFar
+    theVertices[1] = theBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
+                                                        theMaxPnt.Y(),
+                                                        1.0);
+    // LeftBottomNear
+    theVertices[2] = theBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
+                                                        theMinPnt.Y(),
+                                                        0.0);
+    // LeftBottomFar
+    theVertices[3] = theBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
+                                                        theMinPnt.Y(),
+                                                        1.0);
+    // RightTopNear
+    theVertices[4] = theBuilder->ProjectPntOnViewPlane (theMaxPnt.X(),
+                                                        theMaxPnt.Y(),
+                                                        0.0);
+    // RightTopFar
+    theVertices[5] = theBuilder->ProjectPntOnViewPlane (theMaxPnt.X(),
+                                                        theMaxPnt.Y(),
+                                                        1.0);
+    // RightBottomNear
+    theVertices[6] = theBuilder->ProjectPntOnViewPlane (theMaxPnt.X(),
+                                                        theMinPnt.Y(),
+                                                        0.0);
+    // RightBottomFar
+    theVertices[7] = theBuilder->ProjectPntOnViewPlane (theMaxPnt.X(),
+                                                        theMinPnt.Y(),
+                                                        1.0);
+
+    // Horizontal
+    theEdges[0] = theVertices[4].XYZ() - theVertices[0].XYZ();
+    // Vertical
+    theEdges[1] = theVertices[2].XYZ() - theVertices[0].XYZ();
+    // LeftLower
+    theEdges[2] = theVertices[2].XYZ() - theVertices[3].XYZ();
+    // RightLower
+    theEdges[3] = theVertices[6].XYZ() - theVertices[7].XYZ();
+    // LeftUpper
+    theEdges[4] = theVertices[0].XYZ() - theVertices[1].XYZ();
+    // RightUpper
+    theEdges[5] = theVertices[4].XYZ() - theVertices[5].XYZ();
   }
 
-  SelectMgr_Vec3 aClosestPnt = myNearPickedPnt + anU * aParam;
-  theDepth = DISTANCE (myNearPickedPnt, aClosestPnt);
+  // =======================================================================
+  // function : computeNormals
+  // purpose  : Computes normals to frustum faces
+  // =======================================================================
+  void computeNormals (const gp_Vec* theEdges, gp_Vec* theNormals)
+  {
+    // Top
+    theNormals[0] = theEdges[0].Crossed (theEdges[4]);
+    // Bottom
+    theNormals[1] = theEdges[2].Crossed (theEdges[0]);
+    // Left
+    theNormals[2] = theEdges[4].Crossed (theEdges[1]);
+    // Right
+    theNormals[3] = theEdges[1].Crossed (theEdges[5]);
+    // Near
+    theNormals[4] = theEdges[0].Crossed (theEdges[1]);
+    // Far
+    theNormals[5] = -theNormals[4];
+  }
 }
 
 // =======================================================================
-// function : Build
-// purpose  : Build volume according to the point and given pixel
-//            tolerance
+// function : cacheVertexProjections
+// purpose  : Caches projection of frustum's vertices onto its plane directions
+//            and {i, j, k}
 // =======================================================================
-void SelectMgr_RectangularFrustum::Build (const gp_Pnt2d &thePoint)
+void SelectMgr_RectangularFrustum::cacheVertexProjections (SelectMgr_RectangularFrustum* theFrustum) const
 {
-  myNearPickedPnt = myBuilder->ProjectPntOnViewPlane (thePoint.X(), thePoint.Y(), 0.0);
-  myFarPickedPnt  = myBuilder->ProjectPntOnViewPlane (thePoint.X(), thePoint.Y(), 1.0);
-  myViewRayDir = myFarPickedPnt - myNearPickedPnt;
-
-  // LeftTopNear
-  myVertices[0] = myBuilder->ProjectPntOnViewPlane (thePoint.X() - myPixelTolerance / 2.0,
-                                                    thePoint.Y() + myPixelTolerance / 2.0,
-                                                    0.0);
-  // LeftTopFar
-  myVertices[1] = myBuilder->ProjectPntOnViewPlane (thePoint.X() - myPixelTolerance / 2.0,
-                                                    thePoint.Y() + myPixelTolerance / 2.0,
-                                                    1.0);
-  // LeftBottomNear
-  myVertices[2] = myBuilder->ProjectPntOnViewPlane (thePoint.X() - myPixelTolerance / 2.0,
-                                                    thePoint.Y() - myPixelTolerance / 2.0,
-                                                    0.0);
-  // LeftBottomFar
-  myVertices[3] = myBuilder->ProjectPntOnViewPlane (thePoint.X() - myPixelTolerance / 2.0,
-                                                    thePoint.Y() - myPixelTolerance / 2.0,
-                                                    1.0);
-  // RightTopNear
-  myVertices[4] = myBuilder->ProjectPntOnViewPlane (thePoint.X() + myPixelTolerance / 2.0,
-                                                    thePoint.Y() + myPixelTolerance / 2.0,
-                                                    0.0);
-  // RightTopFar
-  myVertices[5] = myBuilder->ProjectPntOnViewPlane (thePoint.X() + myPixelTolerance / 2.0,
-                                                    thePoint.Y() + myPixelTolerance / 2.0,
-                                                    1.0);
-  // RightBottomNear
-  myVertices[6] = myBuilder->ProjectPntOnViewPlane (thePoint.X() + myPixelTolerance / 2.0,
-                                                    thePoint.Y() - myPixelTolerance / 2.0,
-                                                    0.0);
-  // RightBottomFar
-  myVertices[7] = myBuilder->ProjectPntOnViewPlane (thePoint.X() + myPixelTolerance / 2.0,
-                                                    thePoint.Y() - myPixelTolerance / 2.0,
-                                                    1.0);
-  // Top
-  myPlanes[0] = myBuilder->PlaneEquation (myVertices[1],
-                                          myVertices[0],
-                                          myVertices[5],
-                                          myVertices[6]);
-  // Bottom
-  myPlanes[1] = myBuilder->PlaneEquation (myVertices[3],
-                                          myVertices[2],
-                                          myVertices[7],
-                                          myVertices[4]);
-  // Left
-  myPlanes[2] = myBuilder->PlaneEquation (myVertices[1],
-                                          myVertices[0],
-                                          myVertices[2],
-                                          myVertices[6]);
-  // Right
-  myPlanes[3] = myBuilder->PlaneEquation (myVertices[5],
-                                          myVertices[4],
-                                          myVertices[6],
-                                          myVertices[2]);
-  // Near
-  myPlanes[4] = myBuilder->PlaneEquation (myVertices[4],
-                                          myVertices[6],
-                                          myVertices[2],
-                                          myVertices[3]);
-  // Far
-  myPlanes[5] = myBuilder->PlaneEquation (myVertices[5],
-                                          myVertices[7],
-                                          myVertices[3],
-                                          myVertices[2]);
-
-  for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < 6; ++aPlaneIdx)
+  if (theFrustum->myIsOrthographic)
   {
-    Standard_Real aMax = -DBL_MAX;
-    Standard_Real aMin =  DBL_MAX;
-    const SelectMgr_Vec3 aPlane = myPlanes[aPlaneIdx];
-    for (Standard_Integer aVertIdx = 0; aVertIdx < 8; ++aVertIdx)
+    // project vertices onto frustum normals
+    // Since orthographic view volume's faces are always a pairwise translation of
+    // one another, only 2 vertices that belong to opposite faces can be projected
+    // to simplify calculations.
+    Standard_Integer aVertIdxs[6] = { LeftTopNear, LeftBottomNear,       // opposite planes in height direction
+                                      LeftBottomNear, RightBottomNear,   // opposite planes in width direcion
+                                      LeftBottomFar, RightBottomNear };  // opposite planes in depth direction
+    for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < 5; aPlaneIdx += 2)
     {
-      Standard_Real aProjection = DOT (aPlane, myVertices[aVertIdx]);
-      aMax = Max (aMax, aProjection);
-      aMin = Min (aMin, aProjection);
+      Standard_Real aProj1 = theFrustum->myPlanes[aPlaneIdx].XYZ().Dot (theFrustum->myVertices[aVertIdxs[aPlaneIdx]].XYZ());
+      Standard_Real aProj2 = theFrustum->myPlanes[aPlaneIdx].XYZ().Dot (theFrustum->myVertices[aVertIdxs[aPlaneIdx + 1]].XYZ());
+      theFrustum->myMinVertsProjections[aPlaneIdx] = Min (aProj1, aProj2);
+      theFrustum->myMaxVertsProjections[aPlaneIdx] = Max (aProj1, aProj2);
     }
-    myMaxVertsProjections[aPlaneIdx] = aMax;
-    myMinVertsProjections[aPlaneIdx] = aMin;
   }
-
-  SelectMgr_Vec3 aDimensions[3] =
+  else
   {
-    SelectMgr_Vec3 (1.0, 0.0, 0.0),
-    SelectMgr_Vec3 (0.0, 1.0, 0.0),
-    SelectMgr_Vec3 (0.0, 0.0, 1.0)
-  };
+    // project all vertices onto frustum normals
+    for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < 6; ++aPlaneIdx)
+    {
+      Standard_Real aMax = -DBL_MAX;
+      Standard_Real aMin = DBL_MAX;
+      const gp_XYZ& aPlane = theFrustum->myPlanes[aPlaneIdx].XYZ();
+      for (Standard_Integer aVertIdx = 0; aVertIdx < 8; ++aVertIdx)
+      {
+        Standard_Real aProjection = aPlane.Dot (theFrustum->myVertices[aVertIdx].XYZ());
+        aMin = Min (aMin, aProjection);
+        aMax = Max (aMax, aProjection);
+      }
+      theFrustum->myMinVertsProjections[aPlaneIdx] = aMin;
+      theFrustum->myMaxVertsProjections[aPlaneIdx] = aMax;
+    }
+  }
 
+  // project vertices onto {i, j, k}
   for (Standard_Integer aDim = 0; aDim < 3; ++aDim)
   {
     Standard_Real aMax = -DBL_MAX;
-    Standard_Real aMin =  DBL_MAX;
+    Standard_Real aMin = DBL_MAX;
     for (Standard_Integer aVertIdx = 0; aVertIdx < 8; ++aVertIdx)
     {
-      Standard_Real aProjection = DOT (aDimensions[aDim], myVertices[aVertIdx]);
-      aMax = Max (aProjection, aMax);
-      aMin = Min (aProjection, aMin);
+      const gp_XYZ& aVert = theFrustum->myVertices[aVertIdx].XYZ();
+      aMax = Max (aVert.GetData()[aDim], aMax);
+      aMin = Min (aVert.GetData()[aDim], aMin);
     }
-    myMaxOrthoVertsProjections[aDim] = aMax;
-    myMinOrthoVertsProjections[aDim] = aMin;
+    theFrustum->myMaxOrthoVertsProjections[aDim] = aMax;
+    theFrustum->myMinOrthoVertsProjections[aDim] = aMin;
   }
+}
+
+// =======================================================================
+// function : Build
+// purpose  : Build volume according to the point and given pixel
+//            tolerance
+// =======================================================================
+void SelectMgr_RectangularFrustum::Build (const gp_Pnt2d &thePoint)
+{
+  myNearPickedPnt = myBuilder->ProjectPntOnViewPlane (thePoint.X(), thePoint.Y(), 0.0);
+  myFarPickedPnt = myBuilder->ProjectPntOnViewPlane (thePoint.X(), thePoint.Y(), 1.0);
+  myViewRayDir = myFarPickedPnt.XYZ() - myNearPickedPnt.XYZ();
+  myMousePos = thePoint;
+
+  gp_Pnt2d aMinPnt (thePoint.X() - myPixelTolerance * 0.5,
+                    thePoint.Y() - myPixelTolerance * 0.5);
+  gp_Pnt2d aMaxPnt (thePoint.X() + myPixelTolerance * 0.5,
+                    thePoint.Y() + myPixelTolerance * 0.5);
+
+  // calculate base frustum characteristics: vertices and edge directions
+  computeFrustum (aMinPnt, aMaxPnt, myBuilder, myVertices, myEdgeDirs);
 
-  // Horizontal
-  myEdgeDirs[0] = myVertices[4] - myVertices[0];
-  // Vertical
-  myEdgeDirs[1] = myVertices[2] - myVertices[0];
-  // LeftLower
-  myEdgeDirs[2] = myVertices[2] - myVertices[3];
-  // RightLower
-  myEdgeDirs[3] = myVertices[6] - myVertices[7];
-  // LeftUpper
-  myEdgeDirs[4] = myVertices[0] - myVertices[1];
-  // RightUpper
-  myEdgeDirs[5] = myVertices[4] - myVertices[5];
+  // compute frustum normals
+  computeNormals (myEdgeDirs, myPlanes);
+
+  // compute vertices projections onto frustum normals and
+  // {i, j, k} vectors and store them to corresponding class fields
+  cacheVertexProjections (this);
+
+  myViewClipRange.SetVoid();
+
+  myScale = 1.0;
 }
 
 // =======================================================================
@@ -281,234 +310,115 @@ void SelectMgr_RectangularFrustum::Build (const gp_Pnt2d& theMinPnt,
   myFarPickedPnt = myBuilder->ProjectPntOnViewPlane ((theMinPnt.X() + theMaxPnt.X()) * 0.5,
                                                      (theMinPnt.Y() + theMaxPnt.Y()) * 0.5,
                                                      1.0);
-  myViewRayDir = myFarPickedPnt - myNearPickedPnt;
-
-  // LeftTopNear
-  myVertices[0] = myBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
-                                                    theMaxPnt.Y(),
-                                                    0.0);
-  // LeftTopFar
-  myVertices[1] = myBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
-                                                    theMaxPnt.Y(),
-                                                    1.0);
-  // LeftBottomNear
-  myVertices[2] = myBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
-                                                    theMinPnt.Y(),
-                                                    0.0);
-  // LeftBottomFar
-  myVertices[3] = myBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
-                                                    theMinPnt.Y(),
-                                                    1.0);
-  // RightTopNear
-  myVertices[4] = myBuilder->ProjectPntOnViewPlane (theMaxPnt.X(),
-                                                    theMaxPnt.Y(),
-                                                    0.0);
-  // RightTopFar
-  myVertices[5] = myBuilder->ProjectPntOnViewPlane (theMaxPnt.X(),
-                                                    theMaxPnt.Y(),
-                                                    1.0);
-  // RightBottomNear
-  myVertices[6] = myBuilder->ProjectPntOnViewPlane (theMaxPnt.X(),
-                                                    theMinPnt.Y(),
-                                                    0.0);
-  // RightBottomFar
-  myVertices[7] = myBuilder->ProjectPntOnViewPlane (theMaxPnt.X() ,
-                                                    theMinPnt.Y(),
-                                                    1.0);
-
-  // Top
-  myPlanes[0] = myBuilder->PlaneEquation (myVertices[1],
-                                          myVertices[0],
-                                          myVertices[5],
-                                          myVertices[6]);
-  // Bottom
-  myPlanes[1] = myBuilder->PlaneEquation (myVertices[3],
-                                          myVertices[2],
-                                          myVertices[7],
-                                          myVertices[4]);
-  // Left
-  myPlanes[2] = myBuilder->PlaneEquation (myVertices[1],
-                                          myVertices[0],
-                                          myVertices[2],
-                                          myVertices[6]);
-  // Right
-  myPlanes[3] = myBuilder->PlaneEquation (myVertices[5],
-                                          myVertices[4],
-                                          myVertices[6],
-                                          myVertices[2]);
-  // Near
-  myPlanes[4] = myBuilder->PlaneEquation (myVertices[4],
-                                          myVertices[6],
-                                          myVertices[2],
-                                          myVertices[3]);
-  // Far
-  myPlanes[5] = myBuilder->PlaneEquation (myVertices[5],
-                                          myVertices[7],
-                                          myVertices[3],
-                                          myVertices[2]);
+  myViewRayDir = myFarPickedPnt.XYZ() - myNearPickedPnt.XYZ();
 
-  for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < 6; ++aPlaneIdx)
-  {
-    Standard_Real aMax = -DBL_MAX;
-    Standard_Real aMin =  DBL_MAX;
-    const SelectMgr_Vec3 aPlane = myPlanes[aPlaneIdx];
-    for (Standard_Integer aVertIdx = 0; aVertIdx < 8; ++aVertIdx)
-    {
-      Standard_Real aProjection = DOT (aPlane, myVertices[aVertIdx]);
-      aMax = Max (aMax, aProjection);
-      aMin = Min (aMin, aProjection);
-    }
-    myMaxVertsProjections[aPlaneIdx] = aMax;
-    myMinVertsProjections[aPlaneIdx] = aMin;
-  }
+  // calculate base frustum characteristics: vertices and edge directions
+  computeFrustum (theMinPnt, theMaxPnt, myBuilder, myVertices, myEdgeDirs);
 
-  SelectMgr_Vec3 aDimensions[3] =
-  {
-    SelectMgr_Vec3 (1.0, 0.0, 0.0),
-    SelectMgr_Vec3 (0.0, 1.0, 0.0),
-    SelectMgr_Vec3 (0.0, 0.0, 1.0)
-  };
+  // compute frustum normals
+  computeNormals (myEdgeDirs, myPlanes);
 
-  for (Standard_Integer aDim = 0; aDim < 3; ++aDim)
-  {
-    Standard_Real aMax = -DBL_MAX;
-    Standard_Real aMin =  DBL_MAX;
-    for (Standard_Integer aVertIdx = 0; aVertIdx < 8; ++aVertIdx)
-    {
-      Standard_Real aProjection = DOT (aDimensions[aDim], myVertices[aVertIdx]);
-      aMax = Max (aMax, aProjection);
-      aMin = Min (aMin, aProjection);
-    }
-    myMaxOrthoVertsProjections[aDim] = aMax;
-    myMinOrthoVertsProjections[aDim] = aMin;
-  }
+  // compute vertices projections onto frustum normals and
+  // {i, j, k} vectors and store them to corresponding class fields
+  cacheVertexProjections (this);
 
-  // Horizontal
-  myEdgeDirs[0] = myVertices[4] - myVertices[0];
-  // Vertical
-  myEdgeDirs[1] = myVertices[2] - myVertices[0];
-  // LeftLower
-  myEdgeDirs[2] = myVertices[2] - myVertices[3];
-  // RightLower
-  myEdgeDirs[3] = myVertices[6] - myVertices[7];
-  // LeftUpper
-  myEdgeDirs[4] = myVertices[0] - myVertices[1];
-  // RightUpper
-  myEdgeDirs[5] = myVertices[4] - myVertices[5];
+  myViewClipRange.SetVoid();
+
+  myScale = 1.0;
 }
 
 // =======================================================================
-// function : Transform
-// purpose  : Returns a copy of the frustum transformed according to the matrix given
+// function : ScaleAndTransform
+// purpose  : IMPORTANT: Scaling makes sense only for frustum built on a single point!
+//            Note that this method does not perform any checks on type of the frustum.
+//            Returns a copy of the frustum resized according to the scale factor given
+//            and transforms it using the matrix given.
+//            There are no default parameters, but in case if:
+//                - transformation only is needed: @theScaleFactor must be initialized
+//                  as any negative value;
+//                - scale only is needed: @theTrsf must be set to gp_Identity.
 // =======================================================================
-NCollection_Handle<SelectMgr_BaseFrustum> SelectMgr_RectangularFrustum::Transform (const gp_Trsf& theTrsf)
+Handle(SelectMgr_BaseFrustum) SelectMgr_RectangularFrustum::ScaleAndTransform (const Standard_Integer theScaleFactor,
+                                                                               const gp_GTrsf& theTrsf) const
 {
-  SelectMgr_RectangularFrustum* aRes = new SelectMgr_RectangularFrustum();
+  Standard_ASSERT_RAISE (theScaleFactor > 0,
+    "Error! Pixel tolerance for selection should be greater than zero");
 
-  aRes->myNearPickedPnt = SelectMgr_MatOp::Transform (theTrsf, myNearPickedPnt);
-  aRes->myFarPickedPnt  = SelectMgr_MatOp::Transform (theTrsf, myFarPickedPnt);
-  aRes->myViewRayDir    = aRes->myFarPickedPnt - aRes->myNearPickedPnt;
+  Handle(SelectMgr_RectangularFrustum) aRes = new SelectMgr_RectangularFrustum();
+  const Standard_Boolean isToScale = theScaleFactor != 1;
+  const Standard_Boolean isToTrsf  = theTrsf.Form() != gp_Identity;
 
-  aRes->myIsOrthographic = myIsOrthographic;
+  if (!isToScale && !isToTrsf)
+    return aRes;
 
-  // LeftTopNear
-  aRes->myVertices[0] = SelectMgr_MatOp::Transform (theTrsf, myVertices[0]);
-  // LeftTopFar
-  aRes->myVertices[1] = SelectMgr_MatOp::Transform (theTrsf, myVertices[1]);
-  // LeftBottomNear
-  aRes->myVertices[2] = SelectMgr_MatOp::Transform (theTrsf, myVertices[2]);
-  // LeftBottomFar
-  aRes->myVertices[3] = SelectMgr_MatOp::Transform (theTrsf, myVertices[3]);
-  // RightTopNear
-  aRes->myVertices[4] = SelectMgr_MatOp::Transform (theTrsf, myVertices[4]);
-  // RightTopFar
-  aRes->myVertices[5] = SelectMgr_MatOp::Transform (theTrsf, myVertices[5]);
-  // RightBottomNear
-  aRes->myVertices[6] = SelectMgr_MatOp::Transform (theTrsf, myVertices[6]);
-  // RightBottomFar
-  aRes->myVertices[7] = SelectMgr_MatOp::Transform (theTrsf, myVertices[7]);
-
-  // Top
-  aRes->myPlanes[0] = myBuilder->PlaneEquation (aRes->myVertices[1],
-                                                aRes->myVertices[0],
-                                                aRes->myVertices[5],
-                                                aRes->myVertices[6]);
-  // Bottom
-  aRes->myPlanes[1] = myBuilder->PlaneEquation (aRes->myVertices[3],
-                                                aRes->myVertices[2],
-                                                aRes->myVertices[7],
-                                                aRes->myVertices[4]);
-  // Left
-  aRes->myPlanes[2] = myBuilder->PlaneEquation (aRes->myVertices[1],
-                                                aRes->myVertices[0],
-                                                aRes->myVertices[2],
-                                                aRes->myVertices[6]);
-  // Right
-  aRes->myPlanes[3] = myBuilder->PlaneEquation (aRes->myVertices[5],
-                                                aRes->myVertices[4],
-                                                aRes->myVertices[6],
-                                                aRes->myVertices[2]);
-  // Near
-  aRes->myPlanes[4] = myBuilder->PlaneEquation (aRes->myVertices[4],
-                                                aRes->myVertices[6],
-                                                aRes->myVertices[2],
-                                                aRes->myVertices[3]);
-  // Far
-  aRes->myPlanes[5] = myBuilder->PlaneEquation (aRes->myVertices[5],
-                                                aRes->myVertices[7],
-                                                aRes->myVertices[3],
-                                                aRes->myVertices[2]);
+  aRes->myIsOrthographic = myIsOrthographic;
+  const SelectMgr_RectangularFrustum* aRef = this;
 
-  for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < 6; ++aPlaneIdx)
+  if (isToScale)
   {
-    Standard_Real aMax = -DBL_MAX;
-    Standard_Real aMin =  DBL_MAX;
-    const SelectMgr_Vec3 aPlane = aRes->myPlanes[aPlaneIdx];
-    for (Standard_Integer aVertIdx = 0; aVertIdx < 8; ++aVertIdx)
-    {
-      Standard_Real aProjection = DOT (aPlane, aRes->myVertices[aVertIdx]);
-      aMax = Max (aMax, aProjection);
-      aMin = Min (aMin, aProjection);
-    }
-    aRes->myMaxVertsProjections[aPlaneIdx] = aMax;
-    aRes->myMinVertsProjections[aPlaneIdx] = aMin;
+    aRes->myNearPickedPnt = myNearPickedPnt;
+    aRes->myFarPickedPnt  = myFarPickedPnt;
+    aRes->myViewRayDir    = myViewRayDir;
+
+    const gp_Pnt2d aMinPnt (myMousePos.X() - theScaleFactor * 0.5,
+                            myMousePos.Y() - theScaleFactor * 0.5);
+    const gp_Pnt2d aMaxPnt (myMousePos.X() + theScaleFactor * 0.5,
+                            myMousePos.Y() + theScaleFactor * 0.5);
+
+    // recompute base frustum characteristics from scratch
+    computeFrustum (aMinPnt, aMaxPnt, myBuilder, aRes->myVertices, aRes->myEdgeDirs);
+
+    aRef = aRes.get();
   }
 
-  SelectMgr_Vec3 aDimensions[3] =
+  if (isToTrsf)
   {
-    SelectMgr_Vec3 (1.0, 0.0, 0.0),
-    SelectMgr_Vec3 (0.0, 1.0, 0.0),
-    SelectMgr_Vec3 (0.0, 0.0, 1.0)
-  };
+    const Standard_Real aRefScale = aRef->myFarPickedPnt.SquareDistance (aRef->myNearPickedPnt);
 
-  for (Standard_Integer aDim = 0; aDim < 3; ++aDim)
-  {
-    Standard_Real aMax = -DBL_MAX;
-    Standard_Real aMin =  DBL_MAX;
-    for (Standard_Integer aVertIdx = 0; aVertIdx < 8; ++aVertIdx)
+    gp_Pnt aPoint = aRef->myNearPickedPnt;
+    theTrsf.Transforms (aPoint.ChangeCoord());
+    aRes->myNearPickedPnt = aPoint;
+
+    aPoint.SetXYZ (aRef->myFarPickedPnt.XYZ());
+    theTrsf.Transforms (aPoint.ChangeCoord());
+    aRes->myFarPickedPnt = aPoint;
+
+    aRes->myViewRayDir    = aRes->myFarPickedPnt.XYZ() - aRes->myNearPickedPnt.XYZ();
+
+    for (Standard_Integer anIt = 0; anIt < 8; anIt++)
     {
-      Standard_Real aProjection = DOT (aDimensions[aDim], aRes->myVertices[aVertIdx]);
-      aMax = Max (aMax, aProjection);
-      aMin = Min (aMin, aProjection);
+      aPoint = aRef->myVertices[anIt];
+      theTrsf.Transforms (aPoint.ChangeCoord());
+      aRes->myVertices[anIt] = aPoint;
     }
-    aRes->myMaxOrthoVertsProjections[aDim] = aMax;
-    aRes->myMinOrthoVertsProjections[aDim] = aMin;
+
+    // Horizontal
+    aRes->myEdgeDirs[0] = aRes->myVertices[4].XYZ() - aRes->myVertices[0].XYZ();
+    // Vertical
+    aRes->myEdgeDirs[1] = aRes->myVertices[2].XYZ() - aRes->myVertices[0].XYZ();
+    // LeftLower
+    aRes->myEdgeDirs[2] = aRes->myVertices[2].XYZ() - aRes->myVertices[3].XYZ();
+    // RightLower
+    aRes->myEdgeDirs[3] = aRes->myVertices[6].XYZ() - aRes->myVertices[7].XYZ();
+    // LeftUpper
+    aRes->myEdgeDirs[4] = aRes->myVertices[0].XYZ() - aRes->myVertices[1].XYZ();
+    // RightUpper
+    aRes->myEdgeDirs[5] = aRes->myVertices[4].XYZ() - aRes->myVertices[5].XYZ();
+
+    // Compute scale to transform depth from local coordinate system to world coordinate system
+    aRes->myScale = Sqrt (aRefScale / aRes->myFarPickedPnt.SquareDistance (aRes->myNearPickedPnt));
   }
 
-  // Horizontal
-  aRes->myEdgeDirs[0] = aRes->myVertices[4] - aRes->myVertices[0];
-  // Vertical
-  aRes->myEdgeDirs[1] = aRes->myVertices[2] - aRes->myVertices[0];
-  // LeftLower
-  aRes->myEdgeDirs[2] = aRes->myVertices[2] - aRes->myVertices[3];
-  // RightLower
-  aRes->myEdgeDirs[3] = aRes->myVertices[6] - aRes->myVertices[7];
-  // LeftUpper
-  aRes->myEdgeDirs[4] = aRes->myVertices[0] - aRes->myVertices[1];
-  // RightUpper
-  aRes->myEdgeDirs[5] = aRes->myVertices[4] - aRes->myVertices[5];
-
-  return NCollection_Handle<SelectMgr_BaseFrustum> (aRes);
+  // compute frustum normals
+  computeNormals (aRes->myEdgeDirs, aRes->myPlanes);
+
+  cacheVertexProjections (aRes.get());
+
+  aRes->myViewClipRange = myViewClipRange;
+  aRes->myIsViewClipEnabled = myIsViewClipEnabled;
+  aRes->myMousePos      = myMousePos;
+
+  return aRes;
 }
 
 // =======================================================================
@@ -517,10 +427,11 @@ NCollection_Handle<SelectMgr_BaseFrustum> SelectMgr_RectangularFrustum::Transfor
 //            axis-aligned bounding box with minimum corner at point
 //            theMinPnt and maximum at point theMaxPnt
 // =======================================================================
-const Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const SelectMgr_Vec3& theMinPnt,
-                                                               const SelectMgr_Vec3& theMaxPnt)
+Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const SelectMgr_Vec3& theBoxMin,
+                                                         const SelectMgr_Vec3& theBoxMax,
+                                                         Standard_Boolean*     theInside) const
 {
-  return hasOverlap (theMinPnt, theMaxPnt);
+  return hasOverlap (theBoxMin, theBoxMax, theInside);
 }
 
 // =======================================================================
@@ -528,57 +439,66 @@ const Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const SelectMgr_V
 // purpose  : SAT intersection test between defined volume and
 //            given axis-aligned box
 // =======================================================================
-const Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const BVH_Box<Standard_Real, 3>& theBox,
-                                                               Standard_Real& theDepth)
+Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const SelectMgr_Vec3& theBoxMin,
+                                                         const SelectMgr_Vec3& theBoxMax,
+                                                         SelectBasics_PickResult& thePickResult) const
 {
-  const SelectMgr_Vec3& aMinPnt = theBox.CornerMin();
-  const SelectMgr_Vec3& aMaxPnt = theBox.CornerMax();
-  if (!hasOverlap (aMinPnt, aMaxPnt))
+  if (!hasOverlap (theBoxMin, theBoxMax))
     return Standard_False;
 
-  SelectMgr_Vec3 aNearestPnt = SelectMgr_Vec3 (RealLast(), RealLast(), RealLast());
-  aNearestPnt.x() = Max (Min (myNearPickedPnt.x(), aMaxPnt.x()), aMinPnt.x());
-  aNearestPnt.y() = Max (Min (myNearPickedPnt.y(), aMaxPnt.y()), aMinPnt.y());
-  aNearestPnt.z() = Max (Min (myNearPickedPnt.z(), aMaxPnt.z()), aMinPnt.z());
+  gp_Pnt aNearestPnt (RealLast(), RealLast(), RealLast());
+  aNearestPnt.SetX (Max (Min (myNearPickedPnt.X(), theBoxMax.x()), theBoxMin.x()));
+  aNearestPnt.SetY (Max (Min (myNearPickedPnt.Y(), theBoxMax.y()), theBoxMin.y()));
+  aNearestPnt.SetZ (Max (Min (myNearPickedPnt.Z(), theBoxMax.z()), theBoxMin.z()));
 
-  theDepth = DISTANCE (aNearestPnt, myNearPickedPnt);
+  thePickResult.SetDepth (aNearestPnt.Distance (myNearPickedPnt));
 
-  return Standard_True;
+  return isViewClippingOk (thePickResult);
 }
 
 // =======================================================================
 // function : Overlaps
 // purpose  : Intersection test between defined volume and given point
 // =======================================================================
-const Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const gp_Pnt& thePnt,
-                                                               Standard_Real& theDepth)
+Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const gp_Pnt& thePnt,
+                                                         SelectBasics_PickResult& thePickResult) const
 {
   if (!hasOverlap (thePnt))
     return Standard_False;
 
-  SelectMgr_Vec3 aPnt (thePnt.X(), thePnt.Y(), thePnt.Z());
-  SelectMgr_Vec3 aV = aPnt - myNearPickedPnt;
-  SelectMgr_Vec3 aDetectedPnt = myNearPickedPnt + myViewRayDir * (DOT (aV, myViewRayDir) / DOT (myViewRayDir, myViewRayDir));
+  gp_XYZ aV = thePnt.XYZ() - myNearPickedPnt.XYZ();
+  gp_Pnt aDetectedPnt =
+    myNearPickedPnt.XYZ() + myViewRayDir.XYZ() * (aV.Dot (myViewRayDir.XYZ()) / myViewRayDir.Dot (myViewRayDir));
 
-  theDepth = DISTANCE (aDetectedPnt, myNearPickedPnt);
+  thePickResult.SetDepth (aDetectedPnt.Distance (myNearPickedPnt) * myScale);
+  thePickResult.SetPickedPoint (thePnt);
 
-  return Standard_True;
+  return isViewClippingOk (thePickResult);
+}
+
+// =======================================================================
+// function : Overlaps
+// purpose  : Intersection test between defined volume and given point
+// =======================================================================
+Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const gp_Pnt& thePnt) const
+{
+  return hasOverlap (thePnt);
 }
 
 // =======================================================================
 // function : Overlaps
 // purpose  : Checks if line segment overlaps selecting frustum
 // =======================================================================
-const Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const gp_Pnt& thePnt1,
-                                                               const gp_Pnt& thePnt2,
-                                                               Standard_Real& theDepth)
+Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const gp_Pnt& thePnt1,
+                                                         const gp_Pnt& thePnt2,
+                                                         SelectBasics_PickResult& thePickResult) const
 {
-  theDepth = -DBL_MAX;
   if (!hasOverlap (thePnt1, thePnt2))
     return Standard_False;
 
-  segmentSegmentDistance (thePnt1, thePnt2, theDepth);
-  return Standard_True;
+  segmentSegmentDistance (thePnt1, thePnt2, thePickResult);
+
+  return isViewClippingOk (thePickResult);
 }
 
 // =======================================================================
@@ -588,29 +508,26 @@ const Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const gp_Pnt& the
 //            may be considered of interior part or boundary line defined
 //            by segments depending on given sensitivity type
 // =======================================================================
-const Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const Handle(TColgp_HArray1OfPnt)& theArrayOfPnts,
-                                                               Select3D_TypeOfSensitivity theSensType,
-                                                               Standard_Real& theDepth)
+Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const TColgp_Array1OfPnt& theArrayOfPnts,
+                                                         Select3D_TypeOfSensitivity theSensType,
+                                                         SelectBasics_PickResult& thePickResult) const
 {
   if (theSensType == Select3D_TOS_BOUNDARY)
   {
     Standard_Integer aMatchingSegmentsNb = -1;
-    theDepth = DBL_MAX;
-    Standard_Integer aLower = theArrayOfPnts->Lower();
-    Standard_Integer anUpper = theArrayOfPnts->Upper();
-
+    SelectBasics_PickResult aPickResult;
+    thePickResult.Invalidate();
+    const Standard_Integer aLower  = theArrayOfPnts.Lower();
+    const Standard_Integer anUpper = theArrayOfPnts.Upper();
     for (Standard_Integer aPntIter = aLower; aPntIter <= anUpper; ++aPntIter)
     {
-      const gp_Pnt& aStartPnt = theArrayOfPnts->Value (aPntIter);
-      const gp_Pnt& aEndPnt = aPntIter == anUpper ? theArrayOfPnts->Value (aLower)
-        : theArrayOfPnts->Value (aPntIter + 1);
-
+      const gp_Pnt& aStartPnt = theArrayOfPnts.Value (aPntIter);
+      const gp_Pnt& aEndPnt   = theArrayOfPnts.Value (aPntIter == anUpper ? aLower : (aPntIter + 1));
       if (hasOverlap (aStartPnt, aEndPnt))
       {
         aMatchingSegmentsNb++;
-        Standard_Real aSegmentDepth = RealLast();
-        segmentSegmentDistance (aStartPnt, aEndPnt, aSegmentDepth);
-        theDepth = Min (theDepth, aSegmentDepth);
+        segmentSegmentDistance (aStartPnt, aEndPnt, aPickResult);
+        thePickResult = SelectBasics_PickResult::Min (thePickResult, aPickResult);
       }
     }
 
@@ -619,16 +536,17 @@ const Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const Handle(TCol
   }
   else if (theSensType == Select3D_TOS_INTERIOR)
   {
-    SelectMgr_Vec3 aPolyNorm (RealLast());
-    if (!hasOverlap (theArrayOfPnts, aPolyNorm))
+    gp_Vec aPolyNorm (gp_XYZ (RealLast(), RealLast(), RealLast()));
+    if (!hasOverlap (theArrayOfPnts, aPolyNorm)
+     || !segmentPlaneIntersection (aPolyNorm,
+                                   theArrayOfPnts.First(),
+                                   thePickResult))
+    {
       return Standard_False;
-
-    segmentPlaneIntersection (aPolyNorm,
-                              theArrayOfPnts->Value (theArrayOfPnts->Lower()),
-                              theDepth);
+    }
   }
 
-  return Standard_True;
+  return isViewClippingOk (thePickResult);
 }
 
 // =======================================================================
@@ -638,75 +556,82 @@ const Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const Handle(TCol
 //            boundary line defined by triangle vertices depending on
 //            given sensitivity type
 // =======================================================================
-const Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const gp_Pnt& thePnt1,
-                                                               const gp_Pnt& thePnt2,
-                                                               const gp_Pnt& thePnt3,
-                                                               Select3D_TypeOfSensitivity theSensType,
-                                                               Standard_Real& theDepth)
+Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const gp_Pnt& thePnt1,
+                                                         const gp_Pnt& thePnt2,
+                                                         const gp_Pnt& thePnt3,
+                                                         Select3D_TypeOfSensitivity theSensType,
+                                                         SelectBasics_PickResult& thePickResult) const
 {
   if (theSensType == Select3D_TOS_BOUNDARY)
   {
-    Handle(TColgp_HArray1OfPnt) aPntsArray = new TColgp_HArray1OfPnt(1, 4);
-    aPntsArray->SetValue (1, thePnt1);
-    aPntsArray->SetValue (2, thePnt2);
-    aPntsArray->SetValue (3, thePnt3);
-    aPntsArray->SetValue (4, thePnt1);
-    return Overlaps (aPntsArray, Select3D_TOS_BOUNDARY, theDepth);
+    const gp_Pnt aPntsArrayBuf[4] = { thePnt1, thePnt2, thePnt3, thePnt1 };
+    const TColgp_Array1OfPnt aPntsArray (aPntsArrayBuf[0], 1, 4);
+    return Overlaps (aPntsArray, Select3D_TOS_BOUNDARY, thePickResult);
   }
   else if (theSensType == Select3D_TOS_INTERIOR)
   {
-    SelectMgr_Vec3 aTriangleNormal (RealLast());
+    gp_Vec aTriangleNormal (gp_XYZ (RealLast(), RealLast(), RealLast()));
     if (!hasOverlap (thePnt1, thePnt2, thePnt3, aTriangleNormal))
       return Standard_False;
 
     // check if intersection point belongs to triangle's interior part
-    SelectMgr_Vec3 aPnt1 (thePnt1.X(), thePnt1.Y(), thePnt1.Z());
-    SelectMgr_Vec3 aTrEdges[3] = { SelectMgr_Vec3 (thePnt2.X() - thePnt1.X(), thePnt2.Y() - thePnt1.Y(), thePnt2.Z() - thePnt1.Z()),
-                                   SelectMgr_Vec3 (thePnt3.X() - thePnt2.X(), thePnt3.Y() - thePnt2.Y(), thePnt3.Z() - thePnt2.Z()),
-                                   SelectMgr_Vec3 (thePnt1.X() - thePnt3.X(), thePnt1.Y() - thePnt3.Y(), thePnt1.Z() - thePnt3.Z()) };
-    SelectMgr_Vec3 anEdge = (aPnt1 - myNearPickedPnt) * (1.0 / DOT (aTriangleNormal, myViewRayDir));
+    gp_XYZ aTrEdges[3] = { thePnt2.XYZ() - thePnt1.XYZ(),
+                           thePnt3.XYZ() - thePnt2.XYZ(),
+                           thePnt1.XYZ() - thePnt3.XYZ() };
+
+    Standard_Real anAlpha = aTriangleNormal.Dot (myViewRayDir);
+    if (Abs (anAlpha) < gp::Resolution())
+    {
+      // handle degenerated triangles: in this case, there is no possible way to detect overlap correctly.
+      if (aTriangleNormal.SquareMagnitude() < gp::Resolution())
+      {
+        return Standard_False;
+      }
 
-    Standard_Real aTime = DOT (aTriangleNormal, anEdge);
+      // handle the case when triangle normal and selecting frustum direction are orthogonal: for this case, overlap
+      // is detected correctly, and distance to triangle's plane can be measured as distance to its arbitrary vertex.
+      const gp_XYZ aDiff = myNearPickedPnt.XYZ() - thePnt1.XYZ();
+      thePickResult.SetDepth (aTriangleNormal.Dot (aDiff) * myScale);
+      thePickResult.SetPickedPoint (thePnt1);
+      return isViewClippingOk (thePickResult);
+    }
 
-    SelectMgr_Vec3 aVec = SelectMgr_Vec3 (myViewRayDir.y() * anEdge.z() - myViewRayDir.z() * anEdge.y(),
-                                          myViewRayDir.z() * anEdge.x() - myViewRayDir.x() * anEdge.z(),
-                                          myViewRayDir.x() * anEdge.y() - myViewRayDir.y() * anEdge.x());
+    gp_XYZ anEdge = (thePnt1.XYZ() - myNearPickedPnt.XYZ()) * (1.0 / anAlpha);
 
-    Standard_Real anU = DOT (aVec, aTrEdges[2]);
-    Standard_Real aV  = DOT (aVec, aTrEdges[0]);
+    Standard_Real aTime = aTriangleNormal.Dot (anEdge);
 
-    Standard_Boolean isInterior = (aTime >= 0.0) && (anU >= 0.0) && (aV >= 0.0) && (anU + aV <= 1.0);
+    gp_XYZ aVec = myViewRayDir.XYZ().Crossed (anEdge);
 
+    Standard_Real anU = aVec.Dot (aTrEdges[2]);
+    Standard_Real aV  = aVec.Dot (aTrEdges[0]);
+
+    const Standard_Boolean isInterior = (aTime >= 0.0) && (anU >= 0.0) && (aV >= 0.0) && (anU + aV <= 1.0);
+    const gp_Pnt aPtOnPlane = myNearPickedPnt.XYZ() + myViewRayDir.XYZ() * aTime;
     if (isInterior)
     {
-      SelectMgr_Vec3 aDetectedPnt = myNearPickedPnt + myViewRayDir * aTime;
-      theDepth = DISTANCE (myNearPickedPnt, aDetectedPnt);
-      return Standard_True;
+      thePickResult.SetDepth (myNearPickedPnt.Distance (aPtOnPlane) * myScale);
+      thePickResult.SetPickedPoint (aPtOnPlane);
+      return isViewClippingOk (thePickResult);
     }
 
     gp_Pnt aPnts[3] = {thePnt1, thePnt2, thePnt3};
     Standard_Real aMinDist = RealLast();
     Standard_Integer aNearestEdgeIdx = -1;
-    SelectMgr_Vec3 aPtOnPlane = myNearPickedPnt + myViewRayDir * aTime;
     for (Standard_Integer anEdgeIdx = 0; anEdgeIdx < 3; ++anEdgeIdx)
     {
-      SelectMgr_Vec3 aW = SelectMgr_Vec3 (aPtOnPlane.x() - aPnts[anEdgeIdx].X(),
-                                          aPtOnPlane.y() - aPnts[anEdgeIdx].Y(),
-                                          aPtOnPlane.z() - aPnts[anEdgeIdx].Z());
-      Standard_Real aCoef = DOT (aTrEdges[anEdgeIdx], aW) / DOT (aTrEdges[anEdgeIdx], aTrEdges[anEdgeIdx]);
-      Standard_Real aDist = DISTANCE (aPtOnPlane, SelectMgr_Vec3 (aPnts[anEdgeIdx].X() + aCoef * aTrEdges[anEdgeIdx].x(),
-                                                                  aPnts[anEdgeIdx].Y() + aCoef * aTrEdges[anEdgeIdx].y(),
-                                                                  aPnts[anEdgeIdx].Z() + aCoef * aTrEdges[anEdgeIdx].z()));
+      gp_XYZ aW = aPtOnPlane.XYZ() - aPnts[anEdgeIdx].XYZ();
+      Standard_Real aCoef = aTrEdges[anEdgeIdx].Dot (aW) / aTrEdges[anEdgeIdx].Dot (aTrEdges[anEdgeIdx]);
+      Standard_Real aDist = aPtOnPlane.Distance (aPnts[anEdgeIdx].XYZ() + aCoef * aTrEdges[anEdgeIdx]);
       if (aMinDist > aDist)
       {
         aMinDist = aDist;
         aNearestEdgeIdx = anEdgeIdx;
       }
     }
-    segmentSegmentDistance (aPnts[aNearestEdgeIdx], aPnts[(aNearestEdgeIdx + 1) % 3], theDepth);
+    segmentSegmentDistance (aPnts[aNearestEdgeIdx], aPnts[(aNearestEdgeIdx + 1) % 3], thePickResult);
   }
 
-  return Standard_True;
+  return isViewClippingOk (thePickResult);
 }
 
 // =======================================================================
@@ -714,10 +639,9 @@ const Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const gp_Pnt& the
 // purpose  : Measures distance between 3d projection of user-picked
 //            screen point and given point theCOG
 // =======================================================================
-const Standard_Real SelectMgr_RectangularFrustum::DistToGeometryCenter (const gp_Pnt& theCOG)
+Standard_Real SelectMgr_RectangularFrustum::DistToGeometryCenter (const gp_Pnt& theCOG) const
 {
-  const SelectMgr_Vec3& aCOG = SelectMgr_Vec3 (theCOG.X(), theCOG.Y(), theCOG.Z());
-  return DISTANCE (aCOG, myNearPickedPnt);
+  return theCOG.Distance (myNearPickedPnt) * myScale;
 }
 
 // =======================================================================
@@ -725,68 +649,151 @@ const Standard_Real SelectMgr_RectangularFrustum::DistToGeometryCenter (const gp
 // purpose  : Calculates the point on a view ray that was detected during
 //            the run of selection algo by given depth
 // =======================================================================
-SelectMgr_Vec3 SelectMgr_RectangularFrustum::DetectedPoint (const Standard_Real theDepth) const
+gp_Pnt SelectMgr_RectangularFrustum::DetectedPoint (const Standard_Real theDepth) const
 {
-  return myNearPickedPnt + myViewRayDir * theDepth;
+  return myNearPickedPnt.XYZ() + myViewRayDir.Normalized().XYZ() * theDepth / myScale;
 }
 
 // =======================================================================
-// function : IsClipped
-// purpose  : Checks if the point of sensitive in which selection was
-//            detected belongs to the region defined by clipping planes
+// function : computeClippingRange
+// purpose  :
 // =======================================================================
-const Standard_Boolean SelectMgr_RectangularFrustum::IsClipped (const Graphic3d_SequenceOfHClipPlane& thePlanes,
-                                                                const Standard_Real theDepth)
+void SelectMgr_RectangularFrustum::computeClippingRange (const Graphic3d_SequenceOfHClipPlane& thePlanes,
+                                                         SelectMgr_ViewClipRange& theRange) const
 {
-  Graphic3d_SequenceOfHClipPlane::Iterator aPlaneIt (thePlanes);
-  Standard_Real aMaxDepth = DBL_MAX;
-  Standard_Real aMinDepth = -DBL_MAX;
   Standard_Real aPlaneA, aPlaneB, aPlaneC, aPlaneD;
-  for ( ; aPlaneIt.More(); aPlaneIt.Next())
+  for (Graphic3d_SequenceOfHClipPlane::Iterator aPlaneIt (thePlanes); aPlaneIt.More(); aPlaneIt.Next())
   {
     const Handle(Graphic3d_ClipPlane)& aClipPlane = aPlaneIt.Value();
     if (!aClipPlane->IsOn())
+    {
       continue;
+    }
 
-    gp_Pln aGeomPlane = aClipPlane->ToPlane();
-
-    aGeomPlane.Coefficients (aPlaneA, aPlaneB, aPlaneC, aPlaneD);
+    Bnd_Range aSubRange (RealFirst(), RealLast());
+    for (const Graphic3d_ClipPlane* aSubPlaneIter = aClipPlane.get(); aSubPlaneIter != NULL; aSubPlaneIter = aSubPlaneIter->ChainNextPlane().get())
+    {
+      const gp_Pln aGeomPlane = aSubPlaneIter->ToPlane();
+      aGeomPlane.Coefficients (aPlaneA, aPlaneB, aPlaneC, aPlaneD);
 
-    const gp_XYZ& aPlaneDirXYZ = aGeomPlane.Axis().Direction().XYZ();
+      const gp_XYZ& aPlaneDirXYZ = aGeomPlane.Axis().Direction().XYZ();
+      Standard_Real aDotProduct = myViewRayDir.XYZ().Dot (aPlaneDirXYZ);
+      Standard_Real aDistance   = -myNearPickedPnt.XYZ().Dot (aPlaneDirXYZ) - aPlaneD;
+      Standard_Real aDistToPln  = 0.0;
 
-    Standard_Real aDotProduct = DOTp (myViewRayDir, aPlaneDirXYZ);
-    Standard_Real aDistance = - (DOTp (myNearPickedPnt, aPlaneDirXYZ) + aPlaneD);
+      // check whether the pick line is parallel to clip plane
+      if (Abs (aDotProduct) < Precision::Angular())
+      {
+        if (aDistance < 0.0)
+        {
+          continue;
+        }
+        aDistToPln  = RealLast();
+        aDotProduct = 1.0;
+      }
+      else
+      {
+        // compute distance to point of pick line intersection with the plane
+        const Standard_Real aParam = aDistance / aDotProduct;
+
+        const gp_Pnt anIntersectionPnt = myNearPickedPnt.XYZ() + myViewRayDir.XYZ() * aParam;
+        aDistToPln = anIntersectionPnt.Distance (myNearPickedPnt);
+        if (aParam < 0.0)
+        {
+          // the plane is "behind" the ray
+          aDistToPln = -aDistToPln;
+        }
+      }
 
-    // check whether the pick line is parallel to clip plane
-    if (Abs (aDotProduct) < Precision::Angular())
-    {
-      // line lies below the plane and is not clipped, skip
-      continue;
+      // change depth limits for case of opposite and directed planes
+      if (!aClipPlane->IsChain())
+      {
+        if (aDotProduct < 0.0)
+        {
+          theRange.ChangeUnclipRange().TrimTo (aDistToPln);
+        }
+        else
+        {
+          theRange.ChangeUnclipRange().TrimFrom (aDistToPln);
+        }
+      }
+      else
+      {
+        if (aDotProduct < 0.0)
+        {
+          aSubRange.TrimFrom (aDistToPln);
+        }
+        else
+        {
+          aSubRange.TrimTo (aDistToPln);
+        }
+      }
     }
 
-    // compute distance to point of pick line intersection with the plane
-    Standard_Real aParam = aDistance / aDotProduct;
-
-    // check if ray intersects the plane, in case aIntDist < 0
-    // the plane is "behind" the ray
-    if (aParam < 0.0)
+    if (!aSubRange.IsVoid()
+      && aClipPlane->IsChain())
     {
-      continue;
+      theRange.AddClipSubRange (aSubRange);
     }
+  }
+}
 
-    const SelectMgr_Vec3 anIntersectionPt = myNearPickedPnt + myViewRayDir * aParam;
-    const Standard_Real aDistToPln = DISTANCE (anIntersectionPt, myNearPickedPnt);
+// =======================================================================
+// function : IsClipped
+// purpose  : Checks if the point of sensitive in which selection was
+//            detected belongs to the region defined by clipping planes
+// =======================================================================
+Standard_Boolean SelectMgr_RectangularFrustum::IsClipped (const Graphic3d_SequenceOfHClipPlane& thePlanes,
+                                                          const Standard_Real theDepth) const
+{
+  SelectMgr_ViewClipRange aRange;
+  computeClippingRange (thePlanes, aRange);
+  return aRange.IsClipped (theDepth);
+}
 
-    // change depth limits for case of opposite and directed planes
-    if (aDotProduct < 0.0)
-    {
-      aMaxDepth = Min (aDistToPln, aMaxDepth);
-    }
-    else if (aDistToPln > aMinDepth)
-    {
-      aMinDepth = Max (aDistToPln, aMinDepth);
-    }
+// =======================================================================
+// function : SetViewClipping
+// purpose  :
+// =======================================================================
+void SelectMgr_RectangularFrustum::SetViewClipping (const Handle(Graphic3d_SequenceOfHClipPlane)& thePlanes)
+{
+  if (thePlanes.IsNull()
+   || thePlanes->IsEmpty())
+  {
+    myViewClipRange.SetVoid();
+    return;
   }
 
-  return (theDepth <= aMinDepth || theDepth >= aMaxDepth);
+  computeClippingRange (*thePlanes, myViewClipRange);
+}
+
+// =======================================================================
+// function : isViewClippingOk
+// purpose  :
+// =======================================================================
+Standard_Boolean SelectMgr_RectangularFrustum::isViewClippingOk (const SelectBasics_PickResult& thePickResult) const
+{
+  return !myIsViewClipEnabled
+      || !myViewClipRange.IsClipped (thePickResult.Depth());
+}
+
+// =======================================================================
+// function : GetPlanes
+// purpose  :
+// =======================================================================
+void SelectMgr_RectangularFrustum::GetPlanes (NCollection_Vector<SelectMgr_Vec4>& thePlaneEquations) const
+{
+  thePlaneEquations.Clear();
+
+  SelectMgr_Vec4 anEquation;
+  for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < 6; ++aPlaneIdx)
+  {
+    const gp_Vec& aPlaneNorm = myIsOrthographic && aPlaneIdx % 2 == 1 ?
+      myPlanes[aPlaneIdx - 1].Reversed() : myPlanes[aPlaneIdx];
+    anEquation.x() = aPlaneNorm.X();
+    anEquation.y() = aPlaneNorm.Y();
+    anEquation.z() = aPlaneNorm.Z();
+    anEquation.w() = - (aPlaneNorm.XYZ().Dot (myVertices[aPlaneIdx % 2 == 0 ? aPlaneIdx : aPlaneIdx + 2].XYZ()));
+    thePlaneEquations.Append (anEquation);
+  }
 }