From 099f351396706c93a12612ff717cf1e6ec7f2597 Mon Sep 17 00:00:00 2001
From: apl <apl@opencascade.com>
Date: Fri, 9 Sep 2016 15:53:58 +0300
Subject: [PATCH] 0027739: Visualization, TKV3d - implement individual
 acceleration data structure for selection of 2D persistent objects

Low-level selection algorithms has been improved to explicitly support 2D
transformation persistent objects. This patch generally touches:

1) SelectMgr_SelectableObjectSet - the lists of objects has been split onto three
subsets (regular, 3d-persistent, 2d-persistent). Each subset has individual BVH
tree. The algorithms are now have more flexibility to update only those trees
that is really required for actual camera state.

2) SelectMgr_ViewerSelector - explicitly supports Eye space selection operations
on BVH tree for 2d-persistent subset. Change of camera position does not
anymore affect acceleration data structure (BVH tree) of 2d-persistent selectable objects.

3) Other classes from SelectMgr have been fine-tuned to provide appropriate API.

Porting notes:

This patch touches very low-level selection classes. If the low-level features were used
the following modifications may need to be considered for porting:

1) Iteration over objects of SelectMgr_SelectableObjectSet should now
be implemented with corresponding Iterator class.

2) SelectMgr_BaseFrustum::ScaleAndTransform and derived classes return
Handle(SelectMgr_BaseFrustum) instead of NCollection_Handle<> type.

Small correction of test case for issue CR27739
---
 src/SelectMgr/FILES                           |   2 -
 src/SelectMgr/SelectMgr_BaseFrustum.cxx       |   2 +-
 src/SelectMgr/SelectMgr_BaseFrustum.hxx       |   6 +-
 src/SelectMgr/SelectMgr_FrustumBuilder.cxx    |   2 +-
 src/SelectMgr/SelectMgr_FrustumBuilder.hxx    |   2 +-
 .../SelectMgr_RectangularFrustum.cxx          |  21 +-
 .../SelectMgr_RectangularFrustum.hxx          |   6 +-
 .../SelectMgr_SelectableObjectSet.cxx         | 419 ++++++++++++++----
 .../SelectMgr_SelectableObjectSet.hxx         | 220 +++++++--
 .../SelectMgr_SelectableObjectTrsfPersSet.cxx | 170 -------
 .../SelectMgr_SelectableObjectTrsfPersSet.hxx | 118 -----
 .../SelectMgr_SelectingVolumeManager.cxx      |  10 +-
 .../SelectMgr_SelectingVolumeManager.hxx      |  13 +-
 src/SelectMgr/SelectMgr_TriangularFrustum.cxx |  12 +-
 src/SelectMgr/SelectMgr_TriangularFrustum.hxx |   6 +-
 .../SelectMgr_TriangularFrustumSet.cxx        |   8 +-
 .../SelectMgr_TriangularFrustumSet.hxx        |   4 +-
 src/SelectMgr/SelectMgr_ViewerSelector.cxx    | 171 +++----
 src/SelectMgr/SelectMgr_ViewerSelector.hxx    |  21 +-
 src/StdSelect/StdSelect_ViewerSelector3d.cxx  |  28 +-
 tests/bugs/vis/bug27739                       |  81 ++++
 21 files changed, 765 insertions(+), 557 deletions(-)
 delete mode 100644 src/SelectMgr/SelectMgr_SelectableObjectTrsfPersSet.cxx
 delete mode 100644 src/SelectMgr/SelectMgr_SelectableObjectTrsfPersSet.hxx
 create mode 100644 tests/bugs/vis/bug27739

diff --git a/src/SelectMgr/FILES b/src/SelectMgr/FILES
index bf2eb89118..6997ac27cd 100755
--- a/src/SelectMgr/FILES
+++ b/src/SelectMgr/FILES
@@ -28,8 +28,6 @@ SelectMgr_SelectableObject.cxx
 SelectMgr_SelectableObject.hxx
 SelectMgr_SelectableObjectSet.cxx
 SelectMgr_SelectableObjectSet.hxx
-SelectMgr_SelectableObjectTrsfPersSet.cxx
-SelectMgr_SelectableObjectTrsfPersSet.hxx
 SelectMgr_SelectingVolumeManager.cxx
 SelectMgr_SelectingVolumeManager.hxx
 SelectMgr_Selection.cxx
diff --git a/src/SelectMgr/SelectMgr_BaseFrustum.cxx b/src/SelectMgr/SelectMgr_BaseFrustum.cxx
index 79dabddaf2..a70b9f4e24 100644
--- a/src/SelectMgr/SelectMgr_BaseFrustum.cxx
+++ b/src/SelectMgr/SelectMgr_BaseFrustum.cxx
@@ -124,7 +124,7 @@ void SelectMgr_BaseFrustum::SetWindowSize (const Standard_Integer theWidth, cons
 // purpose  :
 //=======================================================================
 void SelectMgr_BaseFrustum::WindowSize (Standard_Integer& theWidth,
-                                        Standard_Integer& theHeight)
+                                        Standard_Integer& theHeight) const
 {
   myBuilder->WindowSize (theWidth, theHeight);
 }
diff --git a/src/SelectMgr/SelectMgr_BaseFrustum.hxx b/src/SelectMgr/SelectMgr_BaseFrustum.hxx
index 487cece2af..88398faf87 100644
--- a/src/SelectMgr/SelectMgr_BaseFrustum.hxx
+++ b/src/SelectMgr/SelectMgr_BaseFrustum.hxx
@@ -75,7 +75,7 @@ public:
                                       const Standard_Integer theHeight);
 
   Standard_EXPORT void WindowSize (Standard_Integer& theWidth,
-                                   Standard_Integer& theHeight);
+                                   Standard_Integer& theHeight) const;
 
   //! Passes viewport parameters to builder
   Standard_EXPORT void SetViewport (const Standard_Real theX,
@@ -109,8 +109,8 @@ public:
   //! 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.
-  Standard_EXPORT virtual NCollection_Handle<SelectMgr_BaseFrustum> ScaleAndTransform (const Standard_Integer /*theScaleFactor*/,
-                                                                                       const gp_GTrsf& /*theTrsf*/) { return NULL; }
+  Standard_EXPORT virtual Handle(SelectMgr_BaseFrustum) ScaleAndTransform (const Standard_Integer /*theScaleFactor*/,
+                                                                           const gp_GTrsf& /*theTrsf*/) const { return NULL; }
 
   //! SAT intersection test between defined volume and given axis-aligned box
   Standard_EXPORT virtual Standard_Boolean Overlaps (const SelectMgr_Vec3& theBoxMin,
diff --git a/src/SelectMgr/SelectMgr_FrustumBuilder.cxx b/src/SelectMgr/SelectMgr_FrustumBuilder.cxx
index 75447a823d..c5c83486db 100644
--- a/src/SelectMgr/SelectMgr_FrustumBuilder.cxx
+++ b/src/SelectMgr/SelectMgr_FrustumBuilder.cxx
@@ -119,7 +119,7 @@ void SelectMgr_FrustumBuilder::SetViewport (const Standard_Real theX,
 // purpose  :
 //=======================================================================
 void SelectMgr_FrustumBuilder::WindowSize (Standard_Integer& theWidth,
-                                           Standard_Integer& theHeight)
+                                           Standard_Integer& theHeight) const
 {
   theWidth = myWidth;
   theHeight = myHeight;
diff --git a/src/SelectMgr/SelectMgr_FrustumBuilder.hxx b/src/SelectMgr/SelectMgr_FrustumBuilder.hxx
index c490aad4f1..6956f797cd 100644
--- a/src/SelectMgr/SelectMgr_FrustumBuilder.hxx
+++ b/src/SelectMgr/SelectMgr_FrustumBuilder.hxx
@@ -63,7 +63,7 @@ public:
   Standard_EXPORT void InvalidateViewport();
 
   Standard_EXPORT void WindowSize (Standard_Integer& theWidth,
-                                   Standard_Integer& theHeight);
+                                   Standard_Integer& theHeight) const;
 
   //! Calculates signed distance between plane with equation
   //! theEq and point thePnt
diff --git a/src/SelectMgr/SelectMgr_RectangularFrustum.cxx b/src/SelectMgr/SelectMgr_RectangularFrustum.cxx
index fe2c835c65..1745cb74c5 100644
--- a/src/SelectMgr/SelectMgr_RectangularFrustum.cxx
+++ b/src/SelectMgr/SelectMgr_RectangularFrustum.cxx
@@ -178,11 +178,11 @@ namespace
     // Top
     theNormals[0] = theEdges[0].Crossed (theEdges[4]);
     // Bottom
-    theNormals[1] = theEdges[2].Crossed (theEdges[3]);
+    theNormals[1] = theEdges[2].Crossed (theEdges[0]);
     // Left
     theNormals[2] = theEdges[4].Crossed (theEdges[1]);
     // Right
-    theNormals[3] = theEdges[5].Crossed (theEdges[3]);
+    theNormals[3] = theEdges[1].Crossed (theEdges[5]);
     // Near
     theNormals[4] = theEdges[0].Crossed (theEdges[1]);
     // Far
@@ -195,7 +195,7 @@ namespace
 // purpose  : Caches projection of frustum's vertices onto its plane directions
 //            and {i, j, k}
 // =======================================================================
-void SelectMgr_RectangularFrustum::cacheVertexProjections (SelectMgr_RectangularFrustum* theFrustum)
+void SelectMgr_RectangularFrustum::cacheVertexProjections (SelectMgr_RectangularFrustum* theFrustum) const
 {
   if (theFrustum->myIsOrthographic)
   {
@@ -322,13 +322,13 @@ void SelectMgr_RectangularFrustum::Build (const gp_Pnt2d& theMinPnt,
 //                  as any negative value;
 //                - scale only is needed: @theTrsf must be set to gp_Identity.
 // =======================================================================
-NCollection_Handle<SelectMgr_BaseFrustum> SelectMgr_RectangularFrustum::ScaleAndTransform (const Standard_Integer theScaleFactor,
-                                                                                           const gp_GTrsf& theTrsf)
+Handle(SelectMgr_BaseFrustum) SelectMgr_RectangularFrustum::ScaleAndTransform (const Standard_Integer theScaleFactor,
+                                                                               const gp_GTrsf& theTrsf) const
 {
   Standard_ASSERT_RAISE (theScaleFactor > 0,
     "Error! Pixel tolerance for selection should be greater than zero");
 
-  SelectMgr_RectangularFrustum* aRes = new SelectMgr_RectangularFrustum();
+  Handle(SelectMgr_RectangularFrustum) aRes = new SelectMgr_RectangularFrustum();
   const Standard_Boolean isToScale = theScaleFactor != 1;
   const Standard_Boolean isToTrsf  = theTrsf.Form() != gp_Identity;
 
@@ -336,7 +336,7 @@ NCollection_Handle<SelectMgr_BaseFrustum> SelectMgr_RectangularFrustum::ScaleAnd
     return aRes;
 
   aRes->myIsOrthographic = myIsOrthographic;
-  SelectMgr_RectangularFrustum* aRef = this;
+  const SelectMgr_RectangularFrustum* aRef = this;
 
   if (isToScale)
   {
@@ -352,7 +352,7 @@ NCollection_Handle<SelectMgr_BaseFrustum> SelectMgr_RectangularFrustum::ScaleAnd
     // recompute base frustum characteristics from scratch
     computeFrustum (aMinPnt, aMaxPnt, myBuilder, aRes->myVertices, aRes->myEdgeDirs);
 
-    aRef = aRes;
+    aRef = aRes.get();
   }
 
   if (isToTrsf)
@@ -396,11 +396,12 @@ NCollection_Handle<SelectMgr_BaseFrustum> SelectMgr_RectangularFrustum::ScaleAnd
   // compute frustum normals
   computeNormals (aRes->myEdgeDirs, aRes->myPlanes);
 
-  cacheVertexProjections (aRes);
+  cacheVertexProjections (aRes.get());
 
   aRes->myViewClipRange = myViewClipRange;
+  aRes->myMousePos      = myMousePos;
 
-  return NCollection_Handle<SelectMgr_BaseFrustum> (aRes);
+  return aRes;
 }
 
 // =======================================================================
diff --git a/src/SelectMgr/SelectMgr_RectangularFrustum.hxx b/src/SelectMgr/SelectMgr_RectangularFrustum.hxx
index 4f2a92ff74..53fa3db504 100644
--- a/src/SelectMgr/SelectMgr_RectangularFrustum.hxx
+++ b/src/SelectMgr/SelectMgr_RectangularFrustum.hxx
@@ -50,8 +50,8 @@ public:
   //! 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.
-  Standard_EXPORT virtual NCollection_Handle<SelectMgr_BaseFrustum> ScaleAndTransform (const Standard_Integer theScaleFactor,
-                                                                                       const gp_GTrsf& theTrsf) Standard_OVERRIDE;
+  Standard_EXPORT virtual Handle(SelectMgr_BaseFrustum) ScaleAndTransform (const Standard_Integer theScaleFactor,
+                                                                           const gp_GTrsf& theTrsf) const Standard_OVERRIDE;
 
 
   // SAT Tests for different objects
@@ -143,7 +143,7 @@ protected:
 
 private:
 
-  void cacheVertexProjections (SelectMgr_RectangularFrustum* theFrustum);
+  void cacheVertexProjections (SelectMgr_RectangularFrustum* theFrustum) const;
 
 private:
   enum { LeftTopNear, LeftTopFar,
diff --git a/src/SelectMgr/SelectMgr_SelectableObjectSet.cxx b/src/SelectMgr/SelectMgr_SelectableObjectSet.cxx
index 5bc3f8f4ed..6aa38decef 100644
--- a/src/SelectMgr/SelectMgr_SelectableObjectSet.cxx
+++ b/src/SelectMgr/SelectMgr_SelectableObjectSet.cxx
@@ -13,58 +13,236 @@
 // Alternatively, this file may be used under the terms of Open CASCADE
 // commercial license or contractual agreement.
 
-#include <Bnd_Box.hxx>
-#include <BVH_BinnedBuilder.hxx>
-
 #include <SelectMgr_SelectableObjectSet.hxx>
+#include <SelectMgr_VectorTypes.hxx>
 
-//=======================================================================
-// function : SelectMgr_SelectableObjectSet
-// purpose  : Creates new empty objects set and initializes BVH tree
-//            builder to Binned builder with 1 element per list
-//=======================================================================
-SelectMgr_SelectableObjectSet::SelectMgr_SelectableObjectSet()
+#include <BVH_BinnedBuilder.hxx>
+#include <BVH_LinearBuilder.hxx>
+
+namespace
 {
-  myBuilder = new BVH_BinnedBuilder<Standard_Real, 3, 4> (1, 32, Standard_True);
-}
+  //! Short-cut definition of indexed data map of selectable objects
+  typedef NCollection_IndexedMap<Handle(SelectMgr_SelectableObject)> ObjectsMap;
 
-//=======================================================================
-// function : Append
-// purpose  : Adds new object to the set and marks BVH tree for rebuild
-//=======================================================================
-Standard_Boolean SelectMgr_SelectableObjectSet::Append (const Handle(SelectMgr_SelectableObject)& theObject)
-{
-  Standard_Integer aSize = Size();
+  //-------------------------------------------------------------------------------------
+  // Adaptor over regular objects subset of SelectMgr_SelectableObjectSet for BVH builder
+  //-------------------------------------------------------------------------------------
 
-  if (aSize < myObjects.Add (theObject))
+  //! This class provides direct access to fields of SelectMgr_SelectableObjectSet
+  //! so the BVH builder could explicitly arrange objects in the map as necessary
+  //! to provide synchronization of indexes with constructed BVH tree.
+  class BVHBuilderAdaptorRegular : public BVH_Set<Standard_Real, 3>
   {
-    MarkDirty();
+  public:
 
-    return Standard_True;
-  }
+    //! Construct adaptor.
+    BVHBuilderAdaptorRegular (ObjectsMap& theObjects) : myObjects (theObjects) {};
 
-  return Standard_False;
-}
-
-//=======================================================================
-// function : Remove
-// purpose  : Removes object theObject from set and marks BVH tree for
-//            rebuild
-//=======================================================================
-Standard_Boolean SelectMgr_SelectableObjectSet::Remove (const Handle(SelectMgr_SelectableObject)& theObject)
-{
-  const Standard_Integer anIndex = myObjects.FindIndex (theObject);
-
-  if (anIndex != 0)
-  {
-    if (anIndex != Size())
+    //! Returns bounding box of object with index theIndex
+    virtual Select3D_BndBox3d Box (const Standard_Integer theIndex) const Standard_OVERRIDE
     {
-      Swap (anIndex - 1, Size() - 1);
+      const Handle(SelectMgr_SelectableObject)& anObject = myObjects.FindKey (theIndex + 1);
+      Bnd_Box aBox;
+      anObject->BoundingBox (aBox);
+      if (aBox.IsVoid())
+        return Select3D_BndBox3d();
+
+      return Select3D_BndBox3d (SelectMgr_Vec3 (aBox.CornerMin().X(), aBox.CornerMin().Y(), aBox.CornerMin().Z()),
+                                SelectMgr_Vec3 (aBox.CornerMax().X(), aBox.CornerMax().Y(), aBox.CornerMax().Z()));
     }
 
-    myObjects.RemoveLast();
+    //! Returns bounding box of the whole subset.
+    virtual Select3D_BndBox3d Box() const Standard_OVERRIDE
+    {
+      if (!myBox.IsValid())
+      {
+        myBox = BVH_Set<Standard_Real, 3>::Box();
+      }
+      return myBox;
+    }
 
-    MarkDirty();
+    //! Make inherited method Box() visible to avoid CLang warning
+    using BVH_Set<Standard_Real, 3>::Box;
+
+    //! Returns center of object with index theIndex in the set
+    //! along the given axis theAxis
+    virtual Standard_Real Center (const Standard_Integer theIndex,
+                                  const Standard_Integer theAxis) const Standard_OVERRIDE
+    {
+      const Select3D_BndBox3d aBndBox = Box (theIndex);
+
+      return (aBndBox.CornerMin()[theAxis] +
+              aBndBox.CornerMax()[theAxis]) * 0.5;
+    }
+
+    //! Returns size of objects set.
+    virtual Standard_Integer Size() const Standard_OVERRIDE
+    {
+      return myObjects.Size();
+    }
+
+    //! Swaps items with indexes theIndex1 and theIndex2 in the set
+    virtual void Swap (const Standard_Integer theIndex1, const Standard_Integer theIndex2) Standard_OVERRIDE
+    {
+      myObjects.Swap (theIndex1 + 1, theIndex2 + 1);
+    }
+
+  private:
+    BVHBuilderAdaptorRegular& operator=(BVHBuilderAdaptorRegular) { return *this; }
+
+  private:
+    ObjectsMap& myObjects;
+    mutable Select3D_BndBox3d myBox;
+  };
+
+  //----------------------------------------------------------------------------------------
+  // Adaptor over persistent objects subset of SelectMgr_SelectableObjectSet for BVH builder
+  //----------------------------------------------------------------------------------------
+
+  //! This class provides direct access to fields of SelectMgr_SelectableObjectSet
+  //! so the BVH builder could explicitly arrange objects in the map as necessary
+  //! to provide synchronization of indexes with constructed BVH tree.
+  class BVHBuilderAdaptorPersistent : public BVH_Set<Standard_Real, 3>
+  {
+  public:
+
+    //! Construct adaptor.
+    //! @param theCamera, theProjectionMat, theWorldViewMat,
+    //!        theWidth, theHeight [in] view properties used for computation of
+    //!        bounding boxes within the world view camera space.
+    BVHBuilderAdaptorPersistent (ObjectsMap& theObjects,
+                                 const Handle(Graphic3d_Camera)& theCamera,
+                                 const Graphic3d_Mat4d& theProjectionMat,
+                                 const Graphic3d_Mat4d& theWorldViewMat,
+                                 const Standard_Integer theWidth,
+                                 const Standard_Integer theHeight)
+    : myObjects (theObjects)
+    {
+      myBoundings.ReSize (myObjects.Size());
+      for (Standard_Integer anI = 1; anI <= myObjects.Size(); ++anI)
+      {
+        const Handle(SelectMgr_SelectableObject)& anObject = myObjects (anI);
+
+        Bnd_Box aBoundingBox;
+        anObject->BoundingBox (aBoundingBox);
+        if (aBoundingBox.IsVoid())
+        {
+          myBoundings.Add (new Select3D_HBndBox3d());
+        }
+        else
+        {
+          anObject->TransformPersistence().Apply (
+            theCamera, theProjectionMat, theWorldViewMat, theWidth, theHeight, aBoundingBox);
+
+          const gp_Pnt aMin = aBoundingBox.CornerMin();
+          const gp_Pnt aMax = aBoundingBox.CornerMax();
+          myBoundings.Add (new Select3D_HBndBox3d (Select3D_Vec3 (aMin.X(), aMin.Y(), aMin.Z()),
+                                                   Select3D_Vec3 (aMax.X(), aMax.Y(), aMax.Z())));
+        }
+      }
+    }
+
+    //! Returns bounding box of object with index theIndex
+    virtual Select3D_BndBox3d Box (const Standard_Integer theIndex) const Standard_OVERRIDE
+    {
+      return *myBoundings (theIndex + 1);
+    }
+
+    //! Returns bounding box of the whole subset.
+    virtual Select3D_BndBox3d Box() const Standard_OVERRIDE
+    {
+      if (!myBox.IsValid())
+      {
+        myBox = BVH_Set<Standard_Real, 3>::Box();
+      }
+      return myBox;
+    }
+
+    //! Make inherited method Box() visible to avoid CLang warning
+    using BVH_Set<Standard_Real, 3>::Box;
+
+    //! Returns center of object with index theIndex in the set
+    //! along the given axis theAxis
+    virtual Standard_Real Center (const Standard_Integer theIndex,
+                                  const Standard_Integer theAxis) const Standard_OVERRIDE
+    {
+      const Select3D_BndBox3d& aBoundingBox = *myBoundings (theIndex + 1);
+
+      return (aBoundingBox.CornerMin()[theAxis] + aBoundingBox.CornerMax()[theAxis]) * 0.5;
+    }
+
+    //! Returns size of objects set.
+    virtual Standard_Integer Size() const Standard_OVERRIDE
+    {
+      return myObjects.Size();
+    }
+
+    //! Swaps items with indexes theIndex1 and theIndex2 in the set
+    virtual void Swap (const Standard_Integer theIndex1, const Standard_Integer theIndex2) Standard_OVERRIDE
+    {
+      const Standard_Integer aStructIdx1 = theIndex1 + 1;
+      const Standard_Integer aStructIdx2 = theIndex2 + 1;
+
+      myObjects.Swap (aStructIdx1, aStructIdx2);
+      myBoundings.Swap (aStructIdx1, aStructIdx2);
+    }
+
+  private:
+    BVHBuilderAdaptorPersistent& operator=(BVHBuilderAdaptorPersistent) { return *this; }
+
+  private:
+    ObjectsMap& myObjects;
+    mutable Select3D_BndBox3d myBox;
+    typedef NCollection_Shared<Select3D_BndBox3d> Select3D_HBndBox3d;
+    NCollection_IndexedMap<Handle(Select3D_HBndBox3d)> myBoundings;
+  };
+
+  static const Graphic3d_Mat4d THE_IDENTITY_MAT;
+}
+
+//=============================================================================
+// Function: Constructor
+// Purpose :
+//=============================================================================
+SelectMgr_SelectableObjectSet::SelectMgr_SelectableObjectSet()
+: myLastWidth (0),
+  myLastHeight (0)
+{
+  myBVH[BVHSubset_2dPersistent] = new BVH_Tree<Standard_Real, 3>();
+  myBVH[BVHSubset_3dPersistent] = new BVH_Tree<Standard_Real, 3>();
+  myBVH[BVHSubset_3d]           = new BVH_Tree<Standard_Real, 3>();
+
+  myBuilder[BVHSubset_2dPersistent] = new BVH_LinearBuilder<Standard_Real, 3> (1, 32);
+  myBuilder[BVHSubset_3dPersistent] = new BVH_LinearBuilder<Standard_Real, 3> (1, 32);
+  myBuilder[BVHSubset_3d]           = new BVH_BinnedBuilder<Standard_Real, 3, 4> (1, 32, Standard_True);
+
+  myIsDirty[BVHSubset_2dPersistent] = Standard_False;
+  myIsDirty[BVHSubset_3dPersistent] = Standard_False;
+  myIsDirty[BVHSubset_3d]           = Standard_False;
+}
+
+//=============================================================================
+// Function: Append
+// Purpose :
+//=============================================================================
+Standard_Boolean SelectMgr_SelectableObjectSet::Append (const Handle(SelectMgr_SelectableObject)& theObject)
+{
+  // get an appropriate BVH subset to insert the object into it
+  const Standard_Integer aSubsetIdx = appropriateSubset (theObject);
+
+  // check that the object is excluded from other subsets
+  if (myObjects[(aSubsetIdx + 1) % BVHSubsetNb].Contains (theObject)
+   || myObjects[(aSubsetIdx + 2) % BVHSubsetNb].Contains (theObject))
+  {
+    return Standard_False;
+  }
+
+  // try adding it into the appropriate object subset
+  const Standard_Integer aSize = myObjects[aSubsetIdx].Size();
+
+  if (aSize < myObjects[aSubsetIdx].Add (theObject))
+  {
+    myIsDirty[aSubsetIdx] = Standard_True;
 
     return Standard_True;
   }
@@ -72,51 +250,142 @@ Standard_Boolean SelectMgr_SelectableObjectSet::Remove (const Handle(SelectMgr_S
   return Standard_False;
 }
 
-//=======================================================================
-// function : Box
-// purpose  : Returns bounding box of object with index theIndex
-//=======================================================================
-Select3D_BndBox3d SelectMgr_SelectableObjectSet::Box (const Standard_Integer theIndex) const
+//=============================================================================
+// Function: Remove
+// Purpose :
+//=============================================================================
+Standard_Boolean SelectMgr_SelectableObjectSet::Remove (const Handle(SelectMgr_SelectableObject)& theObject)
 {
-  const Handle(SelectMgr_SelectableObject)& anObject = GetObjectById (theIndex);
-  Bnd_Box aBox;
-  anObject->BoundingBox (aBox);
-  if (aBox.IsVoid())
-    return Select3D_BndBox3d();
+  // remove object from the first found subset
+  for (Standard_Integer aSubsetIdx = 0; aSubsetIdx < BVHSubsetNb; ++aSubsetIdx)
+  {
+    const Standard_Integer anIndex = myObjects[aSubsetIdx].FindIndex (theObject);
 
-  return Select3D_BndBox3d (SelectMgr_Vec3 (aBox.CornerMin().X(), aBox.CornerMin().Y(), aBox.CornerMin().Z()),
-                            SelectMgr_Vec3 (aBox.CornerMax().X(), aBox.CornerMax().Y(), aBox.CornerMax().Z()));
+    if (anIndex != 0)
+    {
+      const Standard_Integer aSize = myObjects[aSubsetIdx].Size();
+
+      if (anIndex != aSize)
+      {
+        myObjects[aSubsetIdx].Swap (anIndex, aSize);
+      }
+
+      myObjects[aSubsetIdx].RemoveLast();
+      myIsDirty[aSubsetIdx] = Standard_True;
+
+      return Standard_True;
+    }
+  }
+
+  return Standard_False;
 }
 
-//=======================================================================
-// function : Center
-// purpose  : Returns center of object with index theIndex in the set
-//            along the given axis theAxis
-//=======================================================================
-Standard_Real SelectMgr_SelectableObjectSet::Center (const Standard_Integer theIndex,
-                                                     const Standard_Integer theAxis) const
+//=============================================================================
+// Function: ChangeSubset
+// Purpose :
+//=============================================================================
+void SelectMgr_SelectableObjectSet::ChangeSubset (const Handle(SelectMgr_SelectableObject)& theObject)
 {
-  Select3D_BndBox3d aBndBox = Box (theIndex);
+  // do not do anything is object is not in the map
+  const Standard_Integer aCurrSubsetIdx = currentSubset (theObject);
 
-  return (aBndBox.CornerMin()[theAxis] +
-          aBndBox.CornerMax()[theAxis]) * 0.5;
+  if (aCurrSubsetIdx < 0)
+  {
+    return;
+  }
+
+  // check whether the subset need to be changed at all
+  const Standard_Integer aSubsetIdx = appropriateSubset (theObject);
+
+  if (aCurrSubsetIdx == aSubsetIdx)
+  {
+    return;
+  }
+
+  // replace object in the maps
+  Remove (theObject);
+  Append (theObject);
 }
 
-//=======================================================================
-// function : Swap
-// purpose  : Swaps items with indexes theIndex1 and theIndex2 in the set
-//=======================================================================
-void SelectMgr_SelectableObjectSet::Swap (const Standard_Integer theIndex1,
-                                          const Standard_Integer theIndex2)
+//=============================================================================
+// Function: UpdateBVH
+// Purpose :
+//=============================================================================
+void SelectMgr_SelectableObjectSet::UpdateBVH (const Handle(Graphic3d_Camera)& theCamera,
+                                               const Graphic3d_Mat4d& theProjectionMat,
+                                               const Graphic3d_Mat4d& theWorldViewMat,
+                                               const Graphic3d_WorldViewProjState& theViewState,
+                                               const Standard_Integer theViewportWidth,
+                                               const Standard_Integer theViewportHeight)
 {
-  myObjects.Swap (theIndex1 + 1, theIndex2 + 1);
+  // -----------------------------------------
+  // check and update 3D BVH tree if necessary
+  // -----------------------------------------
+  if (!IsEmpty (BVHSubset_3d) && myIsDirty[BVHSubset_3d])
+  {
+    // construct adaptor over private fields to provide direct access for the BVH builder
+    BVHBuilderAdaptorRegular anAdaptor (myObjects[BVHSubset_3d]);
+
+    // update corresponding BVH tree data structure
+    myBuilder[BVHSubset_3d]->Build (&anAdaptor, myBVH[BVHSubset_3d].operator->(), anAdaptor.Box());
+
+    // release dirty state
+    myIsDirty[BVHSubset_3d] = Standard_False;
+  }
+
+  if (!theCamera.IsNull())
+  {
+    const Standard_Boolean isWindowSizeChanged =
+      (myLastHeight != theViewportHeight) || (myLastWidth != theViewportWidth);
+
+    // -----------------------------------------------------
+    // check and update 3D persistence BVH tree if necessary
+    // -----------------------------------------------------
+    if (!IsEmpty (BVHSubset_3dPersistent) &&
+         (myIsDirty[BVHSubset_3dPersistent] || myLastViewState.IsChanged (theViewState) || isWindowSizeChanged))
+    {
+      // construct adaptor over private fields to provide direct access for the BVH builder
+      BVHBuilderAdaptorPersistent anAdaptor (myObjects[BVHSubset_3dPersistent],
+        theCamera, theProjectionMat, theWorldViewMat, theViewportWidth, theViewportHeight);
+
+      // update corresponding BVH tree data structure
+      myBuilder[BVHSubset_3dPersistent]->Build (&anAdaptor, myBVH[BVHSubset_3dPersistent].operator->(), anAdaptor.Box());
+    }
+
+    // -----------------------------------------------------
+    // check and update 2D persistence BVH tree if necessary
+    // -----------------------------------------------------
+    if (!IsEmpty (BVHSubset_2dPersistent) &&
+         (myIsDirty[BVHSubset_2dPersistent] || myLastViewState.IsProjectionChanged (theViewState) || isWindowSizeChanged))
+    {
+      // construct adaptor over private fields to provide direct access for the BVH builder
+      BVHBuilderAdaptorPersistent anAdaptor (myObjects[BVHSubset_2dPersistent],
+        theCamera, theProjectionMat, THE_IDENTITY_MAT, theViewportWidth, theViewportHeight);
+
+      // update corresponding BVH tree data structure
+      myBuilder[BVHSubset_2dPersistent]->Build (&anAdaptor, myBVH[BVHSubset_2dPersistent].operator->(), anAdaptor.Box());
+    }
+
+    // release dirty state for every subset
+    myIsDirty[BVHSubset_3dPersistent] = Standard_False;
+    myIsDirty[BVHSubset_2dPersistent] = Standard_False;
+
+    // keep last view state
+    myLastViewState = theViewState;
+  }
+
+  // keep last window state
+  myLastWidth  = theViewportWidth;
+  myLastHeight = theViewportHeight;
 }
 
-//=======================================================================
-// function : Size
-// purpose  : Returns size of objects set
-//=======================================================================
-Standard_Integer SelectMgr_SelectableObjectSet::Size() const
+//=============================================================================
+// Function: MarkDirty
+// Purpose :
+//=============================================================================
+void SelectMgr_SelectableObjectSet::MarkDirty()
 {
-  return myObjects.Size();
+  myIsDirty[BVHSubset_3d]           = Standard_True;
+  myIsDirty[BVHSubset_3dPersistent] = Standard_True;
+  myIsDirty[BVHSubset_2dPersistent] = Standard_True;
 }
diff --git a/src/SelectMgr/SelectMgr_SelectableObjectSet.hxx b/src/SelectMgr/SelectMgr_SelectableObjectSet.hxx
index 22784edc41..770f182d44 100644
--- a/src/SelectMgr/SelectMgr_SelectableObjectSet.hxx
+++ b/src/SelectMgr/SelectMgr_SelectableObjectSet.hxx
@@ -17,68 +17,210 @@
 #define _SelectMgr_SelectableObjectSet_HeaderFile
 
 #include <BVH_PrimitiveSet.hxx>
-
-#include <Select3D_BndBox3d.hxx>
-#include <SelectMgr_SelectableObject.hxx>
-#include <SelectMgr_VectorTypes.hxx>
-
+#include <BVH_Tree.hxx>
+#include <Graphic3d_Mat4d.hxx>
+#include <Graphic3d_WorldViewProjState.hxx>
+#include <NCollection_Handle.hxx>
 #include <NCollection_IndexedMap.hxx>
+#include <SelectMgr_SelectableObject.hxx>
 
-//! The purpose of this class is to organize all selectable objects into
-//! data structure, allowing to build BVH tree. For selectable objects
-//! binned BVH builder is used with 32 bins and 1 element per leaf.
-class SelectMgr_SelectableObjectSet : public BVH_PrimitiveSet<Standard_Real, 3>
+//! The purpose of this class is to organize all selectable objects into data structure, allowing to build 
+//! set of BVH trees for each transformation persistence subclass of selectable objects. This allow to minify
+//! number of updates for BVH trees - for example 2D persistent object subclass depends only on camera's projection
+//! and the corresponding BVH tree needs to be updated when camera's projection parameters change, while another
+//! tree for non-persistent objects can be left unchanged in this case.
+class SelectMgr_SelectableObjectSet
 {
 public:
 
-  //! Creates new empty objects set and initializes BVH tree
-  //! builder to Binned builder with 1 element per list
+  //! This enumeration declares names for subsets of selectable objects. Each subset has independent BVH tree.
+  //! The class maintains subsets of selectable objects by their persistence flag. This allows to restric
+  //! rebuilding of the trees for particular subset when the camera change does not implicitly require it:
+  //! - BVHSubset_3d refers to the subset of normal world-space 3D objects. Associated BVH tree does not depend
+  //! on the camera's state at all.
+  //! This subset uses binned BVH builder with 32 bins and 1 element per leaf.
+  //! - BVHSubset_3dPersistent refers to the subset of 3D persistent selectable objects (rotate, pan, zoom persistence).
+  //! Associated BVH tree needs to be updated when either the camera's projection and position change.
+  //! This subset uses linear BVH builder with 32 levels of depth and 1 element per leaf.
+  //! - BVHSubset_2dPersistent refers to the subset of 2D persistent selectable objects. Associated BVH tree
+  //! needs to be updated only when camera's projection changes. Bounding volumes for this object subclass
+  //! is represented directly in eye space coordinates.
+  //! This subset uses linear BVH builder with 32 levels of depth and 1 element per leaf.
+  enum BVHSubset
+  {
+    BVHSubset_3d,
+    BVHSubset_3dPersistent,
+    BVHSubset_2dPersistent,
+    BVHSubsetNb
+  };
+
+public:
+
+  //! Class to iterate sequentually over all objects from every subset.
+  class Iterator
+  {
+    //! Short-cut definition of map iterator type
+    typedef NCollection_IndexedMap<Handle(SelectMgr_SelectableObject)>::Iterator ObjectMapIterator;
+
+  public:
+
+    //! Default constructor without initialization.
+    Iterator() : mySet (NULL), mySubsetIdx (BVHSubsetNb) {}
+
+    //! Constructs and initializes the iterator.
+    Iterator (const SelectMgr_SelectableObjectSet& theSet) { Init (theSet); }
+
+    //! Initializes the iterator.
+    void Init (const SelectMgr_SelectableObjectSet& theSet)
+    {
+      mySet       = &theSet;
+      mySubsetIdx = 0;
+      mySubsetIt  = ObjectMapIterator (theSet.myObjects[mySubsetIdx]);
+      More();
+    }
+
+    //! Returns false when there is no more objects to iterate over.
+    Standard_Boolean More()
+    {
+      if (mySubsetIt.More())
+      {
+        return Standard_True;
+      }
+      else if ((mySubsetIdx == BVHSubsetNb - 1) || mySet == NULL)
+      {
+        return Standard_False;
+      }
+      mySubsetIt = ObjectMapIterator (mySet->myObjects[++mySubsetIdx]);
+      return More();
+    }
+
+    //! Steps to next selectable object in the set.
+    void Next() { mySubsetIt.Next(); }
+
+    //! Returns current object.
+    const Handle(SelectMgr_SelectableObject)& Value() const { return mySubsetIt.Value(); }
+
+  private:
+    const SelectMgr_SelectableObjectSet* mySet;
+    Standard_Integer mySubsetIdx;
+    ObjectMapIterator mySubsetIt;
+  };
+
+public:
+
+  //! Creates new empty objects set and initializes BVH tree builders for each subset.
   Standard_EXPORT SelectMgr_SelectableObjectSet();
 
   //! Releases resources of selectable object set.
   virtual ~SelectMgr_SelectableObjectSet() { }
 
-  //! Adds new object to the set and marks BVH tree for rebuild.
-  //! @return true if structure added, otherwise returns false (structure already in the set).
+  //! Adds the new selectable object to the set. The selectable object is placed into one of the
+  //! predefined subsets depending on its persistence type. After adding an object, this method
+  //! marks the corresponding BVH tree for rebuild.
+  //! @return true if selectable object is added, otherwise returns false (selectable object is already in the set).
   Standard_EXPORT Standard_Boolean Append (const Handle(SelectMgr_SelectableObject)& theObject);
 
-  //! Removes object theObject from set and marks BVH tree for rebuild.
-  //! @return true if structure removed, otherwise returns false (structure is not in the set).
+  //! Removes the selectable object from the set. The selectable object is removed from the subset
+  //! it has been placed into. After removing an object, this method marks the corresponding
+  //! BVH tree for rebuild.
+  //! @return true if selectable object is removed, otherwise returns false (selectable object is not in the set).
   Standard_EXPORT Standard_Boolean Remove (const Handle(SelectMgr_SelectableObject)& theObject);
 
-  //! Returns bounding box of object with index theIndex
-  Standard_EXPORT virtual Select3D_BndBox3d Box (const Standard_Integer theIndex) const Standard_OVERRIDE;
+  //! Performs necessary updates when object's persistence types changes.
+  //! This method should be called right after changing transformation persistence flags of the
+  //! objects and before updating BVH tree - to provide up-to-date state of the object set.
+  Standard_EXPORT void ChangeSubset (const Handle(SelectMgr_SelectableObject)& theObject);
 
-  //! Make inherited method Box() visible to avoid CLang warning
-  using BVH_PrimitiveSet<Standard_Real, 3>::Box;
+  //! Updates outdated BVH trees and remembers the last state of the
+  //! camera view-projection matrices and viewport (window) dimensions.
+  Standard_EXPORT void UpdateBVH (const Handle(Graphic3d_Camera)& theCamera,
+                                  const Graphic3d_Mat4d& theProjectionMat,
+                                  const Graphic3d_Mat4d& theWorldViewMat,
+                                  const Graphic3d_WorldViewProjState& theViewState,
+                                  const Standard_Integer theViewportWidth,
+                                  const Standard_Integer theViewportHeight);
 
-  //! Returns center of object with index theIndex in the set
-  //! along the given axis theAxis
-  Standard_EXPORT virtual Standard_Real Center (const Standard_Integer theIndex,
-                                                const Standard_Integer theAxis) const Standard_OVERRIDE;
+  //! Marks every BVH subset for update.
+  Standard_EXPORT void MarkDirty();
 
-  //! Swaps items with indexes theIndex1 and theIndex2 in the set
-  Standard_EXPORT virtual void Swap (const Standard_Integer theIndex1,
-                                     const Standard_Integer theIndex2) Standard_OVERRIDE;
-
-  //! Returns size of objects set
-  Standard_EXPORT virtual Standard_Integer Size() const Standard_OVERRIDE;
-
-  //! Returns object from set by theIndex given
-  const Handle(SelectMgr_SelectableObject)& GetObjectById (const Standard_Integer theIndex) const
-  {
-    return myObjects.FindKey (theIndex + 1);
-  }
-
-  //! Returns true if this objects set contains theObject given
+  //! Returns true if this objects set contains theObject given.
   Standard_Boolean Contains (const Handle(SelectMgr_SelectableObject)& theObject) const
   {
-    return myObjects.Contains (theObject);
+    return myObjects[BVHSubset_3d].Contains (theObject)
+        || myObjects[BVHSubset_3dPersistent].Contains (theObject)
+        || myObjects[BVHSubset_2dPersistent].Contains (theObject);
+  }
+
+  //! Returns true if the object set does not contain any selectable objects.
+  Standard_Boolean IsEmpty() const
+  {
+    return myObjects[BVHSubset_3d].IsEmpty()
+        && myObjects[BVHSubset_3dPersistent].IsEmpty()
+        && myObjects[BVHSubset_2dPersistent].IsEmpty();
+  }
+
+  //! Returns true if the specified object subset is empty.
+  Standard_Boolean IsEmpty (const BVHSubset theSubset) const
+  {
+    return myObjects[theSubset].IsEmpty();
+  }
+
+  //! Returns object from subset theSubset by theIndex given. The method allows to get selectable object
+  //! referred by the index of an element of the subset's BVH tree.
+  const Handle(SelectMgr_SelectableObject)& GetObjectById (const BVHSubset theSubset,
+                                                           const Standard_Integer theIndex) const
+  {
+    return myObjects[theSubset].FindKey (theIndex + 1);
+  }
+
+  //! Returns computed BVH for the theSubset given.
+  const NCollection_Handle<BVH_Tree<Standard_Real, 3> >& BVH(const BVHSubset theSubset) const
+  {
+    return myBVH[theSubset];
   }
 
 private:
 
-  NCollection_IndexedMap<Handle(SelectMgr_SelectableObject)> myObjects;
+  //! Returns an appropriate subset of theObject given depending on its persistence type.
+  Standard_Integer appropriateSubset (const Handle(SelectMgr_SelectableObject)& theObject)
+  {
+    if (!theObject->TransformPersistence().Flags)
+    {
+      return SelectMgr_SelectableObjectSet::BVHSubset_3d;
+    }
+    else if (theObject->TransformPersistence().Flags & Graphic3d_TMF_2d)
+    {
+      return SelectMgr_SelectableObjectSet::BVHSubset_2dPersistent;
+    }
+    else
+    {
+      return SelectMgr_SelectableObjectSet::BVHSubset_3dPersistent;
+    }
+  }
+
+  //! Returns current subset of theObject given.
+  Standard_Integer currentSubset (const Handle(SelectMgr_SelectableObject)& theObject)
+  {
+    for (Standard_Integer aSubsetIdx = 0; aSubsetIdx < BVHSubsetNb; ++aSubsetIdx)
+    {
+      if (myObjects[aSubsetIdx].Contains (theObject))
+      {
+        return aSubsetIdx;
+      }
+    }
+    return -1;
+  }
+
+private:
+
+  NCollection_IndexedMap<Handle(SelectMgr_SelectableObject)> myObjects[BVHSubsetNb]; //!< Map of objects for each subset
+  NCollection_Handle<BVH_Tree<Standard_Real, 3> >            myBVH[BVHSubsetNb];     //!< BVH tree computed for each subset
+  NCollection_Handle<BVH_Builder<Standard_Real, 3> >         myBuilder[BVHSubsetNb]; //!< Builder allocated for each subset
+  Standard_Boolean                                           myIsDirty[BVHSubsetNb]; //!< Dirty flag for each subset
+  Graphic3d_WorldViewProjState                               myLastViewState; //!< Last view-projection state used for construction of BVH
+  Standard_Integer                                           myLastWidth; //!< Last viewport's (window's) width used for construction of BVH
+  Standard_Integer                                           myLastHeight; //!< Last viewport's (window's) height used for construction of BVH
+  friend class Iterator;
 };
 
 #endif // _SelectMgr_SelectableObjectSet_HeaderFile
diff --git a/src/SelectMgr/SelectMgr_SelectableObjectTrsfPersSet.cxx b/src/SelectMgr/SelectMgr_SelectableObjectTrsfPersSet.cxx
deleted file mode 100644
index 5cc3125a63..0000000000
--- a/src/SelectMgr/SelectMgr_SelectableObjectTrsfPersSet.cxx
+++ /dev/null
@@ -1,170 +0,0 @@
-// Created on: 2015-06-30
-// Created by: Anton POLETAEV
-// Copyright (c) 2015 OPEN CASCADE SAS
-//
-// This file is part of Open CASCADE Technology software library.
-//
-// This library is free software; you can redistribute it and/or modify it under
-// the terms of the GNU Lesser General Public License version 2.1 as published
-// by the Free Software Foundation, with special exception defined in the file
-// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
-// distribution for complete text of the license and disclaimer of any warranty.
-//
-// Alternatively, this file may be used under the terms of Open CASCADE
-// commercial license or contractual agreement.
-
-#include <SelectMgr_SelectableObjectTrsfPersSet.hxx>
-
-#include <Bnd_Box.hxx>
-#include <BVH_LinearBuilder.hxx>
-#include <SelectMgr_VectorTypes.hxx>
-
-//=======================================================================
-// function : SelectMgr_SelectableObjectTrsfPersSet
-// purpose  :
-//=======================================================================
-SelectMgr_SelectableObjectTrsfPersSet::SelectMgr_SelectableObjectTrsfPersSet()
-: myIsDirty (Standard_False),
-  myBVH (new BVH_Tree<Standard_Real, 3>())
-{
-  myBuilder = new BVH_LinearBuilder<Standard_Real, 3> (1, 32);
-}
-
-//=======================================================================
-// function : Size
-// purpose  :
-//=======================================================================
-Standard_Integer SelectMgr_SelectableObjectTrsfPersSet::Size() const
-{
-  return myObjects.Size();
-}
-
-//=======================================================================
-// function : Box
-// purpose  :
-//=======================================================================
-Select3D_BndBox3d SelectMgr_SelectableObjectTrsfPersSet::Box (const Standard_Integer theIndex) const
-{
-  return *myObjectBoxes (theIndex + 1);
-}
-
-//=======================================================================
-// function : Center
-// purpose  :
-//=======================================================================
-Standard_Real SelectMgr_SelectableObjectTrsfPersSet::Center (const Standard_Integer theIndex,
-                                                             const Standard_Integer theAxis) const
-{
-  const Select3D_BndBox3d& aBndBox = *myObjectBoxes (theIndex + 1);
-
-  return (aBndBox.CornerMin()[theAxis] + aBndBox.CornerMax()[theAxis]) * 0.5;
-}
-
-//=======================================================================
-// function : Swap
-// purpose  :
-//=======================================================================
-void SelectMgr_SelectableObjectTrsfPersSet::Swap (const Standard_Integer theIndex1,
-                                                  const Standard_Integer theIndex2)
-{
-  const Standard_Integer aStructIdx1 = theIndex1 + 1;
-  const Standard_Integer aStructIdx2 = theIndex2 + 1;
-
-  myObjects.Swap (aStructIdx1, aStructIdx2);
-  myObjectBoxes.Swap (aStructIdx1, aStructIdx2);
-}
-
-//=======================================================================
-// function : Append
-// purpose  :
-//=======================================================================
-Standard_Boolean SelectMgr_SelectableObjectTrsfPersSet::Append (const Handle(SelectMgr_SelectableObject)& theObject)
-{
-  Standard_Integer aSize = Size();
-
-  if (aSize < myObjects.Add (theObject))
-  {
-    MarkDirty();
-
-    return Standard_True;
-  }
-
-  return Standard_False;
-}
-
-//=======================================================================
-// function : Remove
-// purpose  :
-//=======================================================================
-Standard_Boolean SelectMgr_SelectableObjectTrsfPersSet::Remove (const Handle(SelectMgr_SelectableObject)& theObject)
-{
-  const Standard_Integer anIndex = myObjects.FindIndex (theObject);
-
-  if (anIndex != 0)
-  {
-    myObjects.Swap (Size(), anIndex);
-    myObjects.RemoveLast();
-    MarkDirty();
-
-    return Standard_True;
-  }
-
-  return Standard_False;
-}
-
-//=======================================================================
-// function : BVH
-// purpose  :
-//=======================================================================
-const NCollection_Handle<BVH_Tree<Standard_Real, 3> >&
-  SelectMgr_SelectableObjectTrsfPersSet::BVH (const Handle(Graphic3d_Camera)& theCamera,
-                                              const Graphic3d_Mat4d& theProjectionMatrix,
-                                              const Graphic3d_Mat4d& theWorldViewMatrix,
-                                              const Standard_Integer theViewportWidth,
-                                              const Standard_Integer theViewportHeight,
-                                              const Graphic3d_WorldViewProjState& theWVPState)
-{
-  if (!myIsDirty && (myObjectBoxesState.IsValid() && !myObjectBoxesState.IsChanged(theWVPState)))
-  {
-    return myBVH;
-  }
-
-  myObjectBoxes.ReSize (myObjects.Size());
-
-  for (Standard_Integer anObjectIdx = 1; anObjectIdx <= myObjects.Size(); ++anObjectIdx)
-  {
-    const Handle(SelectMgr_SelectableObject)& anObject = myObjects (anObjectIdx);
-
-    Bnd_Box aBoundingBox;
-
-    if (anObject->TransformPersistence().Flags != 0)
-    {
-      anObject->BoundingBox (aBoundingBox);
-      if (!aBoundingBox.IsVoid())
-      {
-        anObject->TransformPersistence().Apply (theCamera, theProjectionMatrix, theWorldViewMatrix, theViewportWidth, theViewportHeight, aBoundingBox);
-      }
-    }
-
-    if (aBoundingBox.IsVoid())
-    {
-      myObjectBoxes.Add (new Select3D_BndBox3d());
-    }
-    else
-    {
-      gp_Pnt aMin = aBoundingBox.CornerMin();
-      gp_Pnt aMax = aBoundingBox.CornerMax();
-
-      myObjectBoxes.Add (new Select3D_BndBox3d (Select3D_Vec3 (aMin.X(), aMin.Y(), aMin.Z()),
-                                                Select3D_Vec3 (aMax.X(), aMax.Y(), aMax.Z())));
-    }
-  }
-
-  myBuilder->Build (this, myBVH.operator->(), BVH_Set<Standard_Real, 3>::Box());
-
-  myObjectBoxesState = theWVPState;
-  myObjectBoxes.Clear();
-  myIsDirty = Standard_False;
-
-  return myBVH;
-}
diff --git a/src/SelectMgr/SelectMgr_SelectableObjectTrsfPersSet.hxx b/src/SelectMgr/SelectMgr_SelectableObjectTrsfPersSet.hxx
deleted file mode 100644
index 86580c294f..0000000000
--- a/src/SelectMgr/SelectMgr_SelectableObjectTrsfPersSet.hxx
+++ /dev/null
@@ -1,118 +0,0 @@
-// Created on: 2015-06-30
-// Created by: Anton POLETAEV
-// Copyright (c) 2015 OPEN CASCADE SAS
-//
-// This file is part of Open CASCADE Technology software library.
-//
-// This library is free software; you can redistribute it and/or modify it under
-// the terms of the GNU Lesser General Public License version 2.1 as published
-// by the Free Software Foundation, with special exception defined in the file
-// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
-// distribution for complete text of the license and disclaimer of any warranty.
-//
-// Alternatively, this file may be used under the terms of Open CASCADE
-// commercial license or contractual agreement.
-
-#ifndef _SelectMgr_SelectableObjectTrsfPersSet_HeaderFile
-#define _SelectMgr_SelectableObjectTrsfPersSet_HeaderFile
-
-#include <BVH_Builder.hxx>
-#include <BVH_Set.hxx>
-#include <BVH_Tree.hxx>
-#include <Graphic3d_Camera.hxx>
-#include <Graphic3d_WorldViewProjState.hxx>
-#include <NCollection_Handle.hxx>
-#include <NCollection_IndexedMap.hxx>
-#include <Select3D_BndBox3d.hxx>
-#include <SelectMgr_SelectableObject.hxx>
-
-//! Primitive set specialized for transformation persistent selectable objects.
-//! Provides built-in mechanism to invalidate tree when world view projection state changes.
-//! Due to frequent invalidation of BVH tree the choice of BVH tree builder is made
-//! in favor of BVH linear builder (quick rebuild).
-class SelectMgr_SelectableObjectTrsfPersSet : public BVH_Set<Standard_Real, 3>
-{
-private:
-
-  typedef NCollection_Handle<Select3D_BndBox3d> HBndBox3d;
-
-  Handle(SelectMgr_SelectableObject) EMPTY_OBJ;
-
-public:
-
-  //! Creates new empty objects set and initializes BVH tree
-  //! builder to Linear builder with 1 element per list.
-  Standard_EXPORT SelectMgr_SelectableObjectTrsfPersSet();
-
-  //! Returns size of objects set.
-  Standard_EXPORT virtual Standard_Integer Size() const Standard_OVERRIDE;
-
-  //! Returns bounding box of object with index theIndex.
-  Standard_EXPORT virtual Select3D_BndBox3d Box (const Standard_Integer theIndex) const Standard_OVERRIDE;
-
-  //! Returns center of object with index theIndex in the set along the given axis theAxis.
-  Standard_EXPORT virtual Standard_Real Center (const Standard_Integer theIndex,
-                                                const Standard_Integer theAxis) const Standard_OVERRIDE;
-
-  //! Swaps items with indexes theIndex1 and theIndex2 in the set.
-  Standard_EXPORT virtual void Swap (const Standard_Integer theIndex1,
-                                     const Standard_Integer theIndex2) Standard_OVERRIDE;
-
-  //! Adds new selectable object to the set.
-  //! @return true if structure added, otherwise returns false (structure already in the set).
-  Standard_EXPORT Standard_Boolean Append (const Handle(SelectMgr_SelectableObject)& theObject);
-
-  //! Removes selectable object from set.
-  //! @return true if structure removed, otherwise returns false (structure is not in the set).
-  Standard_EXPORT Standard_Boolean Remove (const Handle(SelectMgr_SelectableObject)& theObject);
-
-  //! Returns object from set by theIndex given.
-  const Handle(SelectMgr_SelectableObject)& GetObjectById (const Standard_Integer theIndex) const
-  {
-    return myObjects.FindKey (theIndex + 1);
-  }
-
-  //! Marks object state as outdated (needs BVH rebuilding).
-  void MarkDirty()
-  {
-    myIsDirty = Standard_True;
-  }
-
-  //! Returns true if this objects set contains theObject given
-  Standard_Boolean Contains (const Handle(SelectMgr_SelectableObject)& theObject) const
-  {
-    return myObjects.Contains (theObject);
-  }
-
-  //! Returns BVH tree for the given world view projection (builds it if necessary).
-  Standard_EXPORT const NCollection_Handle<BVH_Tree<Standard_Real, 3> >& BVH (const Handle(Graphic3d_Camera)& theCamera,
-                                                                              const Graphic3d_Mat4d& theProjectionMatrix,
-                                                                              const Graphic3d_Mat4d& theWorldViewMatrix,
-                                                                              const Standard_Integer theViewportWidth,
-                                                                              const Standard_Integer theViewportHeight,
-                                                                              const Graphic3d_WorldViewProjState& theWVPState);
-
-private:
-
-  //! Marks internal object state as outdated.
-  Standard_Boolean myIsDirty;
-
-  //! Constructed bottom-level BVH.
-  NCollection_Handle<BVH_Tree<Standard_Real, 3> > myBVH;
-
-  //! Builder for bottom-level BVH.
-  NCollection_Handle<BVH_Builder<Standard_Real, 3> > myBuilder;
-
-  //! Set of transform persistence objects.
-  NCollection_IndexedMap<Handle(SelectMgr_SelectableObject)> myObjects;
-
-  //! Cached set of bounding boxes precomputed for transformation persistent selectable objects.
-  //! Cache exists only during computation of BVH Tree. Bounding boxes are world view projection
-  //! dependent and should by synchronized.
-  NCollection_IndexedMap<HBndBox3d> myObjectBoxes;
-
-  //! State of world view projection used for generation of transformation persistence bounding boxes.
-  Graphic3d_WorldViewProjState myObjectBoxesState;
-};
-
-#endif
diff --git a/src/SelectMgr/SelectMgr_SelectingVolumeManager.cxx b/src/SelectMgr/SelectMgr_SelectingVolumeManager.cxx
index 53406659e1..fd1b0588a7 100644
--- a/src/SelectMgr/SelectMgr_SelectingVolumeManager.cxx
+++ b/src/SelectMgr/SelectMgr_SelectingVolumeManager.cxx
@@ -42,9 +42,13 @@ SelectMgr_SelectingVolumeManager::SelectMgr_SelectingVolumeManager (Standard_Boo
 //                - transformation only is needed: @theScaleFactor must be initialized
 //                  as any negative value;
 //                - scale only is needed: @theTrsf must be set to gp_Identity.
+//            Builder is an optional argument that represents corresponding settings for
+//            re-constructing transformed frustum from scratch. Can be null if reconstruction
+//            is not needed furthermore in the code.
 //=======================================================================
 SelectMgr_SelectingVolumeManager SelectMgr_SelectingVolumeManager::ScaleAndTransform (const Standard_Integer theScaleFactor,
-                                                                                      const gp_GTrsf& theTrsf)
+                                                                                      const gp_GTrsf& theTrsf,
+                                                                                      const Handle(SelectMgr_FrustumBuilder)& theBuilder) const
 {
   SelectMgr_SelectingVolumeManager aMgr (Standard_False);
 
@@ -52,10 +56,10 @@ SelectMgr_SelectingVolumeManager SelectMgr_SelectingVolumeManager::ScaleAndTrans
     return aMgr;
 
   aMgr.myActiveSelectionType = myActiveSelectionType;
-
   aMgr.mySelectingVolumes[myActiveSelectionType / 2]
     = mySelectingVolumes[myActiveSelectionType / 2]->ScaleAndTransform (theScaleFactor, theTrsf);
   aMgr.myToAllowOverlap = myToAllowOverlap;
+  aMgr.mySelectingVolumes[myActiveSelectionType / 2]->SetBuilder (theBuilder);
 
   return aMgr;
 }
@@ -141,7 +145,7 @@ const Graphic3d_WorldViewProjState& SelectMgr_SelectingVolumeManager::WorldViewP
 // function : WindowSize
 // purpose  :
 //=======================================================================
-void SelectMgr_SelectingVolumeManager::WindowSize (Standard_Integer& theWidth, Standard_Integer& theHeight)
+void SelectMgr_SelectingVolumeManager::WindowSize (Standard_Integer& theWidth, Standard_Integer& theHeight) const
 {
   mySelectingVolumes[Frustum]->WindowSize (theWidth, theHeight);
 }
diff --git a/src/SelectMgr/SelectMgr_SelectingVolumeManager.hxx b/src/SelectMgr/SelectMgr_SelectingVolumeManager.hxx
index 4d1b8feb8b..d06b5ef1e8 100644
--- a/src/SelectMgr/SelectMgr_SelectingVolumeManager.hxx
+++ b/src/SelectMgr/SelectMgr_SelectingVolumeManager.hxx
@@ -46,8 +46,11 @@ public:
   //! 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.
+  //! Builder is an optional argument that represents corresponding settings for re-constructing transformed
+  //! frustum from scratch. Can be null if reconstruction is not expected furthermore.
   Standard_EXPORT virtual SelectMgr_SelectingVolumeManager ScaleAndTransform (const Standard_Integer theScaleFactor,
-                                                                              const gp_GTrsf& theTrsf);
+                                                                              const gp_GTrsf& theTrsf,
+                                                                              const Handle(SelectMgr_FrustumBuilder)& theBuilder = NULL) const;
 
   Standard_EXPORT virtual Standard_Integer GetActiveSelectionType() const Standard_OVERRIDE;
 
@@ -71,7 +74,7 @@ public:
   //! @return current world view transformation common for all selecting volumes
   Standard_EXPORT const Graphic3d_Mat4d& WorldViewMatrix() const;
 
-  Standard_EXPORT void WindowSize (Standard_Integer& theWidth, Standard_Integer& theHeight);
+  Standard_EXPORT void WindowSize (Standard_Integer& theWidth, Standard_Integer& theHeight) const;
 
   //! @return current camera world view projection transformation state common for all selecting volumes
   Standard_EXPORT const Graphic3d_WorldViewProjState& WorldViewProjState() const;
@@ -189,8 +192,8 @@ public:
 private:
   enum { Frustum, FrustumSet, VolumeTypesNb };       //!< Defines the amount of available selecting volumes
 
-  NCollection_Handle<SelectMgr_BaseFrustum> mySelectingVolumes[VolumeTypesNb];      //!< Array of selecting volumes
-  Standard_Boolean                          myToAllowOverlap;      //!< Defines if partially overlapped entities will me detected or not
+  Handle(SelectMgr_BaseFrustum) mySelectingVolumes[VolumeTypesNb];      //!< Array of selecting volumes
+  Standard_Boolean              myToAllowOverlap;      //!< Defines if partially overlapped entities will me detected or not
 };
 
-#endif
+#endif
\ No newline at end of file
diff --git a/src/SelectMgr/SelectMgr_TriangularFrustum.cxx b/src/SelectMgr/SelectMgr_TriangularFrustum.cxx
index d67f7c788c..9c318cc2e0 100644
--- a/src/SelectMgr/SelectMgr_TriangularFrustum.cxx
+++ b/src/SelectMgr/SelectMgr_TriangularFrustum.cxx
@@ -44,7 +44,7 @@ namespace
 // purpose  : Caches projection of frustum's vertices onto its plane directions
 //            and {i, j, k}
 // =======================================================================
-void SelectMgr_TriangularFrustum::cacheVertexProjections (SelectMgr_TriangularFrustum* theFrustum)
+void SelectMgr_TriangularFrustum::cacheVertexProjections (SelectMgr_TriangularFrustum* theFrustum) const
 {
   for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < 5; ++aPlaneIdx)
   {
@@ -128,10 +128,10 @@ void SelectMgr_TriangularFrustum::Build (const gp_Pnt2d& theP1,
 //                  as any negative value;
 //                - scale only is needed: @theTrsf must be set to gp_Identity.
 //=======================================================================
-NCollection_Handle<SelectMgr_BaseFrustum> SelectMgr_TriangularFrustum::ScaleAndTransform (const Standard_Integer /*theScale*/,
-                                                                                          const gp_GTrsf& theTrsf)
+Handle(SelectMgr_BaseFrustum) SelectMgr_TriangularFrustum::ScaleAndTransform (const Standard_Integer /*theScale*/,
+                                                                              const gp_GTrsf& theTrsf) const
 {
-  SelectMgr_TriangularFrustum* aRes = new SelectMgr_TriangularFrustum();
+  Handle(SelectMgr_TriangularFrustum) aRes = new SelectMgr_TriangularFrustum();
 
   for (Standard_Integer anIt = 0; anIt < 6; anIt++)
   {
@@ -157,9 +157,9 @@ NCollection_Handle<SelectMgr_BaseFrustum> SelectMgr_TriangularFrustum::ScaleAndT
 
   computeFrustumNormals (aRes->myEdgeDirs, aRes->myPlanes);
 
-  cacheVertexProjections (aRes);
+  cacheVertexProjections (aRes.get());
 
-  return NCollection_Handle<SelectMgr_BaseFrustum> (aRes);
+  return aRes;
 }
 
 //=======================================================================
diff --git a/src/SelectMgr/SelectMgr_TriangularFrustum.hxx b/src/SelectMgr/SelectMgr_TriangularFrustum.hxx
index 3e6c8da7b1..46a9443e21 100644
--- a/src/SelectMgr/SelectMgr_TriangularFrustum.hxx
+++ b/src/SelectMgr/SelectMgr_TriangularFrustum.hxx
@@ -40,8 +40,8 @@ public:
                                       const gp_Pnt2d& theP3) Standard_OVERRIDE;
 
   //! Returns a copy of the frustum transformed according to the matrix given
-  Standard_EXPORT virtual NCollection_Handle<SelectMgr_BaseFrustum> ScaleAndTransform (const Standard_Integer theScale,
-                                                                                       const gp_GTrsf& theTrsf) Standard_OVERRIDE;
+  Standard_EXPORT virtual Handle(SelectMgr_BaseFrustum) ScaleAndTransform (const Standard_Integer theScale,
+                                                                           const gp_GTrsf& theTrsf) const Standard_OVERRIDE;
 
   // SAT Tests for different objects
 
@@ -86,7 +86,7 @@ public:
 
 private:
 
-  void cacheVertexProjections (SelectMgr_TriangularFrustum* theFrustum);
+  void cacheVertexProjections (SelectMgr_TriangularFrustum* theFrustum) const;
 
   DEFINE_STANDARD_RTTIEXT(SelectMgr_TriangularFrustum,Standard_Transient)
 };
diff --git a/src/SelectMgr/SelectMgr_TriangularFrustumSet.cxx b/src/SelectMgr/SelectMgr_TriangularFrustumSet.cxx
index efce1f80dd..202b84e1b0 100644
--- a/src/SelectMgr/SelectMgr_TriangularFrustumSet.cxx
+++ b/src/SelectMgr/SelectMgr_TriangularFrustumSet.cxx
@@ -108,17 +108,17 @@ void SelectMgr_TriangularFrustumSet::Build (const TColgp_Array1OfPnt2d& thePoint
 //                  as any negative value;
 //                - scale only is needed: @theTrsf must be set to gp_Identity.
 // =======================================================================
-NCollection_Handle<SelectMgr_BaseFrustum> SelectMgr_TriangularFrustumSet::ScaleAndTransform (const Standard_Integer theScale,
-                                                                                             const gp_GTrsf& theTrsf)
+Handle(SelectMgr_BaseFrustum) SelectMgr_TriangularFrustumSet::ScaleAndTransform (const Standard_Integer theScale,
+                                                                                 const gp_GTrsf& theTrsf) const
 {
-  SelectMgr_TriangularFrustumSet* aRes = new SelectMgr_TriangularFrustumSet();
+  Handle(SelectMgr_TriangularFrustumSet) aRes = new SelectMgr_TriangularFrustumSet();
 
   for (SelectMgr_TriangFrustumsIter anIter (myFrustums); anIter.More(); anIter.Next())
   {
     aRes->myFrustums.Append (Handle(SelectMgr_TriangularFrustum)::DownCast (anIter.Value()->ScaleAndTransform (theScale, theTrsf)));
   }
 
-  return NCollection_Handle<SelectMgr_BaseFrustum> (aRes);
+  return aRes;
 }
 
 // =======================================================================
diff --git a/src/SelectMgr/SelectMgr_TriangularFrustumSet.hxx b/src/SelectMgr/SelectMgr_TriangularFrustumSet.hxx
index 474bb355fa..09f285b0a8 100644
--- a/src/SelectMgr/SelectMgr_TriangularFrustumSet.hxx
+++ b/src/SelectMgr/SelectMgr_TriangularFrustumSet.hxx
@@ -47,8 +47,8 @@ public:
   Standard_EXPORT virtual void Build (const TColgp_Array1OfPnt2d& thePoints) Standard_OVERRIDE;
 
   //! Returns a copy of the frustum with all sub-volumes transformed according to the matrix given
-  Standard_EXPORT virtual NCollection_Handle<SelectMgr_BaseFrustum> ScaleAndTransform (const Standard_Integer theScale,
-                                                                                       const gp_GTrsf& theTrsf) Standard_OVERRIDE;
+  Standard_EXPORT virtual Handle(SelectMgr_BaseFrustum) ScaleAndTransform (const Standard_Integer theScale,
+                                                                           const gp_GTrsf& theTrsf) const Standard_OVERRIDE;
 
   Standard_EXPORT virtual Standard_Boolean Overlaps (const SelectMgr_Vec3& theMinPnt,
                                                      const SelectMgr_Vec3& theMaxPnt,
diff --git a/src/SelectMgr/SelectMgr_ViewerSelector.cxx b/src/SelectMgr/SelectMgr_ViewerSelector.cxx
index 428bfe0cb7..ee209239ad 100644
--- a/src/SelectMgr/SelectMgr_ViewerSelector.cxx
+++ b/src/SelectMgr/SelectMgr_ViewerSelector.cxx
@@ -76,6 +76,7 @@ namespace {
     }
   }
 
+  static const Graphic3d_Mat4d THE_IDENTITY_MAT;
 }
 
 //==================================================
@@ -218,6 +219,7 @@ void SelectMgr_ViewerSelector::checkOverlap (const Handle(SelectBasics_Sensitive
 //           necessary calculations
 //=======================================================================
 void SelectMgr_ViewerSelector::computeFrustum (const Handle(SelectBasics_SensitiveEntity)& theEnt,
+                                               const SelectMgr_SelectingVolumeManager&     theMgr,
                                                const gp_GTrsf&                             theInvTrsf,
                                                SelectMgr_FrustumCache&                     theCachedMgrs,
                                                SelectMgr_SelectingVolumeManager&           theResMgr)
@@ -228,19 +230,19 @@ void SelectMgr_ViewerSelector::computeFrustum (const Handle(SelectBasics_Sensiti
   const Standard_Boolean toTransform = aTrsfMtr.Form() != gp_Identity;
   if (toScale && toTransform)
   {
-    theResMgr = mySelectingVolumeMgr.ScaleAndTransform (aScale, aTrsfMtr);
+    theResMgr = theMgr.ScaleAndTransform (aScale, aTrsfMtr, NULL);
   }
   else if (toScale)
   {
     if (!theCachedMgrs.IsBound (aScale))
     {
-      theCachedMgrs.Bind (aScale, mySelectingVolumeMgr.ScaleAndTransform (aScale, gp_Trsf()));
+      theCachedMgrs.Bind (aScale, theMgr.ScaleAndTransform (aScale, gp_Trsf(), NULL));
     }
     theResMgr = theCachedMgrs.Find (aScale);
   }
   else if (toTransform)
   {
-    theResMgr = mySelectingVolumeMgr.ScaleAndTransform (1, aTrsfMtr);
+    theResMgr = theMgr.ScaleAndTransform (1, aTrsfMtr, NULL);
   }
 }
 
@@ -250,7 +252,13 @@ void SelectMgr_ViewerSelector::computeFrustum (const Handle(SelectBasics_Sensiti
 //           between some entity of selectable object theObject and
 //           current selecting volume
 //=======================================================================
-void SelectMgr_ViewerSelector::traverseObject (const Handle(SelectMgr_SelectableObject)& theObject)
+void SelectMgr_ViewerSelector::traverseObject (const Handle(SelectMgr_SelectableObject)& theObject,
+                                               const SelectMgr_SelectingVolumeManager& theMgr,
+                                               const Handle(Graphic3d_Camera)& theCamera,
+                                               const Graphic3d_Mat4d& theProjectionMat,
+                                               const Graphic3d_Mat4d& theWorldViewMat,
+                                               const Standard_Integer theViewportWidth,
+                                               const Standard_Integer theViewportHeight)
 {
   NCollection_Handle<SelectMgr_SensitiveEntitySet>& anEntitySet =
     myMapOfObjectSensitives.ChangeFind (theObject);
@@ -270,15 +278,10 @@ void SelectMgr_ViewerSelector::traverseObject (const Handle(SelectMgr_Selectable
     }
     else
     {
-      const Graphic3d_Mat4d& aProjection = mySelectingVolumeMgr.ProjectionMatrix();
-      const Graphic3d_Mat4d& aWorldView  = mySelectingVolumeMgr.WorldViewMatrix();
-
-      Standard_Integer aViewportWidth;
-      Standard_Integer aViewportHeight;
-      mySelectingVolumeMgr.WindowSize (aViewportWidth, aViewportHeight);
-
       gp_GTrsf aTPers;
-      Graphic3d_Mat4d aMat = theObject->TransformPersistence().Compute (mySelectingVolumeMgr.Camera(), aProjection, aWorldView, aViewportWidth, aViewportHeight);
+      Graphic3d_Mat4d aMat = theObject->TransformPersistence().Compute (
+        theCamera, theProjectionMat, theWorldViewMat, theViewportWidth, theViewportHeight);
+
       aTPers.SetValue (1, 1, aMat.GetValue (0, 0));
       aTPers.SetValue (1, 2, aMat.GetValue (0, 1));
       aTPers.SetValue (1, 3, aMat.GetValue (0, 2));
@@ -295,8 +298,8 @@ void SelectMgr_ViewerSelector::traverseObject (const Handle(SelectMgr_Selectable
   }
 
   SelectMgr_SelectingVolumeManager aMgr = aInversedTrsf.Form() != gp_Identity
-                                        ? mySelectingVolumeMgr.ScaleAndTransform (1, aInversedTrsf)
-                                        : mySelectingVolumeMgr;
+                                        ? theMgr.ScaleAndTransform (1, aInversedTrsf, NULL)
+                                        : theMgr;
 
   SelectMgr_FrustumCache aScaledTrnsfFrustums;
 
@@ -353,7 +356,7 @@ void SelectMgr_ViewerSelector::traverseObject (const Handle(SelectMgr_Selectable
         {
           const Handle(SelectBasics_SensitiveEntity)& anEnt = aSensitive->BaseSensitive();
           SelectMgr_SelectingVolumeManager aTmpMgr = aMgr;
-          computeFrustum (anEnt, aInversedTrsf, aScaledTrnsfFrustums, aTmpMgr);
+          computeFrustum (anEnt, theMgr, aInversedTrsf, aScaledTrnsfFrustums, aTmpMgr);
           checkOverlap (anEnt, aInversedTrsf, aTmpMgr);
         }
       }
@@ -377,36 +380,68 @@ void SelectMgr_ViewerSelector::TraverseSensitives()
 {
   mystored.Clear();
 
-  NCollection_Handle<BVH_Tree<Standard_Real, 3> > aBVHTree;
-  for (Standard_Integer aBVHTreeIdx = 0; aBVHTreeIdx < 2; ++aBVHTreeIdx)
+  Standard_Integer aWidth;
+  Standard_Integer aHeight;
+  mySelectingVolumeMgr.WindowSize (aWidth, aHeight);
+  mySelectableObjects.UpdateBVH (mySelectingVolumeMgr.Camera(),
+                                 mySelectingVolumeMgr.ProjectionMatrix(),
+                                 mySelectingVolumeMgr.WorldViewMatrix(),
+                                 mySelectingVolumeMgr.WorldViewProjState(),
+                                 aWidth, aHeight);
+
+  for (Standard_Integer aBVHSetIt = 0; aBVHSetIt < SelectMgr_SelectableObjectSet::BVHSubsetNb; ++aBVHSetIt)
   {
-    const Standard_Boolean isTrsfPers = aBVHTreeIdx == 1;
-    if (isTrsfPers)
+    SelectMgr_SelectableObjectSet::BVHSubset aBVHSubset =
+      static_cast<SelectMgr_SelectableObjectSet::BVHSubset> (aBVHSetIt);
+
+    if (mySelectableObjects.IsEmpty (aBVHSubset))
     {
-      if (mySelectableObjectsTrsfPers.Size() == 0)
-      {
-        continue;
-      }
-      const Graphic3d_Mat4d& aProjection            = mySelectingVolumeMgr.ProjectionMatrix();
-      const Graphic3d_Mat4d& aWorldView             = mySelectingVolumeMgr.WorldViewMatrix();
-      const Graphic3d_WorldViewProjState& aWVPState = mySelectingVolumeMgr.WorldViewProjState();
-      Standard_Integer aViewportWidth;
-      Standard_Integer aViewportHeight;
-      mySelectingVolumeMgr.WindowSize (aViewportWidth, aViewportHeight);
-      aBVHTree = mySelectableObjectsTrsfPers.BVH (mySelectingVolumeMgr.Camera(), aProjection, aWorldView, aViewportWidth, aViewportHeight, aWVPState);
+      continue;
+    }
+
+    gp_GTrsf aTFrustum;
+
+    SelectMgr_SelectingVolumeManager aMgr (Standard_False);
+
+    // for 2D space selection transform selecting volumes to perform overap testing
+    // directly in camera's eye space omitting the camera position, which is not
+    // needed there at all
+    if (aBVHSubset == SelectMgr_SelectableObjectSet::BVHSubset_2dPersistent)
+    {
+      const Graphic3d_Mat4d& aMat = mySelectingVolumeMgr.WorldViewMatrix();
+      aTFrustum.SetValue (1, 1, aMat.GetValue (0, 0));
+      aTFrustum.SetValue (1, 2, aMat.GetValue (0, 1));
+      aTFrustum.SetValue (1, 3, aMat.GetValue (0, 2));
+      aTFrustum.SetValue (2, 1, aMat.GetValue (1, 0));
+      aTFrustum.SetValue (2, 2, aMat.GetValue (1, 1));
+      aTFrustum.SetValue (2, 3, aMat.GetValue (1, 2));
+      aTFrustum.SetValue (3, 1, aMat.GetValue (2, 0));
+      aTFrustum.SetValue (3, 2, aMat.GetValue (2, 1));
+      aTFrustum.SetValue (3, 3, aMat.GetValue (2, 2));
+      aTFrustum.SetTranslationPart (gp_XYZ (aMat.GetValue (0, 3), aMat.GetValue (1, 3), aMat.GetValue (2, 3)));
+
+      // define corresponding frustum builder parameters
+      Handle(SelectMgr_FrustumBuilder) aBuilder = new SelectMgr_FrustumBuilder();
+      aBuilder->SetProjectionMatrix (mySelectingVolumeMgr.ProjectionMatrix());
+      aBuilder->SetWorldViewMatrix (THE_IDENTITY_MAT);
+      aBuilder->SetWindowSize (aWidth, aHeight);
+      aMgr = mySelectingVolumeMgr.ScaleAndTransform (1, aTFrustum, aBuilder);
     }
     else
     {
-      if (mySelectableObjects.Size() == 0)
-      {
-        continue;
-      }
-      aBVHTree = mySelectableObjects.BVH();
+      aMgr = mySelectingVolumeMgr;
     }
 
+    const Handle(Graphic3d_Camera)& aCamera = mySelectingVolumeMgr.Camera();
+    const Graphic3d_Mat4d& aProjectionMat   = mySelectingVolumeMgr.ProjectionMatrix();
+    const Graphic3d_Mat4d& aWorldViewMat    = aBVHSubset != SelectMgr_SelectableObjectSet::BVHSubset_2dPersistent
+                                            ? mySelectingVolumeMgr.WorldViewMatrix()
+                                            : THE_IDENTITY_MAT;
+
+    const NCollection_Handle<BVH_Tree<Standard_Real, 3> >& aBVHTree = mySelectableObjects.BVH (aBVHSubset);
+
     Standard_Integer aNode = 0;
-    if (!mySelectingVolumeMgr.Overlaps (aBVHTree->MinPoint (0),
-                                        aBVHTree->MaxPoint (0)))
+    if (!aMgr.Overlaps (aBVHTree->MinPoint (0), aBVHTree->MaxPoint (0)))
     {
       continue;
     }
@@ -420,11 +455,9 @@ void SelectMgr_ViewerSelector::TraverseSensitives()
         const Standard_Integer aLeftChildIdx  = aBVHTree->Child<0> (aNode);
         const Standard_Integer aRightChildIdx = aBVHTree->Child<1> (aNode);
         const Standard_Boolean isLeftChildIn  =
-          mySelectingVolumeMgr.Overlaps (aBVHTree->MinPoint (aLeftChildIdx),
-                                         aBVHTree->MaxPoint (aLeftChildIdx));
+          aMgr.Overlaps (aBVHTree->MinPoint (aLeftChildIdx), aBVHTree->MaxPoint (aLeftChildIdx));
         const Standard_Boolean isRightChildIn =
-          mySelectingVolumeMgr.Overlaps (aBVHTree->MinPoint (aRightChildIdx),
-                                         aBVHTree->MaxPoint (aRightChildIdx));
+          aMgr.Overlaps (aBVHTree->MinPoint (aRightChildIdx), aBVHTree->MaxPoint (aRightChildIdx));
         if (isLeftChildIn
           && isRightChildIn)
         {
@@ -454,11 +487,10 @@ void SelectMgr_ViewerSelector::TraverseSensitives()
         Standard_Integer anEndIdx  = aBVHTree->EndPrimitive (aNode);
         for (Standard_Integer anIdx = aStartIdx; anIdx <= anEndIdx; ++anIdx)
         {
-          Handle(SelectMgr_SelectableObject) aSelectableObject =
-            isTrsfPers ? mySelectableObjectsTrsfPers.GetObjectById (anIdx)
-                       : mySelectableObjects.GetObjectById (anIdx);
+          const Handle(SelectMgr_SelectableObject)& aSelectableObject =
+            mySelectableObjects.GetObjectById (aBVHSubset, anIdx);
 
-          traverseObject (aSelectableObject);
+          traverseObject (aSelectableObject, aMgr, aCamera, aProjectionMat, aWorldViewMat, aWidth, aHeight);
         }
         if (aHead < 0)
         {
@@ -528,8 +560,7 @@ const SelectMgr_SortCriterion& SelectMgr_ViewerSelector::PickedData(const Standa
 //==================================================
 Standard_Boolean SelectMgr_ViewerSelector::Contains (const Handle(SelectMgr_SelectableObject)& theObject) const
 {
-  return mySelectableObjects.Contains (theObject)
-      || mySelectableObjectsTrsfPers.Contains (theObject);
+  return mySelectableObjects.Contains (theObject);
 }
 
 //==================================================
@@ -691,15 +722,7 @@ void SelectMgr_ViewerSelector::AddSelectableObject (const Handle(SelectMgr_Selec
 {
   if (!myMapOfObjectSensitives.IsBound (theObject))
   {
-    if (!theObject->TransformPersistence().Flags)
-    {
-      mySelectableObjects.Append (theObject);
-    }
-    else
-    {
-      mySelectableObjectsTrsfPers.Append (theObject);
-    }
-
+    mySelectableObjects.Append (theObject);
     NCollection_Handle<SelectMgr_SensitiveEntitySet> anEntitySet = new SelectMgr_SensitiveEntitySet();
     myMapOfObjectSensitives.Bind (theObject, anEntitySet);
   }
@@ -732,19 +755,7 @@ void SelectMgr_ViewerSelector::AddSelectionToObject (const Handle(SelectMgr_Sele
 //=======================================================================
 void SelectMgr_ViewerSelector::MoveSelectableObject (const Handle(SelectMgr_SelectableObject)& theObject)
 {
-  if (!mySelectableObjects.Remove (theObject))
-  {
-    mySelectableObjectsTrsfPers.Remove (theObject);
-  }
-
-  if (!theObject->TransformPersistence().Flags)
-  {
-    mySelectableObjects.Append (theObject);
-  }
-  else
-  {
-    mySelectableObjectsTrsfPers.Append (theObject);
-  }
+  mySelectableObjects.ChangeSubset (theObject);
 }
 
 //=======================================================================
@@ -755,10 +766,7 @@ void SelectMgr_ViewerSelector::RemoveSelectableObject (const Handle(SelectMgr_Se
 {
   if (myMapOfObjectSensitives.IsBound (theObject))
   {
-    if (!mySelectableObjects.Remove (theObject))
-    {
-      mySelectableObjectsTrsfPers.Remove (theObject);
-    }
+    mySelectableObjects.Remove (theObject);
     myMapOfObjectSensitives.UnBind (theObject);
   }
 }
@@ -786,19 +794,20 @@ void SelectMgr_ViewerSelector::RemoveSelectionOfObject (const Handle(SelectMgr_S
 void SelectMgr_ViewerSelector::RebuildObjectsTree (const Standard_Boolean theIsForce)
 {
   mySelectableObjects.MarkDirty();
-  mySelectableObjectsTrsfPers.MarkDirty();
 
   if (theIsForce)
   {
-    const Graphic3d_Mat4d& aProjection            = mySelectingVolumeMgr.ProjectionMatrix();
-    const Graphic3d_Mat4d& aWorldView             = mySelectingVolumeMgr.WorldViewMatrix();
-    const Graphic3d_WorldViewProjState& aWVPState = mySelectingVolumeMgr.WorldViewProjState();
-    Standard_Integer aViewportWidth;
-    Standard_Integer aViewportHeight;
+    Standard_Integer aViewportWidth, aViewportHeight;
     mySelectingVolumeMgr.WindowSize (aViewportWidth, aViewportHeight);
 
-    mySelectableObjects.BVH();
-    mySelectableObjectsTrsfPers.BVH (mySelectingVolumeMgr.Camera(), aProjection, aWorldView, aViewportWidth, aViewportHeight, aWVPState);
+    Standard_Integer aWidth;
+    Standard_Integer aHeight;
+    mySelectingVolumeMgr.WindowSize (aWidth, aHeight);
+    mySelectableObjects.UpdateBVH (mySelectingVolumeMgr.Camera(),
+                                   mySelectingVolumeMgr.ProjectionMatrix(),
+                                   mySelectingVolumeMgr.WorldViewMatrix(),
+                                   mySelectingVolumeMgr.WorldViewProjState(),
+                                   aWidth, aHeight);
   }
 }
 
diff --git a/src/SelectMgr/SelectMgr_ViewerSelector.hxx b/src/SelectMgr/SelectMgr_ViewerSelector.hxx
index 30f033fd76..17c7ddc020 100644
--- a/src/SelectMgr/SelectMgr_ViewerSelector.hxx
+++ b/src/SelectMgr/SelectMgr_ViewerSelector.hxx
@@ -28,7 +28,6 @@
 #include <SelectMgr_Selection.hxx>
 #include <SelectMgr_SelectableObject.hxx>
 #include <SelectMgr_SelectableObjectSet.hxx>
-#include <SelectMgr_SelectableObjectTrsfPersSet.hxx>
 #include <SelectMgr_StateOfSelection.hxx>
 #include <SelectMgr_ToleranceMap.hxx>
 #include <Standard_OStream.hxx>
@@ -248,10 +247,20 @@ protected:
   //! @return True if owner provides depth limits for sensitive clipping.
   Standard_EXPORT virtual Standard_Boolean HasDepthClipping (const Handle(SelectMgr_EntityOwner)& theOwner) const;
 
-  //! Internal function that checks if there is possible overlap
-  //! between some entity of selectable object theObject and
-  //! current selecting volume
-  Standard_EXPORT void traverseObject (const Handle(SelectMgr_SelectableObject)& theObject);
+  //! Internal function that checks if there is possible overlap between some entity of selectable object theObject and
+  //! current selecting volume.
+  //! @param theObject [in] the selectable object for traversal.
+  //! @param theMgr [in] the (un)transformed copy of the selecting volume manager representing active selection frustum.
+  //! @param theCamera, theProjectionMat, theWorldViewMat [in] the source camera and matrices for theMgr given.
+  //! @param theViewportWidth, theViewportHeight [in] viewport (window) dimensions for evaluating 
+  //!        object's transformation persistence.
+  Standard_EXPORT void traverseObject (const Handle(SelectMgr_SelectableObject)& theObject,
+                                       const SelectMgr_SelectingVolumeManager& theMgr,
+                                       const Handle(Graphic3d_Camera)& theCamera,
+                                       const Graphic3d_Mat4d& theProjectionMat,
+                                       const Graphic3d_Mat4d& theWorldViewMat,
+                                       const Standard_Integer theViewportWidth,
+                                       const Standard_Integer theViewportHeight);
 
   //! Internal function that checks if a particular sensitive
   //! entity theEntity overlaps current selecting volume precisely
@@ -279,6 +288,7 @@ private:
   //! needs to be scaled and transformed for the entity and performs
   //! necessary calculations
   void computeFrustum (const Handle(SelectBasics_SensitiveEntity)& theEnt,
+                       const SelectMgr_SelectingVolumeManager&     theMgr,
                        const gp_GTrsf&                             theInvTrsf,
                        SelectMgr_FrustumCache&                     theCachedMgrs,
                        SelectMgr_SelectingVolumeManager&           theResMgr);
@@ -307,7 +317,6 @@ protected:
   SelectMgr_IndexedDataMapOfOwnerCriterion      mystored;
   SelectMgr_SelectingVolumeManager              mySelectingVolumeMgr;
   mutable SelectMgr_SelectableObjectSet         mySelectableObjects;
-  mutable SelectMgr_SelectableObjectTrsfPersSet mySelectableObjectsTrsfPers;
   SelectMgr_ToleranceMap                        myTolerances;
   NCollection_DataMap<Graphic3d_ZLayerId, Standard_Integer> myZLayerOrderMap;
 
diff --git a/src/StdSelect/StdSelect_ViewerSelector3d.cxx b/src/StdSelect/StdSelect_ViewerSelector3d.cxx
index d79356c955..40ca4fa751 100644
--- a/src/StdSelect/StdSelect_ViewerSelector3d.cxx
+++ b/src/StdSelect/StdSelect_ViewerSelector3d.cxx
@@ -183,9 +183,11 @@ void StdSelect_ViewerSelector3d::Pick (const TColgp_Array1OfPnt2d& thePolyline,
 //=======================================================================
 void StdSelect_ViewerSelector3d::DisplaySensitive (const Handle(V3d_View)& theView)
 {
-  for (Standard_Integer anObjectIdx = 0; anObjectIdx <= mySelectableObjects.Size(); ++anObjectIdx)
+  SelectMgr_SelectableObjectSet::Iterator aSelectableIt (mySelectableObjects);
+
+  for (; aSelectableIt.More(); aSelectableIt.Next())
   {
-    const Handle (SelectMgr_SelectableObject)& anObj = mySelectableObjects.GetObjectById (anObjectIdx);
+    const Handle (SelectMgr_SelectableObject)& anObj = aSelectableIt.Value();
 
     Handle(Graphic3d_Structure) aStruct = new Graphic3d_Structure (theView->Viewer()->StructureManager());
 
@@ -200,28 +202,6 @@ void StdSelect_ViewerSelector3d::DisplaySensitive (const Handle(V3d_View)& theVi
     myStructs.Append (aStruct);
   }
 
-  for (Standard_Integer anObjectIdx = 0; anObjectIdx <= mySelectableObjectsTrsfPers.Size(); ++anObjectIdx)
-  {
-    const Handle (SelectMgr_SelectableObject)& anObj = mySelectableObjectsTrsfPers.GetObjectById (anObjectIdx);
-
-    Handle(Graphic3d_Structure) aStruct = new Graphic3d_Structure (theView->Viewer()->StructureManager());
-
-    if (anObj->TransformPersistence().Flags == 0)
-    {
-      continue;
-    }
-
-    for (anObj->Init(); anObj->More(); anObj->Next())
-    {
-      if (anObj->CurrentSelection()->GetSelectionState() == SelectMgr_SOS_Activated)
-      {
-        computeSensitivePrs (aStruct, anObj->CurrentSelection(), anObj->Transformation(), anObj->TransformPersistence());
-      }
-    }
-
-    myStructs.Append (aStruct);
-  }
-
   for (Standard_Integer aStructIdx = 1; aStructIdx <= myStructs.Length(); ++aStructIdx)
   {
     Handle(Graphic3d_Structure)& aStruct = myStructs.ChangeValue (aStructIdx);
diff --git a/tests/bugs/vis/bug27739 b/tests/bugs/vis/bug27739
new file mode 100644
index 0000000000..91afe26362
--- /dev/null
+++ b/tests/bugs/vis/bug27739
@@ -0,0 +1,81 @@
+puts "========"
+puts "OCC27739"
+puts "========"
+puts ""
+##################################################################
+puts "Visualization, TKV3d - implement individual acceleration data structure for selection of 2D persistent objects"
+##################################################################
+
+# Create view
+set win_width 409
+set win_height 409
+vinit View1 w=$win_width h=$win_height
+vclear
+
+# Display several different presentation types with orthographic camera
+vcamera -ortho
+vtrihedron tri1
+box box3d     100 100 100
+box box2d     100 100 1
+box box2d_pos 100 100 1
+box box2d_loc 100 100 1
+box box_zoom  100 100 100
+
+vdisplay box3d     -dispMode 1 -highMode 0
+vdisplay box2d     -dispMode 1 -highMode 1 -2d
+vdisplay box2d_pos -dispMode 1 -highMode 1 -2d -trsfPersPos -1 -1
+vdisplay box2d_loc -dispMode 1 -highMode 1 -2d
+vdisplay box_zoom  -dispMode 1 -highMode 0 -trsfPers zoom
+vdisplay box2d
+vsetlocation box2d_loc  100  0   0
+vsetlocation box_zoom  -100 -100 100
+
+vsetmaterial box3d box2d box2d_pos box2d_loc box_zoom PLASTIC
+vsetcolor box3d     GOLD
+vsetcolor box2d     GREEN
+vsetcolor box2d_pos GREEN
+vsetcolor box2d_loc GREEN
+vsetcolor box_zoom  RED
+vfit
+
+# ==========================================
+# Test selection for orthographic projection
+# ==========================================
+
+set test_1 {220 120}; # box2d
+set test_2 {350 150}; # box2d_pos
+set test_3 { 50 350}; # box2d_loc
+
+vmoveto {*}$test_1
+if {[vreadpixel {*}$test_1 name] != "CYAN1 1"} { puts "ERROR: zoom persistent box is not detected!" }
+vdump $imagedir/${casename}_1.png
+
+vmoveto {*}$test_2
+if {[vreadpixel {*}$test_2 name] != "CYAN1 1"} { puts "ERROR: zoom persistent box is not detected!" }
+vdump $imagedir/${casename}_2.png
+
+vmoveto {*}$test_3
+if {[vreadpixel {*}$test_3 name] != "CYAN1 1"} { puts "ERROR: zoom persistent box is not detected!" }
+vdump $imagedir/${casename}_3.png
+
+# =========================================
+# Test selection for perspective projection
+# =========================================
+
+vcamera -persp
+vcamera -distance 1000
+set test_1 {220 120}; # box2d
+set test_2 {350 150}; # box2d_pos
+set test_3 { 50 350}; # box2d_loc
+
+vmoveto {*}$test_1
+if {[vreadpixel {*}$test_1 name] != "CYAN1 1"} { puts "ERROR: zoom persistent box is not detected!" }
+vdump $imagedir/${casename}_4.png
+
+vmoveto {*}$test_2
+if {[vreadpixel {*}$test_2 name] != "CYAN1 1"} { puts "ERROR: zoom persistent box is not detected!" }
+vdump $imagedir/${casename}_5.png
+
+vmoveto {*}$test_3
+if {[vreadpixel {*}$test_3 name] != "CYAN1 1"} { puts "ERROR: zoom persistent box is not detected!" }
+vdump $imagedir/${casename}_6.png