]> OCCT Git - occt.git/commitdiff
0032886: Visualization, V3d_View - introduce interface for creating a subview CR0-WEEK-15 IR-2022-04-15
authorkgv <kgv@opencascade.com>
Mon, 11 Apr 2022 17:00:39 +0000 (20:00 +0300)
committersmoskvin <smoskvin@opencascade.com>
Fri, 15 Apr 2022 15:23:16 +0000 (18:23 +0300)
V3d_View/Graphic3d_CView pair has been extended to define subview within the other V3d_View instance.
The initialization is done in form of V3d_View::SetWindow() taking parent V3d_View instance on input.

Subview definition includes dimensions defined as a fraction of a parent view and offset from a corner.
This scheme allows splitting window into several subviews automatically occupying entire viewport,
like splitting window into two vertial subviews (100%x50% + 100%x50%),
three horizontal subviews (33%x100% + 30%x100% + 30%x100%),
1 + 2 stacked subviews (50%x100% + 50%x50% + 50%x50%),
as well as thumbnail-alike subviews displayed on top of another larger view.

OpenGl_View::Redraw() blits content of subviews into the window within immediate redraw step.

AIS_ViewController::FlushViewEvents() has been extended
to re-calculate mouse input into local subview coordinates.
AIS_ViewController::handleViewRedraw() first redraws subviews and then parent views.
Introduced new callback AIS_ViewController::OnSubviewChanged()
to switch input focus to another subview on mouse click,
implemented by ViewerTest_EventManager (has to be done at application level).

vinit command has been extended with parameters -subview and -parent to create a subview.
In addition, view dimension arguments now can be defined as a fraction of screen size instead of pixels.

34 files changed:
src/AIS/AIS_ViewController.cxx
src/AIS/AIS_ViewController.hxx
src/Aspect/Aspect_Window.hxx
src/D3DHost/D3DHost_View.cxx
src/D3DHost/D3DHost_View.hxx
src/DPrsStd/DPrsStd_AISViewerCommands.cxx
src/Graphic3d/Graphic3d_CView.cxx
src/Graphic3d/Graphic3d_CView.hxx
src/OpenGl/OpenGl_GraphicDriver.cxx
src/OpenGl/OpenGl_GraphicDriver.hxx
src/OpenGl/OpenGl_View.cxx
src/OpenGl/OpenGl_View.hxx
src/OpenGl/OpenGl_Window.cxx
src/OpenGl/OpenGl_Window.hxx
src/OpenGl/OpenGl_Window_1.mm
src/V3d/V3d_View.cxx
src/V3d/V3d_View.hxx
src/V3d/V3d_Viewer.cxx
src/V3d/V3d_Viewer.hxx
src/ViewerTest/ViewerTest.cxx
src/ViewerTest/ViewerTest.hxx
src/ViewerTest/ViewerTest_AutoUpdater.cxx
src/ViewerTest/ViewerTest_EventManager.cxx
src/ViewerTest/ViewerTest_EventManager.hxx
src/ViewerTest/ViewerTest_OpenGlCommands.cxx
src/ViewerTest/ViewerTest_ViewerCommands.cxx
src/XDEDRAW/XDEDRAW.cxx
tests/opengl/data/general/multiview1 [new file with mode: 0644]
tests/opengl/data/general/multiview1ssaa [new file with mode: 0644]
tests/opengl/data/general/multiview2 [new file with mode: 0644]
tests/opengl/data/general/multiview3 [new file with mode: 0644]
tests/opengl/data/general/multiview4 [new file with mode: 0644]
tests/opengl/data/general/multiview5 [new file with mode: 0644]
tests/opengl/data/general/multiview6 [new file with mode: 0644]

index 62cc3d9ea5bc426baff873551edb49cc291d853d..e8738defb39489d01ca7a3385536efd9955bd887 100644 (file)
@@ -184,6 +184,53 @@ void AIS_ViewController::FlushViewEvents (const Handle(AIS_InteractiveContext)&
 {
   flushBuffers (theCtx, theView);
   flushGestures(theCtx, theView);
+
+  if (theView->IsSubview())
+  {
+    // move input coordinates inside the view
+    const Graphic3d_Vec2i aDelta = theView->View()->SubviewTopLeft();
+    if (myGL.MoveTo.ToHilight || myGL.Dragging.ToStart)
+    {
+      myGL.MoveTo.Point -= aDelta;
+    }
+    if (myGL.Panning.ToStart)
+    {
+      myGL.Panning.PointStart -= aDelta;
+    }
+    if (myGL.Dragging.ToStart)
+    {
+      myGL.Dragging.PointStart -= aDelta;
+    }
+    if (myGL.Dragging.ToMove)
+    {
+      myGL.Dragging.PointTo -= aDelta;
+    }
+    if (myGL.OrbitRotation.ToStart)
+    {
+      myGL.OrbitRotation.PointStart -= Graphic3d_Vec2d (aDelta);
+    }
+    if (myGL.OrbitRotation.ToRotate)
+    {
+      myGL.OrbitRotation.PointTo -= Graphic3d_Vec2d (aDelta);
+    }
+    if (myGL.ViewRotation.ToStart)
+    {
+      myGL.ViewRotation.PointStart -= Graphic3d_Vec2d (aDelta);
+    }
+    if (myGL.ViewRotation.ToRotate)
+    {
+      myGL.ViewRotation.PointTo -= Graphic3d_Vec2d (aDelta);
+    }
+    for (Graphic3d_Vec2i& aPntIter : myGL.Selection.Points)
+    {
+      aPntIter -= aDelta;
+    }
+    for (Aspect_ScrollDelta& aZoomIter : myGL.ZoomActions)
+    {
+      aZoomIter.Point -= aDelta;
+    }
+  }
+
   if (theToHandle)
   {
     HandleViewEvents (theCtx, theView);
@@ -2627,6 +2674,17 @@ void AIS_ViewController::OnSelectionChanged (const Handle(AIS_InteractiveContext
   //
 }
 
+// =======================================================================
+// function : OnSubviewChanged
+// purpose  :
+// =======================================================================
+void AIS_ViewController::OnSubviewChanged (const Handle(AIS_InteractiveContext)& ,
+                                           const Handle(V3d_View)& ,
+                                           const Handle(V3d_View)& )
+{
+  //
+}
+
 // =======================================================================
 // function : OnObjectDragged
 // purpose  :
@@ -3002,6 +3060,8 @@ void AIS_ViewController::handleMoveTo (const Handle(AIS_InteractiveContext)& the
 void AIS_ViewController::handleViewRedraw (const Handle(AIS_InteractiveContext)& ,
                                            const Handle(V3d_View)& theView)
 {
+  Handle(V3d_View) aParentView = theView->IsSubview() ? theView->ParentView() : theView;
+
   // manage animation state
   if (!myViewAnimation.IsNull()
    && !myViewAnimation->IsStopped())
@@ -3029,31 +3089,82 @@ void AIS_ViewController::handleViewRedraw (const Handle(AIS_InteractiveContext)&
     myToAskNextFrame = true;
   }
 
-  for (V3d_ListOfViewIterator aViewIter (theView->Viewer()->ActiveViewIterator()); aViewIter.More(); aViewIter.Next())
+  for (int aSubViewPass = 0; aSubViewPass < 2; ++aSubViewPass)
   {
-    const Handle(V3d_View)& aView = aViewIter.Value();
-    if (aView->IsInvalidated()
-     || (myToAskNextFrame && aView == theView))
+    const bool isSubViewPass = (aSubViewPass == 0);
+    for (V3d_ListOfViewIterator aViewIter (theView->Viewer()->ActiveViewIterator()); aViewIter.More(); aViewIter.Next())
     {
-      if (aView->ComputedMode())
+      const Handle(V3d_View)& aView = aViewIter.Value();
+      if (isSubViewPass
+      && !aView->IsSubview())
       {
-        aView->Update();
+        for (const Handle(V3d_View)& aSubviewIter : aView->Subviews())
+        {
+          if (aSubviewIter->Viewer() != theView->Viewer())
+          {
+            if (aSubviewIter->IsInvalidated())
+            {
+              if (aSubviewIter->ComputedMode())
+              {
+                aSubviewIter->Update();
+              }
+              else
+              {
+                aSubviewIter->Redraw();
+              }
+            }
+            else if (aSubviewIter->IsInvalidatedImmediate())
+            {
+              aSubviewIter->RedrawImmediate();
+            }
+          }
+        }
+        continue;
       }
-      else
+      else if (!isSubViewPass
+             && aView->IsSubview())
       {
-        aView->Redraw();
+        continue;
+      }
+
+      if (aView->IsInvalidated()
+       || (myToAskNextFrame && aView == theView))
+      {
+        if (aView->ComputedMode())
+        {
+          aView->Update();
+        }
+        else
+        {
+          aView->Redraw();
+        }
+
+        if (aView->IsSubview())
+        {
+          aView->ParentView()->InvalidateImmediate();
+        }
+      }
+      else if (aView->IsInvalidatedImmediate())
+      {
+        if (aView->IsSubview())
+        {
+          aView->ParentView()->InvalidateImmediate();
+        }
+
+        aView->RedrawImmediate();
       }
     }
-    else if (aView->IsInvalidatedImmediate())
-    {
-      aView->RedrawImmediate();
-    }
+  }
+  if (theView->IsSubview()
+   && theView->Viewer() != aParentView->Viewer())
+  {
+    aParentView->RedrawImmediate();
   }
 
   if (myToAskNextFrame)
   {
     // ask more frames
-    theView->Window()->InvalidateContent (Handle(Aspect_DisplayConnection)());
+    aParentView->Window()->InvalidateContent (Handle(Aspect_DisplayConnection)());
   }
 }
 
@@ -3298,6 +3409,36 @@ void AIS_ViewController::HandleViewEvents (const Handle(AIS_InteractiveContext)&
 {
   const bool wasImmediateUpdate = theView->SetImmediateUpdate (false);
 
+  Handle(V3d_View) aPickedView;
+  if (theView->IsSubview()
+  || !theView->Subviews().IsEmpty())
+  {
+    // activate another subview on mouse click
+    bool toPickSubview = false;
+    Graphic3d_Vec2i aClickPoint;
+    if (myGL.Selection.Tool == AIS_ViewSelectionTool_Picking
+    && !myGL.Selection.Points.IsEmpty())
+    {
+      aClickPoint = myGL.Selection.Points.Last();
+      toPickSubview = true;
+    }
+    else if (!myGL.ZoomActions.IsEmpty())
+    {
+      //aClickPoint = myGL.ZoomActions.Last().Point;
+      //toPickSubview = true;
+    }
+
+    if (toPickSubview)
+    {
+      if (theView->IsSubview())
+      {
+        aClickPoint += theView->View()->SubviewTopLeft();
+      }
+      Handle(V3d_View) aParent = !theView->IsSubview() ? theView : theView->ParentView();
+      aPickedView = aParent->PickSubview (aClickPoint);
+    }
+  }
+
   handleViewOrientationKeys (theCtx, theView);
   const AIS_WalkDelta aWalk = handleNavigationKeys (theCtx, theView);
   handleXRInput (theCtx, theView, aWalk);
@@ -3315,6 +3456,12 @@ void AIS_ViewController::HandleViewEvents (const Handle(AIS_InteractiveContext)&
 
   theView->SetImmediateUpdate (wasImmediateUpdate);
 
+  if (!aPickedView.IsNull()
+    && aPickedView != theView)
+  {
+    OnSubviewChanged (theCtx, theView, aPickedView);
+  }
+
   // make sure to not process the same events twice
   myGL.Reset();
   myToAskNextFrame = false;
index 98f19387262e5de281a09f807cd742fb67e8d5d2..0215bdde6b53f81a062fe626a7c8a32415bd6ae3 100644 (file)
@@ -492,6 +492,12 @@ public:
                                                 const Handle(V3d_View)& theView,
                                                 AIS_DragAction theAction);
 
+  //! Callback called by HandleViewEvents() on Selection of another (sub)view.
+  //! This method is expected to be called from rendering thread.
+  Standard_EXPORT virtual void OnSubviewChanged (const Handle(AIS_InteractiveContext)& theCtx,
+                                                 const Handle(V3d_View)& theOldView,
+                                                 const Handle(V3d_View)& theNewView);
+
   //! Pick closest point under mouse cursor.
   //! This method is expected to be called from rendering thread.
   //! @param thePnt   [out] result point
index c7153f0bfd3fa83ac25bea623090dcf4d629f101..a849aaea73074d085aac97f232e5259f0eecc07c 100644 (file)
@@ -40,18 +40,58 @@ class Aspect_Window : public Standard_Transient
   DEFINE_STANDARD_RTTIEXT(Aspect_Window, Standard_Transient)
 public:
 
+  //! Returns True if the window <me> is virtual
+  Standard_EXPORT Standard_Boolean IsVirtual() const;
+
+  //! Setup the virtual state
+  Standard_EXPORT void SetVirtual (const Standard_Boolean theVirtual);
+
+  //! Returns window top-left corner.
+  Graphic3d_Vec2i TopLeft() const
+  {
+    Graphic3d_Vec2i aTopLeft, aBotRight;
+    Position (aTopLeft.x(), aTopLeft.y(), aBotRight.x(), aBotRight.y());
+    return aTopLeft;
+  }
+
+  //! Returns window dimensions.
+  Graphic3d_Vec2i Dimensions() const
+  {
+    Graphic3d_Vec2i aSize;
+    Size (aSize.x(), aSize.y());
+    return aSize;
+  }
+
+  //! Returns connection to Display or NULL.
+  const Handle(Aspect_DisplayConnection)& DisplayConnection() const { return myDisplay; }
+
+  //! Returns the window background.
+  Standard_EXPORT Aspect_Background Background() const;
+
+  //! Returns the current image background fill mode.
+  Standard_EXPORT Aspect_FillMethod BackgroundFillMethod() const;
+
+  //! Returns the window gradient background.
+  Standard_EXPORT Aspect_GradientBackground GradientBackground() const;
+
   //! Modifies the window background.
-  Standard_EXPORT void SetBackground (const Aspect_Background& ABack);
+  Standard_EXPORT void SetBackground (const Aspect_Background& theBack);
 
   //! Modifies the window background.
-  Standard_EXPORT void SetBackground (const Quantity_Color& color);
+  Standard_EXPORT void SetBackground (const Quantity_Color& theColor);
 
   //! Modifies the window gradient background.
-  Standard_EXPORT void SetBackground (const Aspect_GradientBackground& ABackground);
+  Standard_EXPORT void SetBackground (const Aspect_GradientBackground& theBackground);
 
   //! Modifies the window gradient background.
   Standard_EXPORT void SetBackground (const Quantity_Color& theFirstColor, const Quantity_Color& theSecondColor, const Aspect_GradientFillMethod theFillMethod);
 
+public:
+
+  //! Returns True if the window <me> is opened
+  //! and False if the window is closed.
+  Standard_EXPORT virtual Standard_Boolean IsMapped() const = 0;
+
   //! Opens the window <me>.
   Standard_EXPORT virtual void Map() const = 0;
 
@@ -65,25 +105,6 @@ public:
   //! and returns TRUE if the window is mapped at screen.
   Standard_EXPORT virtual Standard_Boolean DoMapping() const = 0;
 
-  //! Returns the window background.
-  Standard_EXPORT Aspect_Background Background() const;
-
-  //! Returns the current image background fill mode.
-  Standard_EXPORT Aspect_FillMethod BackgroundFillMethod() const;
-
-  //! Returns the window gradient background.
-  Standard_EXPORT Aspect_GradientBackground GradientBackground() const;
-
-  //! Returns True if the window <me> is opened
-  //! and False if the window is closed.
-  Standard_EXPORT virtual Standard_Boolean IsMapped() const = 0;
-
-  //! Returns True if the window <me> is virtual
-  Standard_EXPORT Standard_Boolean IsVirtual() const;
-
-  //! Setup the virtual state
-  Standard_EXPORT void SetVirtual (const Standard_Boolean theVirtual);
-
   //! Returns The Window RATIO equal to the physical
   //! WIDTH/HEIGHT dimensions
   Standard_EXPORT virtual Standard_Real Ratio() const = 0;
@@ -103,9 +124,6 @@ public:
   //! Returns native Window FB config (GLXFBConfig on Xlib)
   Standard_EXPORT virtual Aspect_FBConfig NativeFBConfig() const = 0;
 
-  //! Returns connection to Display or NULL.
-  const Handle(Aspect_DisplayConnection)& DisplayConnection() const { return myDisplay; }
-
   //! Sets window title.
   virtual void SetTitle (const TCollection_AsciiString& theTitle) { (void )theTitle; }
 
index 6e386769196e0f139748d3df4263fa457e823ee1..af3306d140d8bc6aca03c57f1becb061c9b49dbc 100644 (file)
@@ -128,7 +128,8 @@ IDirect3DSurface9* D3DHost_View::D3dColorSurface() const
 // function : SetWindow
 // purpose  :
 // =======================================================================
-void D3DHost_View::SetWindow (const Handle(Aspect_Window)& theWindow,
+void D3DHost_View::SetWindow (const Handle(Graphic3d_CView)& theParentVIew,
+                              const Handle(Aspect_Window)& theWindow,
                               const Aspect_RenderingContext theContext)
 {
   if (!myD3dWglFbo.IsNull())
@@ -142,7 +143,7 @@ void D3DHost_View::SetWindow (const Handle(Aspect_Window)& theWindow,
     myD3dDevice = NULL;
   }
 
-  OpenGl_View::SetWindow (theWindow, theContext);
+  OpenGl_View::SetWindow (theParentVIew, theWindow, theContext);
 
   if (!myWindow.IsNull())
   {
index 39c307851a3baaed1d9b842e8b342e614162d83d..dc53f62af9fae79e5064cfb02dbf7448f0d8367b 100644 (file)
@@ -44,12 +44,8 @@ public:
   Standard_EXPORT virtual void ReleaseGlResources (const Handle(OpenGl_Context)& theCtx) Standard_OVERRIDE;
 
   //! Creates and maps rendering window to the view.
-  //! @param theWindow [in] the window.
-  //! @param theContext [in] the rendering context. If NULL the context will be created internally.
-  //! @param theDisplayCB [in] the display callback function. If is not a NULL value, then the callback will be
-  //! invoked at the end of the OCC graphic traversal and just before the swap of buffers.
-  //! @param theClientData [in] the client data for the callback.
-  Standard_EXPORT virtual void SetWindow (const Handle(Aspect_Window)&  theWindow,
+  Standard_EXPORT virtual void SetWindow (const Handle(Graphic3d_CView)& theParentVIew,
+                                          const Handle(Aspect_Window)& theWindow,
                                           const Aspect_RenderingContext theContext) Standard_OVERRIDE;
 
   //! Resizes the window.
index 81c7d8a4c25776a47b68fadcddedaba2a59afb06..343caa246866438dfb39ebcca085349a866f5b10 100644 (file)
@@ -56,7 +56,7 @@ static Standard_Integer DPrsStd_AISInitViewer (Draw_Interpretor& theDI,
   TCollection_AsciiString   aViewName = TCollection_AsciiString ("Driver1/Document_") + theArgVec[1] + "/View1";
   if (!TPrsStd_AISViewer::Find (aRoot, aDocViewer))
   {
-    ViewerTest::ViewerInit (0, 0, 0, 0, aViewName.ToCString(), "");
+    ViewerTest::ViewerInit (aViewName);
     aDocViewer = TPrsStd_AISViewer::New (aRoot, ViewerTest::GetAISContext());
   }
 
index 07077415ffc8630d5dbfcf2f3b0a23b3ad7ef4e7..c114c66a214fd95f475f48be247f58323913b538 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <Graphic3d_CView.hxx>
 
+#include <Aspect_NeutralWindow.hxx>
 #include <Aspect_OpenVRSession.hxx>
 #include <Graphic3d_CubeMapPacked.hxx>
 #include <Graphic3d_Layer.hxx>
@@ -26,18 +27,27 @@ IMPLEMENT_STANDARD_RTTIEXT(Graphic3d_CView,Graphic3d_DataStructureManager)
 //purpose  :
 //=======================================================================
 Graphic3d_CView::Graphic3d_CView (const Handle(Graphic3d_StructureManager)& theMgr)
-: myBgColor                (Quantity_NOC_BLACK),
-  myBackgroundType         (Graphic3d_TOB_NONE),
-  myToUpdateSkydome        (Standard_False),
-  myStructureManager       (theMgr),
-  myCamera                 (new Graphic3d_Camera()),
-  myHiddenObjects          (new Graphic3d_NMapOfTransient()),
-  myIsInComputedMode       (Standard_False),
-  myIsActive               (Standard_False),
-  myIsRemoved              (Standard_False),
-  myBackfacing             (Graphic3d_TypeOfBackfacingModel_Auto),
-  myVisualization          (Graphic3d_TOV_WIREFRAME),
-  myUnitFactor             (1.0)
+: myId (0),
+  //
+  myParentView (nullptr),
+  myIsSubviewComposer (Standard_False),
+  mySubviewCorner (Aspect_TOTP_LEFT_UPPER),
+  mySubviewSize (1.0, 1.0),
+  //
+  myStructureManager (theMgr),
+  myCamera (new Graphic3d_Camera()),
+  myHiddenObjects (new Graphic3d_NMapOfTransient()),
+  myIsInComputedMode (Standard_False),
+  myIsActive (Standard_False),
+  myIsRemoved (Standard_False),
+  myBackfacing (Graphic3d_TypeOfBackfacingModel_Auto),
+  myVisualization (Graphic3d_TOV_WIREFRAME),
+  //
+  myBgColor (Quantity_NOC_BLACK),
+  myBackgroundType (Graphic3d_TOB_NONE),
+  myToUpdateSkydome (Standard_False),
+  //
+  myUnitFactor (1.0)
 {
   myId = myStructureManager->Identification (this);
 }
@@ -161,8 +171,21 @@ void Graphic3d_CView::Remove()
     return;
   }
 
-  Graphic3d_MapOfStructure aDisplayedStructs (myStructsDisplayed);
+  if (myParentView != nullptr)
+  {
+    myParentView->RemoveSubview (this);
+    myParentView = nullptr;
+  }
+  {
+    NCollection_Sequence<Handle(Graphic3d_CView)> aSubviews = mySubviews;
+    mySubviews.Clear();
+    for (const Handle(Graphic3d_CView)& aViewIter : aSubviews)
+    {
+      aViewIter->Remove();
+    }
+  }
 
+  Graphic3d_MapOfStructure aDisplayedStructs (myStructsDisplayed);
   for (Graphic3d_MapIteratorOfMapOfStructure aStructIter (aDisplayedStructs); aStructIter.More(); aStructIter.Next())
   {
     Erase (aStructIter.Value());
@@ -181,6 +204,114 @@ void Graphic3d_CView::Remove()
   myIsRemoved = Standard_True;
 }
 
+// ========================================================================
+// function : AddSubview
+// purpose  :
+// ========================================================================
+void Graphic3d_CView::AddSubview (const Handle(Graphic3d_CView)& theView)
+{
+  mySubviews.Append (theView);
+}
+
+// ========================================================================
+// function : RemoveSubview
+// purpose  :
+// ========================================================================
+bool Graphic3d_CView::RemoveSubview (const Graphic3d_CView* theView)
+{
+  for (NCollection_Sequence<Handle(Graphic3d_CView)>::Iterator aViewIter (mySubviews); aViewIter.More(); aViewIter.Next())
+  {
+    if (aViewIter.Value() == theView)
+    {
+      mySubviews.Remove (aViewIter);
+      return true;
+    }
+  }
+  return false;
+}
+
+// ========================================================================
+// function : Resized
+// purpose  :
+// ========================================================================
+void Graphic3d_CView::Resized()
+{
+  if (IsSubview())
+  {
+    Handle(Aspect_NeutralWindow) aWindow = Handle(Aspect_NeutralWindow)::DownCast(Window());
+    SubviewResized (aWindow);
+  }
+}
+
+//! Calculate offset in pixels from fraction.
+static int getSubViewOffset (double theOffset, int theWinSize)
+{
+  if (theOffset >= 1.0)
+  {
+    return int(theOffset);
+  }
+  else
+  {
+    return int(theOffset * theWinSize);
+  }
+}
+
+// ========================================================================
+// function : SubviewResized
+// purpose  :
+// ========================================================================
+void Graphic3d_CView::SubviewResized (const Handle(Aspect_NeutralWindow)& theWindow)
+{
+  if (!IsSubview()
+   || theWindow.IsNull())
+  {
+    return;
+  }
+
+  const Graphic3d_Vec2i aWinSize (myParentView->Window()->Dimensions());
+  Graphic3d_Vec2i aViewSize (Graphic3d_Vec2d(aWinSize) * mySubviewSize);
+  if (mySubviewSize.x() > 1.0)
+  {
+    aViewSize.x() = (int)mySubviewSize.x();
+  }
+  if (mySubviewSize.y() > 1.0)
+  {
+    aViewSize.y() = (int)mySubviewSize.y();
+  }
+
+  Graphic3d_Vec2i anOffset (getSubViewOffset (mySubviewOffset.x(), aWinSize.x()),
+                            getSubViewOffset (mySubviewOffset.y(), aWinSize.y()));
+  mySubviewTopLeft = (aWinSize - aViewSize) / 2; // Aspect_TOTP_CENTER
+  if ((mySubviewCorner & Aspect_TOTP_LEFT) != 0)
+  {
+    mySubviewTopLeft.x() = anOffset.x();
+  }
+  else if ((mySubviewCorner & Aspect_TOTP_RIGHT) != 0)
+  {
+    mySubviewTopLeft.x() = Max (aWinSize.x() - anOffset.x() - aViewSize.x(), 0);
+  }
+
+  if ((mySubviewCorner & Aspect_TOTP_TOP) != 0)
+  {
+    mySubviewTopLeft.y() = anOffset.y();
+  }
+  else if ((mySubviewCorner & Aspect_TOTP_BOTTOM) != 0)
+  {
+    mySubviewTopLeft.y() = Max (aWinSize.y() - anOffset.y() - aViewSize.y(), 0);
+  }
+
+  mySubviewTopLeft += mySubviewMargins;
+  aViewSize -= mySubviewMargins * 2;
+
+  const int aRight = Min(mySubviewTopLeft.x() + aViewSize.x(), aWinSize.x());
+  aViewSize.x() = aRight - mySubviewTopLeft.x();
+
+  const int aBot = Min(mySubviewTopLeft.y() + aViewSize.y(), aWinSize.y());
+  aViewSize.y() = aBot - mySubviewTopLeft.y();
+
+  theWindow->SetSize (aViewSize.x(), aViewSize.y());
+}
+
 // ========================================================================
 // function : SetComputedMode
 // purpose  :
index 43e3675fc306522cdae42827cbd3e87a33be31ea..fe29afeb38f9b5d89b71b9bad5e0e17f83cd725e 100644 (file)
@@ -48,6 +48,7 @@
 #include <Standard_Transient.hxx>
 #include <TColStd_IndexedDataMapOfStringString.hxx>
 
+class Aspect_NeutralWindow;
 class Aspect_XRSession;
 class Graphic3d_CView;
 class Graphic3d_Layer;
@@ -239,7 +240,7 @@ public:
   virtual Standard_Boolean IsInvalidated() = 0;
 
   //! Handle changing size of the rendering window.
-  virtual void Resized() = 0;
+  Standard_EXPORT virtual void Resized() = 0;
 
   //! @param theDrawToFrontBuffer Advanced option to modify rendering mode:
   //! 1. TRUE.  Drawing immediate mode structures directly to the front buffer over the scene image.
@@ -256,10 +257,12 @@ public:
   virtual Standard_Boolean SetImmediateModeDrawToFront (const Standard_Boolean theDrawToFrontBuffer) = 0;
 
   //! Creates and maps rendering window to the view.
-  //! @param theWindow [in] the window.
-  //! @param theContext [in] the rendering context. If NULL the context will be created internally.
-  virtual void SetWindow (const Handle(Aspect_Window)& theWindow,
-                          const Aspect_RenderingContext theContext = NULL) = 0;
+  //! @param[in] theParentVIew parent view or NULL
+  //! @param[in] theWindow the window
+  //! @param[in] theContext the rendering context; if NULL the context will be created internally
+  virtual void SetWindow (const Handle(Graphic3d_CView)& theParentVIew,
+                          const Handle(Aspect_Window)& theWindow,
+                          const Aspect_RenderingContext theContext) = 0;
 
   //! Returns the window associated to the view.
   virtual Handle(Aspect_Window) Window() const = 0;
@@ -554,6 +557,70 @@ public: //! @name obsolete Graduated Trihedron functionality
   //! Dumps the content of me into the stream
   Standard_EXPORT virtual void DumpJson (Standard_OStream& theOStream, Standard_Integer theDepth = -1) const Standard_OVERRIDE;
 
+public: //! @name subview properties
+
+  //! Return TRUE if this is a subview of another view.
+  bool IsSubview() const { return myParentView != nullptr; }
+
+  //! Return parent View or NULL if this is not a subview.
+  Graphic3d_CView* ParentView() { return myParentView; }
+
+  //! Return TRUE if this is view performs rendering of subviews and nothing else; FALSE by default.
+  //! By default, view with subviews will render main scene and blit subviews on top of it.
+  //! Rendering of main scene might become redundant in case if subviews cover entire window of parent view.
+  //! This flag allows to disable rendering of the main scene in such scenarios
+  //! without creation of a dedicated V3d_Viewer instance just for composing subviews.
+  bool IsSubviewComposer() const { return myIsSubviewComposer; }
+
+  //! Set if this view should perform composing of subviews and nothing else.
+  void SetSubviewComposer (bool theIsComposer) { myIsSubviewComposer = theIsComposer; }
+
+  //! Return subview list.
+  const NCollection_Sequence<Handle(Graphic3d_CView)>& Subviews() const { return mySubviews; }
+
+  //! Add subview to the list.
+  Standard_EXPORT void AddSubview (const Handle(Graphic3d_CView)& theView);
+
+  //! Remove subview from the list.
+  Standard_EXPORT bool RemoveSubview (const Graphic3d_CView* theView);
+
+  //! Return subview position within parent view; Aspect_TOTP_LEFT_UPPER by default.
+  Aspect_TypeOfTriedronPosition SubviewCorner() const { return mySubviewCorner; }
+
+  //! Set subview position within parent view.
+  void SetSubviewCorner (Aspect_TypeOfTriedronPosition thePos) { mySubviewCorner = thePos; }
+
+  //! Return subview top-left position relative to parent view in pixels.
+  const Graphic3d_Vec2i& SubviewTopLeft() const { return mySubviewTopLeft; }
+
+  //! Return TRUE if subview size is set as proportions relative to parent view.
+  bool IsSubViewRelativeSize() const { return mySubviewSize.x() <= 1.0 && mySubviewSize.y() <= 1.0; }
+
+  //! Return subview dimensions; (1.0, 1.0) by default.
+  //! Values >= 2   define size in pixels;
+  //! Values <= 1.0 define size as fraction of parent view.
+  const Graphic3d_Vec2d& SubviewSize() const { return mySubviewSize; }
+
+  //! Set subview size relative to parent view.
+  void SetSubviewSize (const Graphic3d_Vec2d& theSize) { mySubviewSize = theSize; }
+
+  //! Return corner offset within parent view; (0.0,0.0) by default.
+  //! Values >= 2   define offset in pixels;
+  //! Values <= 1.0 define offset as fraction of parent view dimensions.
+  const Graphic3d_Vec2d& SubviewOffset() const { return mySubviewOffset; }
+
+  //! Set corner offset within parent view.
+  void SetSubviewOffset (const Graphic3d_Vec2d& theOffset) { mySubviewOffset = theOffset; }
+
+  //! Return subview margins in pixels; (0,0) by default
+  const Graphic3d_Vec2i& SubviewMargins() const { return mySubviewMargins; }
+
+  //! Set subview margins in pixels.
+  void SetSubviewMargins (const Graphic3d_Vec2i& theMargins) { mySubviewMargins = theMargins; }
+
+  //! Update subview position and dimensions.
+  Standard_EXPORT void SubviewResized (const Handle(Aspect_NeutralWindow)& theWindow);
+
 private:
 
   //! Adds the structure to display lists of the view.
@@ -576,14 +643,14 @@ protected:
   Standard_Integer myId;
   Graphic3d_RenderingParams myRenderParams;
 
-  Quantity_ColorRGBA           myBgColor;
-  Handle(Graphic3d_TextureMap) myBackgroundImage;
-  Handle(Graphic3d_CubeMap)    myCubeMapBackground;  //!< Cubemap displayed at background
-  Handle(Graphic3d_CubeMap)    myCubeMapIBL;         //!< Cubemap used for environment lighting
-  Handle(Graphic3d_TextureEnv) myTextureEnvData;
-  Graphic3d_TypeOfBackground   myBackgroundType;     //!< Current type of background
-  Aspect_SkydomeBackground     mySkydomeAspect;
-  Standard_Boolean             myToUpdateSkydome;
+  NCollection_Sequence<Handle(Graphic3d_CView)> mySubviews; //!< list of child views
+  Graphic3d_CView*              myParentView;               //!< back-pointer to the parent view
+  Standard_Boolean              myIsSubviewComposer;        //!< flag to skip rendering of viewer contents
+  Aspect_TypeOfTriedronPosition mySubviewCorner;            //!< position within parent view
+  Graphic3d_Vec2i               mySubviewTopLeft;           //!< subview top-left position relative to parent view
+  Graphic3d_Vec2i               mySubviewMargins;           //!< subview margins in pixels
+  Graphic3d_Vec2d               mySubviewSize;              //!< subview size
+  Graphic3d_Vec2d               mySubviewOffset;            //!< subview corner offset within parent view
 
   Handle(Graphic3d_StructureManager) myStructureManager;
   Handle(Graphic3d_Camera)  myCamera;
@@ -597,6 +664,16 @@ protected:
   Graphic3d_TypeOfBackfacingModel myBackfacing;
   Graphic3d_TypeOfVisualization myVisualization;
 
+  Quantity_ColorRGBA           myBgColor;
+  Handle(Graphic3d_TextureMap) myBackgroundImage;
+  Handle(Graphic3d_CubeMap)    myCubeMapBackground;  //!< Cubemap displayed at background
+  Handle(Graphic3d_CubeMap)    myCubeMapIBL;         //!< Cubemap used for environment lighting
+  Handle(Graphic3d_TextureEnv) myTextureEnvData;
+  Graphic3d_GraduatedTrihedron myGTrihedronData;
+  Graphic3d_TypeOfBackground   myBackgroundType;     //!< Current type of background
+  Aspect_SkydomeBackground     mySkydomeAspect;
+  Standard_Boolean             myToUpdateSkydome;
+
   Handle(Aspect_XRSession) myXRSession;
   Handle(Graphic3d_Camera) myBackXRCamera;       //!< camera projection parameters to restore after closing XR session (FOV, aspect and similar)
   Handle(Graphic3d_Camera) myBaseXRCamera;       //!< neutral camera orientation defining coordinate system in which head tracking is defined
@@ -604,10 +681,6 @@ protected:
   Handle(Graphic3d_Camera) myPosedXRCameraCopy;  //!< neutral camera orientation copy at the beginning of processing input
   Standard_Real            myUnitFactor;         //!< unit scale factor defined as scale factor for m (meters)
 
-protected:
-
-  Graphic3d_GraduatedTrihedron myGTrihedronData;
-
 };
 
 #endif // _Graphic3d_CView_HeaderFile
index 8e348d94068e1c35f5a12c2f26a0d8ab07869020..ebd29d5c9bd44925ac47d2709999ae8ef22c9413 100644 (file)
@@ -868,14 +868,16 @@ void OpenGl_GraphicDriver::RemoveView (const Handle(Graphic3d_CView)& theView)
 }
 
 // =======================================================================
-// function : Window
+// function : CreateRenderWindow
 // purpose  :
 // =======================================================================
-Handle(OpenGl_Window) OpenGl_GraphicDriver::CreateRenderWindow (const Handle(Aspect_Window)&  theWindow,
+Handle(OpenGl_Window) OpenGl_GraphicDriver::CreateRenderWindow (const Handle(Aspect_Window)& theNativeWindow,
+                                                                const Handle(Aspect_Window)& theSizeWindow,
                                                                 const Aspect_RenderingContext theContext)
 {
   Handle(OpenGl_Context) aShareCtx = GetSharedContext();
-  Handle(OpenGl_Window) aWindow = new OpenGl_Window (this, theWindow, theContext, myCaps, aShareCtx);
+  Handle(OpenGl_Window) aWindow = new OpenGl_Window();
+  aWindow->Init (this, theNativeWindow, theSizeWindow, theContext, myCaps, aShareCtx);
   return aWindow;
 }
 
index 64e0518e61ded5d15c9f56baf4591b2aa659947d..1bde0b43dd68aa7e3534613b9a57c1149dd361d4 100644 (file)
@@ -88,7 +88,13 @@ public:
 
   Standard_EXPORT virtual void RemoveView (const Handle(Graphic3d_CView)& theView) Standard_OVERRIDE;
 
-  Standard_EXPORT virtual Handle(OpenGl_Window) CreateRenderWindow (const Handle(Aspect_Window)& theWindow, const Aspect_RenderingContext theContext);
+  //! Create OpenGL window from native window.
+  //! @param[in] theNativeWindow native window holder
+  //! @param[in] theSizeWindow object defining window dimensions
+  //! @param[in] theContext existing native rendering context
+  Standard_EXPORT virtual Handle(OpenGl_Window) CreateRenderWindow (const Handle(Aspect_Window)& theNativeWindow,
+                                                                    const Handle(Aspect_Window)& theSizeWindow,
+                                                                    const Aspect_RenderingContext theContext);
 
 public:
 
index 375e366f0237bec24d45ce6fa64bf23a4f6aee23..f3bcbc9bde77d459a9f630023a97bdfe66c97964 100644 (file)
@@ -15,8 +15,8 @@
 
 #include <OpenGl_View.hxx>
 
+#include <Aspect_NeutralWindow.hxx>
 #include <Aspect_RenderingContext.hxx>
-#include <Aspect_Window.hxx>
 #include <Aspect_XRSession.hxx>
 #include <Graphic3d_AspectFillArea3d.hxx>
 #include <Graphic3d_Texture2Dmanual.hxx>
@@ -366,20 +366,57 @@ Standard_Boolean OpenGl_View::SetImmediateModeDrawToFront (const Standard_Boolea
 // =======================================================================
 Handle(Aspect_Window) OpenGl_View::Window() const
 {
-  return myWindow->PlatformWindow();
+  return myWindow->SizeWindow();
 }
 
 // =======================================================================
 // function : SetWindow
 // purpose  :
 // =======================================================================
-void OpenGl_View::SetWindow (const Handle(Aspect_Window)& theWindow,
+void OpenGl_View::SetWindow (const Handle(Graphic3d_CView)& theParentVIew,
+                             const Handle(Aspect_Window)& theWindow,
                              const Aspect_RenderingContext theContext)
 {
-  myWindow = myDriver->CreateRenderWindow (theWindow, theContext);
-  Standard_ASSERT_RAISE (!myWindow.IsNull(),
-                         "OpenGl_View::SetWindow, "
-                         "Failed to create OpenGl window.");
+  if (theContext != nullptr
+  && !theParentVIew.IsNull())
+  {
+    throw Standard_ProgramError ("OpenGl_View::SetWindow(), internal error");
+  }
+
+  if (myParentView != nullptr)
+  {
+    myParentView->RemoveSubview (this);
+    myParentView = nullptr;
+  }
+
+  OpenGl_View* aParentView = dynamic_cast<OpenGl_View*> (theParentVIew.get());
+  if (!theParentVIew.IsNull())
+  {
+    if (aParentView == nullptr
+     || aParentView->GlWindow().IsNull()
+     || aParentView->GlWindow()->GetGlContext().IsNull())
+    {
+      throw Standard_ProgramError ("OpenGl_View::SetWindow(), internal error");
+    }
+
+    myParentView = aParentView;
+    myParentView->AddSubview (this);
+
+    Handle(Aspect_NeutralWindow) aSubWindow = Handle(Aspect_NeutralWindow)::DownCast(theWindow);
+    SubviewResized (aSubWindow);
+
+    const Handle(OpenGl_Window)& aParentGlWindow = aParentView->GlWindow();
+    Aspect_RenderingContext aRendCtx = aParentGlWindow->GetGlContext()->RenderingContext();
+    myWindow = myDriver->CreateRenderWindow (aParentGlWindow->PlatformWindow(), theWindow, aRendCtx);
+  }
+  else
+  {
+    myWindow = myDriver->CreateRenderWindow (theWindow, theWindow, theContext);
+  }
+  if (myWindow.IsNull())
+  {
+    throw Standard_ProgramError ("OpenGl_View::SetWindow, Failed to create OpenGl window");
+  }
 
   myWorkspace = new OpenGl_Workspace (this, myWindow);
   myWorldViewProjState.Reset();
@@ -414,10 +451,11 @@ void OpenGl_View::SetWindow (const Handle(Aspect_Window)& theWindow,
 // =======================================================================
 void OpenGl_View::Resized()
 {
-  if (myWindow.IsNull())
-    return;
-
-  myWindow->Resize();
+  base_type::Resized();
+  if (!myWindow.IsNull())
+  {
+    myWindow->Resize();
+  }
 }
 
 // =======================================================================
@@ -1157,9 +1195,10 @@ bool OpenGl_View::prepareFrameBuffers (Graphic3d_Camera::Projection& theProj)
   const bool hasTextureMsaa = aCtx->HasTextureMultisampling();
 
   bool toUseOit = myRenderParams.TransparencyMethod != Graphic3d_RTM_BLEND_UNORDERED
+               && !myIsSubviewComposer
                && checkOitCompatibility (aCtx, aNbSamples > 0);
 
-  const bool toInitImmediateFbo = myTransientDrawToFront
+  const bool toInitImmediateFbo = myTransientDrawToFront && !myIsSubviewComposer
                                && (!aCtx->caps->useSystemBuffer || (toUseOit && HasImmediateStructures()));
 
   if ( aFrameBuffer == NULL
@@ -1662,7 +1701,7 @@ void OpenGl_View::Redraw()
   OpenGl_FrameBuffer* aFrameBuffer = myFBO.get();
   bool toSwap = aCtx->IsRender()
             && !aCtx->caps->buffersNoSwap
-            &&  aFrameBuffer == NULL
+            &&  aFrameBuffer == nullptr
             &&  (!IsActiveXR() || myRenderParams.ToMirrorComposer);
   if ( aFrameBuffer == NULL
    && !aCtx->DefaultFrameBuffer().IsNull()
@@ -1738,7 +1777,9 @@ void OpenGl_View::Redraw()
     {
       toSwap = false;
     }
-    else if (aStereoMode == Graphic3d_StereoMode_SoftPageFlip && toSwap)
+    else if (aStereoMode == Graphic3d_StereoMode_SoftPageFlip
+          && toSwap
+          && myParentView == nullptr)
     {
       aCtx->SwapBuffers();
     }
@@ -1863,7 +1904,8 @@ void OpenGl_View::Redraw()
   }
 
   // Swap the buffers
-  if (toSwap)
+  if (toSwap
+   && myParentView == nullptr)
   {
     aCtx->SwapBuffers();
     if (!myMainSceneFbos[0]->IsValid())
@@ -1969,8 +2011,9 @@ void OpenGl_View::RedrawImmediate()
                               Standard_True) || toSwap;
     if (aStereoMode == Graphic3d_StereoMode_SoftPageFlip
     &&  toSwap
-    &&  myFBO.get() == NULL
-    && !aCtx->caps->buffersNoSwap)
+    &&  myFBO.get() == nullptr
+    && !aCtx->caps->buffersNoSwap
+    &&  myParentView == nullptr)
     {
       aCtx->SwapBuffers();
     }
@@ -2035,7 +2078,8 @@ void OpenGl_View::RedrawImmediate()
 
   if (toSwap
   &&  myFBO.get() == NULL
-  && !aCtx->caps->buffersNoSwap)
+  && !aCtx->caps->buffersNoSwap
+  &&  myParentView == nullptr)
   {
     aCtx->SwapBuffers();
   }
@@ -2159,9 +2203,91 @@ bool OpenGl_View::redrawImmediate (const Graphic3d_Camera::Projection theProject
 
   render (theProjection, theDrawFbo, theOitAccumFbo, Standard_True);
 
+  blitSubviews (theProjection, theDrawFbo);
+
   return !toCopyBackToFront;
 }
 
+// =======================================================================
+// function : blitSubviews
+// purpose  :
+// =======================================================================
+bool OpenGl_View::blitSubviews (const Graphic3d_Camera::Projection ,
+                                OpenGl_FrameBuffer* theDrawFbo)
+{
+  const Handle(OpenGl_Context)& aCtx = myWorkspace->GetGlContext();
+  if (aCtx->arbFBOBlit == nullptr)
+  {
+    return false;
+  }
+
+  bool isChanged = false;
+  for (const Handle(Graphic3d_CView)& aChildIter : mySubviews)
+  {
+    OpenGl_View* aSubView = dynamic_cast<OpenGl_View*> (aChildIter.get());
+    const Handle(OpenGl_FrameBuffer)& aChildFbo = !aSubView->myImmediateSceneFbos[0].IsNull()
+                                                 ? aSubView->myImmediateSceneFbos[0]
+                                                 : aSubView->myMainSceneFbos[0];
+    if (aChildFbo.IsNull() || !aChildFbo->IsValid())
+    {
+      continue;
+    }
+
+    aChildFbo->BindReadBuffer (aCtx);
+    if (theDrawFbo != NULL
+     && theDrawFbo->IsValid())
+    {
+      theDrawFbo->BindDrawBuffer (aCtx);
+    }
+    else
+    {
+      aCtx->arbFBO->glBindFramebuffer (GL_DRAW_FRAMEBUFFER, OpenGl_FrameBuffer::NO_FRAMEBUFFER);
+      aCtx->SetFrameBufferSRGB (false);
+    }
+
+    Graphic3d_Vec2i aWinSize (aCtx->Viewport()[2], aCtx->Viewport()[3]); //aSubView->GlWindow()->PlatformWindow()->Dimensions();
+    Graphic3d_Vec2i aSubViewSize = aChildFbo->GetVPSize();
+    Graphic3d_Vec2i aSubViewPos  = aSubView->SubviewTopLeft();
+    Graphic3d_Vec2i aDestSize    = aSubViewSize;
+    if (aSubView->RenderingParams().RenderResolutionScale != 1.0f)
+    {
+      aDestSize = Graphic3d_Vec2i (Graphic3d_Vec2d(aDestSize) / Graphic3d_Vec2d(aSubView->RenderingParams().RenderResolutionScale));
+    }
+    aSubViewPos.y() = aWinSize.y() - aDestSize.y() - aSubViewPos.y();
+
+    const GLint aFilterGl = aDestSize == aSubViewSize ? GL_NEAREST : GL_LINEAR;
+    aCtx->arbFBOBlit->glBlitFramebuffer (0, 0, aSubViewSize.x(), aSubViewSize.y(),
+                                         aSubViewPos.x(), aSubViewPos.y(), aSubViewPos.x() + aDestSize.x(), aSubViewPos.y() + aDestSize.y(),
+                                         GL_COLOR_BUFFER_BIT, aFilterGl);
+    const int anErr = aCtx->core11fwd->glGetError();
+    if (anErr != GL_NO_ERROR)
+    {
+      TCollection_ExtendedString aMsg = TCollection_ExtendedString() + "FBO blitting has failed [Error " + OpenGl_Context::FormatGlError (anErr) + "]\n"
+                                      + "  Please check your graphics driver settings or try updating driver.";
+      if (aChildFbo->NbSamples() != 0)
+      {
+        myToDisableMSAA = true;
+        aMsg += "\n  MSAA settings should not be overridden by driver!";
+      }
+      aCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, aMsg);
+    }
+
+    if (theDrawFbo != NULL
+     && theDrawFbo->IsValid())
+    {
+      theDrawFbo->BindBuffer (aCtx);
+    }
+    else
+    {
+      aCtx->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, OpenGl_FrameBuffer::NO_FRAMEBUFFER);
+      aCtx->SetFrameBufferSRGB (false);
+    }
+    isChanged = true;
+  }
+
+  return isChanged;
+}
+
 //=======================================================================
 //function : renderShadowMap
 //purpose  :
@@ -2409,9 +2535,16 @@ void OpenGl_View::renderStructs (Graphic3d_Camera::Projection theProjection,
                                  OpenGl_FrameBuffer*          theOitAccumFbo,
                                  const Standard_Boolean       theToDrawImmediate)
 {
+  if (myIsSubviewComposer)
+  {
+    return;
+  }
+
   myZLayers.UpdateCulling (myWorkspace, theToDrawImmediate);
-  if ( myZLayers.NbStructures() <= 0 )
+  if (myZLayers.NbStructures() <= 0)
+  {
     return;
+  }
 
   Handle(OpenGl_Context) aCtx = myWorkspace->GetGlContext();
   Standard_Boolean toRenderGL = theToDrawImmediate ||
index 4f5781aa64055d3b4f3268ebebb2dc5a4acc9e1b..138c0c1c4b057a54310ca372d0dc9782fe310cf7 100644 (file)
@@ -85,9 +85,8 @@ public:
   Standard_EXPORT Standard_Boolean SetImmediateModeDrawToFront (const Standard_Boolean theDrawToFrontBuffer) Standard_OVERRIDE;
 
   //! Creates and maps rendering window to the view.
-  //! @param theWindow [in] the window.
-  //! @param theContext [in] the rendering context. If NULL the context will be created internally.
-  Standard_EXPORT virtual void SetWindow (const Handle(Aspect_Window)&  theWindow,
+  Standard_EXPORT virtual void SetWindow (const Handle(Graphic3d_CView)& theParentVIew,
+                                          const Handle(Aspect_Window)& theWindow,
                                           const Aspect_RenderingContext theContext) Standard_OVERRIDE;
 
   //! Returns window associated with the view.
@@ -345,6 +344,10 @@ protected: //! @name low-level redrawing sub-routines
                                                 OpenGl_FrameBuffer* theOitAccumFbo,
                                                 const Standard_Boolean theIsPartialUpdate = Standard_False);
 
+  //! Blit subviews into this view.
+  Standard_EXPORT bool blitSubviews (const Graphic3d_Camera::Projection theProjection,
+                                     OpenGl_FrameBuffer* theDrawFbo);
+
   //! Blit image from/to specified buffers.
   Standard_EXPORT bool blitBuffers (OpenGl_FrameBuffer*    theReadFbo,
                                     OpenGl_FrameBuffer*    theDrawFbo,
index 16545d39fc4c5e3be57b7a6d4b71bfc2c1705c72..601396f81cb240d4fb008d9c12c1aea8f474c788 100644 (file)
@@ -169,17 +169,31 @@ namespace
 // function : OpenGl_Window
 // purpose  :
 // =======================================================================
-OpenGl_Window::OpenGl_Window (const Handle(OpenGl_GraphicDriver)& theDriver,
-                              const Handle(Aspect_Window)&  thePlatformWindow,
-                              Aspect_RenderingContext       theGContext,
-                              const Handle(OpenGl_Caps)&    theCaps,
-                              const Handle(OpenGl_Context)& theShareCtx)
-: myGlContext (new OpenGl_Context (theCaps)),
-  myOwnGContext (theGContext == 0),
-  myPlatformWindow (thePlatformWindow),
-  mySwapInterval (theCaps->swapInterval)
+OpenGl_Window::OpenGl_Window()
+: myOwnGContext (false),
+  mySwapInterval (0)
 {
-  myPlatformWindow->Size (myWidth, myHeight);
+  //
+}
+
+// =======================================================================
+// function : Init
+// purpose  :
+// =======================================================================
+void OpenGl_Window::Init (const Handle(OpenGl_GraphicDriver)& theDriver,
+                          const Handle(Aspect_Window)&  thePlatformWindow,
+                          const Handle(Aspect_Window)&  theSizeWindow,
+                          Aspect_RenderingContext       theGContext,
+                          const Handle(OpenGl_Caps)&    theCaps,
+                          const Handle(OpenGl_Context)& theShareCtx)
+{
+  myGlContext = new OpenGl_Context (theCaps);
+  myOwnGContext = (theGContext == 0);
+  myPlatformWindow = thePlatformWindow;
+  mySizeWindow = theSizeWindow;
+  mySwapInterval = theCaps->swapInterval;
+
+  mySizeWindow->Size (mySize.x(), mySize.y());
 
   Standard_Boolean isCoreProfile = Standard_False;
 
@@ -193,7 +207,6 @@ OpenGl_Window::OpenGl_Window (const Handle(OpenGl_GraphicDriver)& theDriver,
     && (EGLContext )theGContext == EGL_NO_CONTEXT))
   {
     throw Aspect_GraphicDeviceDefinitionError("OpenGl_Window, EGL does not provide compatible configurations!");
-    return;
   }
 
   EGLSurface anEglSurf = EGL_NO_SURFACE;
@@ -225,8 +238,8 @@ OpenGl_Window::OpenGl_Window (const Handle(OpenGl_GraphicDriver)& theDriver,
       #if !defined(__EMSCRIPTEN__) // eglCreatePbufferSurface() is not implemented by Emscripten EGL
         const int aSurfAttribs[] =
         {
-          EGL_WIDTH,  myWidth,
-          EGL_HEIGHT, myHeight,
+          EGL_WIDTH,  mySize.x(),
+          EGL_HEIGHT, mySize.y(),
           // EGL_KHR_gl_colorspace extension specifies if OpenGL should write into window buffer as into sRGB or RGB framebuffer
           //EGL_GL_COLORSPACE_KHR, !theCaps->sRGBDisable ? EGL_GL_COLORSPACE_SRGB_KHR : EGL_GL_COLORSPACE_LINEAR_KHR,
           EGL_NONE
@@ -258,8 +271,8 @@ OpenGl_Window::OpenGl_Window (const Handle(OpenGl_GraphicDriver)& theDriver,
       #if !defined(__EMSCRIPTEN__) // eglCreatePbufferSurface() is not implemented by Emscripten EGL
         const int aSurfAttribs[] =
         {
-          EGL_WIDTH,  myWidth,
-          EGL_HEIGHT, myHeight,
+          EGL_WIDTH,  mySize.x(),
+          EGL_HEIGHT, mySize.y(),
           // EGL_KHR_gl_colorspace extension specifies if OpenGL should write into window buffer as into sRGB or RGB framebuffer
           //EGL_GL_COLORSPACE_KHR, !theCaps->sRGBDisable ? EGL_GL_COLORSPACE_SRGB_KHR : EGL_GL_COLORSPACE_LINEAR_KHR,
           EGL_NONE
@@ -322,7 +335,6 @@ OpenGl_Window::OpenGl_Window (const Handle(OpenGl_GraphicDriver)& theDriver,
     TCollection_AsciiString aMsg ("OpenGl_Window::CreateWindow: ChoosePixelFormat failed. Error code: ");
     aMsg += (int )GetLastError();
     throw Aspect_GraphicDeviceDefinitionError(aMsg.ToCString());
-    return;
   }
 
   DescribePixelFormat (aWindowDC, aPixelFrmtId, sizeof(aPixelFrmt), &aPixelFrmt);
@@ -435,7 +447,6 @@ OpenGl_Window::OpenGl_Window (const Handle(OpenGl_GraphicDriver)& theDriver,
       TCollection_AsciiString aMsg("OpenGl_Window::CreateWindow: SetPixelFormat failed. Error code: ");
       aMsg += (int )GetLastError();
       throw Aspect_GraphicDeviceDefinitionError(aMsg.ToCString());
-      return;
     }
 
     // create GL context with extra options
@@ -526,7 +537,6 @@ OpenGl_Window::OpenGl_Window (const Handle(OpenGl_GraphicDriver)& theDriver,
       TCollection_AsciiString aMsg ("OpenGl_Window::CreateWindow: wglCreateContext failed. Error code: ");
       aMsg += (int )GetLastError();
       throw Aspect_GraphicDeviceDefinitionError(aMsg.ToCString());
-      return;
     }
   }
 
@@ -536,7 +546,6 @@ OpenGl_Window::OpenGl_Window (const Handle(OpenGl_GraphicDriver)& theDriver,
     TCollection_AsciiString aMsg ("OpenGl_Window::CreateWindow: wglShareLists failed. Error code: ");
     aMsg += (int )GetLastError();
     throw Aspect_GraphicDeviceDefinitionError(aMsg.ToCString());
-    return;
   }
 
   myGlContext->Init ((Aspect_Handle )aWindow, (Aspect_Handle )aWindowDC, (Aspect_RenderingContext )aGContext, isCoreProfile);
@@ -557,12 +566,10 @@ OpenGl_Window::OpenGl_Window (const Handle(OpenGl_GraphicDriver)& theDriver,
   if (aVis.get() == NULL)
   {
     throw Aspect_GraphicDeviceDefinitionError("OpenGl_Window::CreateWindow: XGetVisualInfo is unable to choose needed configuration in existing OpenGL context. ");
-    return;
   }
   else if (glXGetConfig (aDisp, aVis.get(), GLX_USE_GL, &isGl) != 0 || !isGl)
   {
     throw Aspect_GraphicDeviceDefinitionError("OpenGl_Window::CreateWindow: window Visual does not support GL rendering!");
-    return;
   }
 
   // create new context
@@ -635,7 +642,6 @@ OpenGl_Window::OpenGl_Window (const Handle(OpenGl_GraphicDriver)& theDriver,
     if (aGContext == NULL)
     {
       throw Aspect_GraphicDeviceDefinitionError("OpenGl_Window::CreateWindow: glXCreateContext failed.");
-      return;
     }
   }
 
@@ -675,7 +681,7 @@ OpenGl_Window::OpenGl_Window (const Handle(OpenGl_GraphicDriver)& theDriver,
 #endif
   myGlContext->Share (theShareCtx);
   myGlContext->SetSwapInterval (mySwapInterval);
-  Init();
+  init();
 }
 
 // =======================================================================
@@ -758,29 +764,29 @@ Standard_Boolean OpenGl_Window::Activate()
 // =======================================================================
 void OpenGl_Window::Resize()
 {
-  Standard_Integer aWidth = 0, aHeight = 0;
-  myPlatformWindow->Size (aWidth, aHeight);
-  if (myWidth  == aWidth
-   && myHeight == aHeight)
+  Graphic3d_Vec2i aWinSize;
+  mySizeWindow->Size (aWinSize.x(), aWinSize.y());
+  if (mySize == aWinSize)
   {
     // if the size is not changed - do nothing
     return;
   }
 
-  myWidth  = aWidth;
-  myHeight = aHeight;
+  mySize = aWinSize;
 
-  Init();
+  init();
 }
 
 // =======================================================================
-// function : Init
+// function : init
 // purpose  :
 // =======================================================================
-void OpenGl_Window::Init()
+void OpenGl_Window::init()
 {
   if (!Activate())
+  {
     return;
+  }
 
 #if defined(HAVE_EGL)
   if ((EGLSurface )myGlContext->myWindow == EGL_NO_SURFACE)
@@ -800,7 +806,7 @@ void OpenGl_Window::Init()
 
     OpenGl_ColorFormats aColorFormats;
     aColorFormats.Append (GL_RGBA8);
-    if (!aDefFbo->InitRenderBuffer (myGlContext, Graphic3d_Vec2i (myWidth, myHeight), aColorFormats, GL_DEPTH24_STENCIL8))
+    if (!aDefFbo->InitRenderBuffer (myGlContext, mySize, aColorFormats, GL_DEPTH24_STENCIL8))
     {
       TCollection_AsciiString aMsg ("OpenGl_Window::CreateWindow: default FBO creation failed");
       throw Aspect_GraphicDeviceDefinitionError(aMsg.ToCString());
@@ -810,8 +816,8 @@ void OpenGl_Window::Init()
   }
   else if (!myPlatformWindow->IsVirtual())
   {
-    eglQuerySurface ((EGLDisplay )myGlContext->myDisplay, (EGLSurface )myGlContext->myWindow, EGL_WIDTH,  &myWidth);
-    eglQuerySurface ((EGLDisplay )myGlContext->myDisplay, (EGLSurface )myGlContext->myWindow, EGL_HEIGHT, &myHeight);
+    eglQuerySurface ((EGLDisplay )myGlContext->myDisplay, (EGLSurface )myGlContext->myWindow, EGL_WIDTH,  &mySize.x());
+    eglQuerySurface ((EGLDisplay )myGlContext->myDisplay, (EGLSurface )myGlContext->myWindow, EGL_HEIGHT, &mySize.y());
   }
 #else
   //
@@ -819,7 +825,7 @@ void OpenGl_Window::Init()
 
   myGlContext->core11fwd->glDisable (GL_DITHER);
   myGlContext->core11fwd->glDisable (GL_SCISSOR_TEST);
-  const Standard_Integer aViewport[4] = { 0, 0, myWidth, myHeight };
+  const Standard_Integer aViewport[4] = { 0, 0, mySize.x(), mySize.y() };
   myGlContext->ResizeViewport (aViewport);
   myGlContext->SetDrawBuffer (GL_BACK);
   if (myGlContext->core11ffp != NULL)
index a9c85ac210e8354bc84ad506e058c151e3a6bf3f..daceafb7421006ef83e81b470dc999b5eab62d1b 100644 (file)
 class OpenGl_Context;
 class OpenGl_GraphicDriver;
 
-class OpenGl_Window;
 DEFINE_STANDARD_HANDLE(OpenGl_Window,Standard_Transient)
 
 //! This class represents low-level wrapper over window with GL context.
 //! The window itself should be provided to constructor.
 class OpenGl_Window : public Standard_Transient
 {
+  DEFINE_STANDARD_RTTIEXT(OpenGl_Window, Standard_Transient)
 public:
 
-  //! Main constructor - prepare GL context for specified window.
-  Standard_EXPORT OpenGl_Window (const Handle(OpenGl_GraphicDriver)& theDriver,
-                                 const Handle(Aspect_Window)&  thePlatformWindow,
-                                 Aspect_RenderingContext       theGContext,
-                                 const Handle(OpenGl_Caps)&    theCaps,
-                                 const Handle(OpenGl_Context)& theShareCtx);
+  //! Empty constructor.
+  Standard_EXPORT OpenGl_Window();
+
+  //! Initialize the new window - prepare GL context for specified window.
+  //! Throws exception in case of failure.
+  Standard_EXPORT void Init (const Handle(OpenGl_GraphicDriver)& theDriver,
+                             const Handle(Aspect_Window)&  thePlatformWindow,
+                             const Handle(Aspect_Window)&  theSizeWindow,
+                             Aspect_RenderingContext       theGContext,
+                             const Handle(OpenGl_Caps)&    theCaps,
+                             const Handle(OpenGl_Context)& theShareCtx);
 
   //! Destructor
   Standard_EXPORT virtual ~OpenGl_Window();
@@ -59,15 +64,17 @@ public:
   //! Resizes the window.
   Standard_EXPORT virtual void Resize();
 
-  Handle(Aspect_Window) PlatformWindow() { return myPlatformWindow; }
+  //! Return platform window.
+  const Handle(Aspect_Window)& PlatformWindow() { return myPlatformWindow; }
 
-  Standard_Integer Width()  const { return myWidth; }
-  Standard_Integer Height() const { return myHeight; }
+  //! Return window object defining dimensions.
+  const Handle(Aspect_Window)& SizeWindow() { return mySizeWindow; }
 
-  const Handle(OpenGl_Context)& GetGlContext() const { return myGlContext; }
+  Standard_Integer Width()  const { return mySize.x(); }
+  Standard_Integer Height() const { return mySize.y(); }
 
-  //! Activates GL context and setup viewport.
-  Standard_EXPORT void Init();
+  //! Return OpenGL context.
+  const Handle(OpenGl_Context)& GetGlContext() const { return myGlContext; }
 
   //! Makes GL context for this window active in current thread
   Standard_EXPORT virtual Standard_Boolean Activate();
@@ -75,28 +82,27 @@ public:
   //! Sets swap interval for this window according to the context's settings.
   Standard_EXPORT void SetSwapInterval (Standard_Boolean theToForceNoSync);
 
+protected:
+
+  //! Activates GL context and setup viewport.
+  Standard_EXPORT void init();
+
 protected:
 
   Handle(OpenGl_Context) myGlContext;
-  Standard_Boolean       myOwnGContext; //!< set to TRUE if GL context was not created by this class
+  Standard_Boolean       myOwnGContext;    //!< set to TRUE if GL context was not created by this class
   Handle(Aspect_Window)  myPlatformWindow; //!< software platform window wrapper
+  Handle(Aspect_Window)  mySizeWindow;     //!< window object defining dimensions
 #if defined(__APPLE__)
 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
   UIView*                myUIView;
 #endif
-  Standard_Integer       myWidthPt;     //!< window width  in logical units
-  Standard_Integer       myHeightPt;    //!< window height in logical units
+  Graphic3d_Vec2i        mySizePt;      //!< window width x height in logical units
 #endif
-  Standard_Integer       myWidth;       //!< window width  in pixels
-  Standard_Integer       myHeight;      //!< window height in pixels
+  Graphic3d_Vec2i        mySize;        //!< window width x height in pixels
 
   Standard_Integer       mySwapInterval;//!< last assigned swap interval (VSync) for this window
 
-public:
-
-  DEFINE_STANDARD_RTTIEXT(OpenGl_Window,Standard_Transient) // Type definition
-  DEFINE_STANDARD_ALLOC
-
 };
 
 #endif //_OpenGl_Window_Header
index af552243a5401ddd8a76645715a6f39223dfe75b..f1cc8c19d55f1c63459e1a692726022b266ab733 100644 (file)
 // function : OpenGl_Window
 // purpose  :
 // =======================================================================
-OpenGl_Window::OpenGl_Window (const Handle(OpenGl_GraphicDriver)& theDriver,
-                              const Handle(Aspect_Window)&  thePlatformWindow,
-                              Aspect_RenderingContext       theGContext,
-                              const Handle(OpenGl_Caps)&    theCaps,
-                              const Handle(OpenGl_Context)& theShareCtx)
-: myGlContext (new OpenGl_Context (theCaps)),
-  myOwnGContext (theGContext == 0),
-  myPlatformWindow (thePlatformWindow),
+OpenGl_Window::OpenGl_Window()
+: myOwnGContext (false),
+  mySwapInterval (0)
+{
+  //
+}
+
+// =======================================================================
+// function : Init
+// purpose  :
+// =======================================================================
+void OpenGl_Window::Init (const Handle(OpenGl_GraphicDriver)& theDriver,
+                          const Handle(Aspect_Window)&  thePlatformWindow,
+                          const Handle(Aspect_Window)&  theSizeWindow,
+                          Aspect_RenderingContext       theGContext,
+                          const Handle(OpenGl_Caps)&    theCaps,
+                          const Handle(OpenGl_Context)& theShareCtx)
+{
+  myGlContext = new OpenGl_Context (theCaps);
+  myOwnGContext = (theGContext == 0);
+  myPlatformWindow = thePlatformWindow;
+  mySizeWindow = theSizeWindow;
 #if defined(__APPLE__) && defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
-  myUIView (NULL),
+  myUIView = NULL;
 #endif
-  mySwapInterval (theCaps->swapInterval)
-{
+  mySwapInterval = theCaps->swapInterval;
+
   (void )theDriver;
-  myPlatformWindow->Size (myWidth, myHeight);
+  mySizeWindow->Size (mySize.x(), mySize.y());
 
 #if defined(__APPLE__)
-  myWidthPt  = myWidth;
-  myHeightPt = myHeight;
+  mySizePt = mySize;
 #endif
 
 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
@@ -102,7 +115,6 @@ OpenGl_Window::OpenGl_Window (const Handle(OpenGl_GraphicDriver)& theDriver,
       {
         TCollection_AsciiString aMsg ("OpenGl_Window::CreateWindow: EAGLContext creation failed");
         throw Aspect_GraphicDeviceDefinitionError(aMsg.ToCString());
-        return;
       }
     }
 
@@ -114,7 +126,6 @@ OpenGl_Window::OpenGl_Window (const Handle(OpenGl_GraphicDriver)& theDriver,
     {
       TCollection_AsciiString aMsg ("OpenGl_Window::CreateWindow: EAGLContext can not be assigned");
       throw Aspect_GraphicDeviceDefinitionError(aMsg.ToCString());
-      return;
     }
 
     myGlContext->Init (aGLContext, Standard_False);
@@ -199,7 +210,6 @@ OpenGl_Window::OpenGl_Window (const Handle(OpenGl_GraphicDriver)& theDriver,
     {
       TCollection_AsciiString aMsg ("OpenGl_Window::CreateWindow: NSOpenGLContext creation failed");
       throw Aspect_GraphicDeviceDefinitionError(aMsg.ToCString());
-      return;
     }
 
     if (aTryStereo == 0
@@ -227,7 +237,7 @@ OpenGl_Window::OpenGl_Window (const Handle(OpenGl_GraphicDriver)& theDriver,
 
   myGlContext->Share (theShareCtx);
   myGlContext->SetSwapInterval (mySwapInterval);
-  Init();
+  init();
 }
 
 // =======================================================================
@@ -266,9 +276,9 @@ void OpenGl_Window::Resize()
   // If the size is not changed - do nothing
   Standard_Integer aWidthPt  = 0;
   Standard_Integer aHeightPt = 0;
-  myPlatformWindow->Size (aWidthPt, aHeightPt);
-  if (myWidthPt  == aWidthPt
-   && myHeightPt == aHeightPt)
+  mySizeWindow->Size (aWidthPt, aHeightPt);
+  if (mySizePt.x() == aWidthPt
+   && mySizePt.y() == aHeightPt)
   {
   #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
     return;
@@ -285,25 +295,25 @@ void OpenGl_Window::Resize()
 
     NSRect aBounds = [aView bounds];
     NSSize aRes    = [aView convertSizeToBacking: aBounds.size];
-    if (myWidth  == Standard_Integer(aRes.width)
-     && myHeight == Standard_Integer(aRes.height))
+    if (mySize.x() == Standard_Integer(aRes.width)
+     && mySize.y() == Standard_Integer(aRes.height))
     {
       return;
     }
   #endif
   }
 
-  myWidthPt  = aWidthPt;
-  myHeightPt = aHeightPt;
+  mySizePt.x() = aWidthPt;
+  mySizePt.y() = aHeightPt;
 
-  Init();
+  init();
 }
 
 // =======================================================================
-// function : Init
+// function : init
 // purpose  :
 // =======================================================================
-void OpenGl_Window::Init()
+void OpenGl_Window::init()
 {
   if (!Activate())
   {
@@ -329,11 +339,11 @@ void OpenGl_Window::Init()
     myGlContext->Functions()->glGenRenderbuffers (1, &aWinRBColor);
     myGlContext->Functions()->glBindRenderbuffer (GL_RENDERBUFFER, aWinRBColor);
     [aGLCtx renderbufferStorage: GL_RENDERBUFFER fromDrawable: anEaglLayer];
-    myGlContext->Functions()->glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH,  &myWidth);
-    myGlContext->Functions()->glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &myHeight);
+    myGlContext->Functions()->glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH,  &mySize.x());
+    myGlContext->Functions()->glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &mySize.y());
     myGlContext->Functions()->glBindRenderbuffer (GL_RENDERBUFFER, 0);
 
-    if (!aDefFbo->InitWithRB (myGlContext, Graphic3d_Vec2i (myWidth, myHeight), GL_RGBA8, GL_DEPTH24_STENCIL8, aWinRBColor))
+    if (!aDefFbo->InitWithRB (myGlContext, mySize, GL_RGBA8, GL_DEPTH24_STENCIL8, aWinRBColor))
     {
       TCollection_AsciiString aMsg ("OpenGl_Window::CreateWindow: default FBO creation failed");
       throw Aspect_GraphicDeviceDefinitionError(aMsg.ToCString());
@@ -349,8 +359,8 @@ void OpenGl_Window::Init()
       return;
     }
 
-    myWidth  = aDefFbo->GetVPSizeX();
-    myHeight = aDefFbo->GetVPSizeY();
+    mySize.x() = aDefFbo->GetVPSizeX();
+    mySize.y() = aDefFbo->GetVPSizeY();
   }
   myGlContext->SetDefaultFrameBuffer (aDefFbo);
   aDefFbo->BindBuffer (myGlContext);
@@ -368,21 +378,21 @@ Standard_ENABLE_DEPRECATION_WARNINGS
   if ([aView respondsToSelector: @selector(convertSizeToBacking:)])
   {
     NSSize aRes = [aView convertSizeToBacking: aBounds.size];
-    myWidth  = Standard_Integer(aRes.width);
-    myHeight = Standard_Integer(aRes.height);
+    mySize.x() = Standard_Integer(aRes.width);
+    mySize.y() = Standard_Integer(aRes.height);
   }
   else
   {
-    myWidth  = Standard_Integer(aBounds.size.width);
-    myHeight = Standard_Integer(aBounds.size.height);
+    mySize.x() = Standard_Integer(aBounds.size.width);
+    mySize.y() = Standard_Integer(aBounds.size.height);
   }
-  myWidthPt  = Standard_Integer(aBounds.size.width);
-  myHeightPt = Standard_Integer(aBounds.size.height);
+  mySizePt.x() = Standard_Integer(aBounds.size.width);
+  mySizePt.y() = Standard_Integer(aBounds.size.height);
 #endif
 
   myGlContext->core11fwd->glDisable (GL_DITHER);
   myGlContext->core11fwd->glDisable (GL_SCISSOR_TEST);
-  myGlContext->core11fwd->glViewport (0, 0, myWidth, myHeight);
+  myGlContext->core11fwd->glViewport (0, 0, mySize.x(), mySize.y());
   if (myGlContext->GraphicsLibrary() != Aspect_GraphicsLibrary_OpenGLES)
   {
     myGlContext->core11fwd->glDrawBuffer (GL_BACK);
index 7f8eb4afec4282897ad6a1abbaf1be0c2f7a9dd0..f6050cf34b66e2157d888b427e36d3bc3bbc7af3 100644 (file)
@@ -16,6 +16,7 @@
 #include <Aspect_CircularGrid.hxx>
 #include <Aspect_GradientBackground.hxx>
 #include <Aspect_Grid.hxx>
+#include <Aspect_NeutralWindow.hxx>
 #include <Aspect_RectangularGrid.hxx>
 #include <Aspect_Window.hxx>
 #include <Bnd_Box.hxx>
@@ -142,6 +143,27 @@ V3d_View::V3d_View (const Handle(V3d_Viewer)& theViewer, const Handle(V3d_View)&
 //=============================================================================
 V3d_View::~V3d_View()
 {
+  if (myParentView != nullptr)
+  {
+    myParentView->RemoveSubview (this);
+    myParentView = nullptr;
+  }
+  {
+    NCollection_Sequence<Handle(V3d_View)> aSubviews = mySubviews;
+    mySubviews.Clear();
+    for (const Handle(V3d_View)& aViewIter : aSubviews)
+    {
+      //aViewIter->Remove();
+      aViewIter->myParentView = nullptr;
+      aViewIter->MyWindow.Nullify();
+      aViewIter->myView->Remove();
+      if (aViewIter->MyViewer != nullptr)
+      {
+        aViewIter->MyViewer->SetViewOff (aViewIter);
+      }
+    }
+  }
+
   if (!myView->IsRemoved())
   {
     myView->Remove();
@@ -164,7 +186,7 @@ void V3d_View::SetMagnify (const Handle(Aspect_Window)& theWindow,
     Standard_Real aU1, aV1, aU2, aV2;
     thePreviousView->Convert (theX1, theY1, aU1, aV1);
     thePreviousView->Convert (theX2, theY2, aU2, aV2);
-    myView->SetWindow (theWindow);
+    myView->SetWindow (Handle(Graphic3d_CView)(), theWindow, nullptr);
     FitAll (aU1, aV1, aU2, aV2);
     MyViewer->SetViewOn (this);
     MyWindow = theWindow;
@@ -185,10 +207,14 @@ void V3d_View::SetWindow (const Handle(Aspect_Window)&  theWindow,
   {
     return;
   }
+  if (myParentView != nullptr)
+  {
+    throw Standard_ProgramError ("V3d_View::SetWindow() called twice");
+  }
 
   // method V3d_View::SetWindow() should assign the field MyWindow before calling Redraw()
   MyWindow = theWindow;
-  myView->SetWindow (theWindow, theContext);
+  myView->SetWindow (Handle(Graphic3d_CView)(), theWindow, theContext);
   MyViewer->SetViewOn (this);
   SetRatio();
   if (myImmediateUpdate)
@@ -197,6 +223,49 @@ void V3d_View::SetWindow (const Handle(Aspect_Window)&  theWindow,
   }
 }
 
+//=============================================================================
+//function : SetWindow
+//purpose  :
+//=============================================================================
+void V3d_View::SetWindow (const Handle(V3d_View)& theParentView,
+                          const Graphic3d_Vec2d& theSize,
+                          Aspect_TypeOfTriedronPosition theCorner,
+                          const Graphic3d_Vec2d& theOffset,
+                          const Graphic3d_Vec2i& theMargins)
+{
+  if (myView->IsRemoved())
+  {
+    return;
+  }
+
+  Handle(V3d_View) aParentView = !theParentView->IsSubview()
+                               ? theParentView
+                               : theParentView->ParentView();
+  if (aParentView != myParentView)
+  {
+    if (myParentView != nullptr)
+    {
+      throw Standard_ProgramError ("V3d_View::SetWindow() called twice");
+    }
+
+    myParentView = aParentView.get();
+    aParentView->AddSubview (this);
+  }
+
+  Handle(Aspect_NeutralWindow) aWindow = new Aspect_NeutralWindow();
+  aWindow->SetVirtual (true);
+  aWindow->SetSize (4, 4);
+  myView->SetSubviewCorner (theCorner);
+  myView->SetSubviewSize (theSize);
+  myView->SetSubviewOffset (theOffset);
+  myView->SetSubviewMargins (theMargins);
+
+  MyWindow = aWindow;
+  myView->SetWindow (aParentView->View(), aWindow, 0);
+  MyViewer->SetViewOn (this);
+  SetRatio();
+}
+
 //=============================================================================
 //function : Remove
 //purpose  :
@@ -212,10 +281,83 @@ void V3d_View::Remove()
     myTrihedron->Erase();
   }
 
-  MyViewer->DelView (this);
+  if (myParentView != nullptr)
+  {
+    myParentView->RemoveSubview (this);
+    myParentView = nullptr;
+  }
+  {
+    NCollection_Sequence<Handle(V3d_View)> aSubviews = mySubviews;
+    mySubviews.Clear();
+    for (const Handle(V3d_View)& aViewIter : aSubviews)
+    {
+      aViewIter->Remove();
+    }
+  }
+
+  if (MyViewer != nullptr)
+  {
+    MyViewer->DelView (this);
+    MyViewer = nullptr;
+  }
   myView->Remove();
-  Handle(Aspect_Window)& aWin = const_cast<Handle(Aspect_Window)&> (MyWindow);
-  aWin.Nullify();
+  MyWindow.Nullify();
+}
+
+// =======================================================================
+// function : AddSubview
+// purpose  :
+// =======================================================================
+void V3d_View::AddSubview (const Handle(V3d_View)& theView)
+{
+  mySubviews.Append (theView);
+}
+
+// =======================================================================
+// function : RemoveSubview
+// purpose  :
+// =======================================================================
+bool V3d_View::RemoveSubview (const V3d_View* theView)
+{
+  for (NCollection_Sequence<Handle(V3d_View)>::Iterator aViewIter (mySubviews); aViewIter.More(); aViewIter.Next())
+  {
+    if (aViewIter.Value() == theView)
+    {
+      mySubviews.Remove (aViewIter);
+      return true;
+    }
+  }
+  return false;
+}
+
+// =============================================================================
+// function : PickSubview
+// purpose  :
+// =============================================================================
+Handle(V3d_View) V3d_View::PickSubview (const Graphic3d_Vec2i& thePnt) const
+{
+  if (thePnt.x() < 0
+   || thePnt.x() >= MyWindow->Dimensions().x()
+   || thePnt.y() < 0
+   || thePnt.y() >= MyWindow->Dimensions().y())
+  {
+    return Handle(V3d_View)();
+  }
+
+  // iterate in opposite direction - from front to bottom views
+  for (Standard_Integer aSubviewIter = mySubviews.Upper(); aSubviewIter >= mySubviews.Lower(); --aSubviewIter)
+  {
+    const Handle(V3d_View)& aSubview = mySubviews.Value (aSubviewIter);
+    if (thePnt.x() >= aSubview->View()->SubviewTopLeft().x()
+     && thePnt.x() < (aSubview->View()->SubviewTopLeft().x() + aSubview->Window()->Dimensions().x())
+     && thePnt.y() >= aSubview->View()->SubviewTopLeft().y()
+     && thePnt.y() < (aSubview->View()->SubviewTopLeft().y() + aSubview->Window()->Dimensions().y()))
+    {
+      return aSubview;
+    }
+  }
+
+  return this;
 }
 
 //=============================================================================
index aa4159e1a24661f428ed0ff45cd1f05d3e757343..abea2dd347de0dd089995d22696d4d983f0e09c4 100644 (file)
@@ -76,7 +76,7 @@ DEFINE_STANDARD_HANDLE(V3d_View, Standard_Transient)
 //! View->Move(15.,-5.,0.,False)    (Next motion)
 class V3d_View : public Standard_Transient
 {
-
+  DEFINE_STANDARD_RTTIEXT(V3d_View, Standard_Transient)
 public:
 
   //! Initializes the view.
@@ -97,6 +97,25 @@ public:
   Standard_EXPORT void SetWindow (const Handle(Aspect_Window)& theWindow,
                                   const Aspect_RenderingContext theContext = NULL);
 
+  //! Activates the view as subview of another view.
+  //! @param[in] theParentView parent view to put subview into
+  //! @param[in] theSize subview dimensions;
+  //!                    values >= 2   define size in pixels,
+  //!                    values <= 1.0 define size as a fraction of parent view
+  //! @param[in] theCorner corner within parent view
+  //! @param[in] theOffset offset from the corner;
+  //!                      values >= 1   define offset in pixels,
+  //!                      values <  1.0 define offset as a fraction of parent view
+  //! @param[in] theMargins subview margins in pixels
+  //!
+  //! Example: to split parent view horizontally into 2 subview,
+  //! define one subview with Size=(0.5,1.0),Offset=(0.0,0.0), and 2nd with Size=(0.5,1.0),Offset=(5.0,0.0);
+  Standard_EXPORT void SetWindow (const Handle(V3d_View)& theParentView,
+                                  const Graphic3d_Vec2d& theSize,
+                                  Aspect_TypeOfTriedronPosition theCorner = Aspect_TOTP_LEFT_UPPER,
+                                  const Graphic3d_Vec2d& theOffset = Graphic3d_Vec2d(),
+                                  const Graphic3d_Vec2i& theMargins = Graphic3d_Vec2i());
+
   Standard_EXPORT void SetMagnify (const Handle(Aspect_Window)& theWindow,
                                    const Handle(V3d_View)& thePreviousView,
                                    const Standard_Integer theX1,
@@ -953,7 +972,25 @@ public:
   //! Dumps the content of me into the stream
   Standard_EXPORT void DumpJson (Standard_OStream& theOStream, Standard_Integer theDepth = -1) const;
 
-  DEFINE_STANDARD_RTTIEXT(V3d_View,Standard_Transient)
+public: //! @name subvew management
+
+  //! Return TRUE if this is a subview of another view.
+  bool IsSubview() const { return myParentView != nullptr; }
+
+  //! Return parent View or NULL if this is not a subview.
+  V3d_View* ParentView() { return myParentView; }
+
+  //! Return subview list.
+  const NCollection_Sequence<Handle(V3d_View)>& Subviews() const { return mySubviews; }
+
+  //! Pick subview from the given 2D point.
+  Standard_EXPORT Handle(V3d_View) PickSubview (const Graphic3d_Vec2i& thePnt) const;
+
+  //! Add subview to the list.
+  Standard_EXPORT void AddSubview (const Handle(V3d_View)& theView);
+
+  //! Remove subview from the list.
+  Standard_EXPORT bool RemoveSubview (const V3d_View* theView);
 
 public: //! @name deprecated methods
 
@@ -1033,6 +1070,10 @@ protected:
 private:
 
   V3d_Viewer* MyViewer;
+
+  NCollection_Sequence<Handle(V3d_View)> mySubviews;
+  V3d_View* myParentView;
+
   V3d_ListOfLight myActiveLights;
   gp_Dir myDefaultViewAxis;
   gp_Pnt myDefaultViewPoint;
index ad6467fab4aaa21d4a55325b985f395f3f5b388f..d1ac7efdf4c0408789c6b7c90fc92685c24dc366 100644 (file)
@@ -143,9 +143,23 @@ void V3d_Viewer::SetViewOff (const Handle(V3d_View)& theView)
 // ========================================================================
 void V3d_Viewer::Redraw() const
 {
-  for (V3d_ListOfView::Iterator aDefViewIter (myDefinedViews); aDefViewIter.More(); aDefViewIter.Next())
+  for (int aSubViewPass = 0; aSubViewPass < 2; ++aSubViewPass)
   {
-    aDefViewIter.Value()->Redraw();
+    // redraw subviews first
+    const bool isSubViewPass = (aSubViewPass == 0);
+    for (const Handle(V3d_View)& aViewIter : myDefinedViews)
+    {
+      if (isSubViewPass
+       && aViewIter->IsSubview())
+      {
+        aViewIter->Redraw();
+      }
+      else if (!isSubViewPass
+            && !aViewIter->IsSubview())
+      {
+        aViewIter->Redraw();
+      }
+    }
   }
 }
 
@@ -155,9 +169,23 @@ void V3d_Viewer::Redraw() const
 // ========================================================================
 void V3d_Viewer::RedrawImmediate() const
 {
-  for (V3d_ListOfView::Iterator aDefViewIter (myDefinedViews); aDefViewIter.More(); aDefViewIter.Next())
+  for (int aSubViewPass = 0; aSubViewPass < 2; ++aSubViewPass)
   {
-    aDefViewIter.Value()->RedrawImmediate();
+    // redraw subviews first
+    const bool isSubViewPass = (aSubViewPass == 0);
+    for (const Handle(V3d_View)& aViewIter : myDefinedViews)
+    {
+      if (isSubViewPass
+       && aViewIter->IsSubview())
+      {
+        aViewIter->RedrawImmediate();
+      }
+      else if (!isSubViewPass
+            && !aViewIter->IsSubview())
+      {
+        aViewIter->RedrawImmediate();
+      }
+    }
   }
 }
 
@@ -232,10 +260,24 @@ void V3d_Viewer::AddView (const Handle(V3d_View)& theView)
 // function : DelView
 // purpose  :
 // ========================================================================
-void V3d_Viewer::DelView (const Handle(V3d_View)& theView)
+void V3d_Viewer::DelView (const V3d_View* theView)
 {
-  myActiveViews.Remove (theView);
-  myDefinedViews.Remove (theView);
+  for (V3d_ListOfView::Iterator aViewIter (myActiveViews); aViewIter.More(); aViewIter.Next())
+  {
+    if (aViewIter.Value() == theView)
+    {
+      myActiveViews.Remove (aViewIter);
+      break;
+    }
+  }
+  for (V3d_ListOfView::Iterator aViewIter (myDefinedViews); aViewIter.More(); aViewIter.Next())
+  {
+    if (aViewIter.Value() == theView)
+    {
+      myDefinedViews.Remove (aViewIter);
+      break;
+    }
+  }
 }
 
 //=======================================================================
index de405d822ad59bd1a0625129d88a5ee668b75cce..12bcca7689b4d4b16282636dde0bd36a0b3e8323 100644 (file)
@@ -482,7 +482,7 @@ private:
   Standard_EXPORT void AddView (const Handle(V3d_View)& theView);
   
   //! Delete View in Sequence Of Views.
-  Standard_EXPORT void DelView (const Handle(V3d_View)& theView);
+  Standard_EXPORT void DelView (const V3d_View* theView);
   
 private:
 
index 35e392a98bfce59f9b795c9bad6923025360ab61..983e07ea79e8a299dd1c195abfc203cc2d58caed 100644 (file)
@@ -4776,11 +4776,14 @@ inline Standard_Boolean parseTrsfPersFlag (const TCollection_AsciiString& theFla
   return Standard_True;
 }
 
-//! Auxiliary method to parse transformation persistence flags
-inline Standard_Boolean parseTrsfPersCorner (const TCollection_AsciiString& theString,
-                                             Aspect_TypeOfTriedronPosition& theCorner)
+// =============================================================================
+// function : ParseCorner
+// purpose  :
+// =============================================================================
+Standard_Boolean ViewerTest::ParseCorner (Standard_CString theArg,
+                                          Aspect_TypeOfTriedronPosition& theCorner)
 {
-  TCollection_AsciiString aString (theString);
+  TCollection_AsciiString aString (theArg);
   aString.LowerCase();
   if (aString == "center")
   {
@@ -4806,25 +4809,33 @@ inline Standard_Boolean parseTrsfPersCorner (const TCollection_AsciiString& theS
   }
   else if (aString == "topleft"
         || aString == "leftupper"
-        || aString == "upperleft")
+        || aString == "upperleft"
+        || aString == "left_upper"
+        || aString == "upper_left")
   {
     theCorner = Aspect_TOTP_LEFT_UPPER;
   }
   else if (aString == "bottomleft"
         || aString == "leftlower"
-        || aString == "lowerleft")
+        || aString == "lowerleft"
+        || aString == "left_lower"
+        || aString == "lower_left")
   {
     theCorner = Aspect_TOTP_LEFT_LOWER;
   }
   else if (aString == "topright"
         || aString == "rightupper"
-        || aString == "upperright")
+        || aString == "upperright"
+        || aString == "right_upper"
+        || aString == "upper_right")
   {
     theCorner = Aspect_TOTP_RIGHT_UPPER;
   }
   else if (aString == "bottomright"
         || aString == "lowerright"
-        || aString == "rightlower")
+        || aString == "rightlower"
+        || aString == "right_lower"
+        || aString == "lower_right")
   {
     theCorner = Aspect_TOTP_RIGHT_LOWER;
   }
@@ -4964,7 +4975,7 @@ static int VDisplay2 (Draw_Interpretor& theDI,
       if (anArgIter + 1 < theArgNb)
       {
         Aspect_TypeOfTriedronPosition aCorner = Aspect_TOTP_CENTER;
-        if (parseTrsfPersCorner (theArgVec[anArgIter + 1], aCorner))
+        if (ViewerTest::ParseCorner (theArgVec[anArgIter + 1], aCorner))
         {
           ++anArgIter;
           aTrsfPers->SetCorner2d (aCorner);
index 7f0804cbc7565026741ffd9044ce675ca415f241..77a0cce5537767d383893839e0d7055e84ceb0b8 100644 (file)
 #include <AIS_KindOfInteractive.hxx>
 #include <Aspect_TypeOfLine.hxx>
 #include <Aspect_TypeOfMarker.hxx>
+#include <Aspect_TypeOfTriedronPosition.hxx>
 #include <Draw_Interpretor.hxx>
 #include <Graphic3d_TypeOfShadingModel.hxx>
+#include <Graphic3d_Vec2.hxx>
 #include <Graphic3d_ZLayerId.hxx>
 #include <TCollection_AsciiString.hxx>
 #include <TColStd_HArray1OfTransient.hxx>
@@ -36,6 +38,23 @@ class ViewerTest_EventManager;
 class TopoDS_Shape;
 class WNT_WClass;
 
+//! Parameters for creating new view.
+struct ViewerTest_VinitParams
+{
+  TCollection_AsciiString ViewName;
+  TCollection_AsciiString DisplayName;
+  Handle(V3d_View) ViewToClone;
+  Handle(V3d_View) ParentView;
+  Graphic3d_Vec2d  Offset;
+  Graphic3d_Vec2d  Size;
+  Aspect_TypeOfTriedronPosition Corner;
+  Graphic3d_Vec2i  SubviewMargins;
+  Standard_Boolean IsVirtual;
+  Standard_Boolean IsComposer;
+
+  ViewerTest_VinitParams() : Corner (Aspect_TOTP_LEFT_UPPER), IsVirtual (false), IsComposer (false) {}
+};
+
 class ViewerTest
 {
 public:
@@ -45,25 +64,39 @@ public:
   //! Loads all Draw commands of  V2d & V3d. Used for plugin.
   Standard_EXPORT static void Factory (Draw_Interpretor& theDI);
 
-  //! Creates view with default or custom name
-  //! and adds this name in map to manage multiple views.
+
+
+  //! Creates view with default or custom name and adds this name in map to manage multiple views.
   //! Implemented in ViewerTest_ViewerCommands.cxx.
-  //! @param thePxLeft      left position of newly created window
-  //! @param thePxTop       top  position of newly created window
-  //! @param thePxWidth     width of newly created window
-  //! @param thePxHeight    height of newly created window
-  //! @param theViewName    name of newly created View
-  //! @oaram theDisplayName display name
-  //! @param theViewToClone when specified, the new View will copy properties of existing one
-  //! @param theIsVirtual   force creation of virtual off-screen window within interactive session
-  Standard_EXPORT static TCollection_AsciiString ViewerInit (const Standard_Integer thePxLeft   = 0,
-                                                             const Standard_Integer thePxTop    = 0,
-                                                             const Standard_Integer thePxWidth  = 0,
-                                                             const Standard_Integer thePxHeight = 0,
-                                                             const TCollection_AsciiString& theViewName = "",
-                                                             const TCollection_AsciiString& theDisplayName = "",
-                                                             const Handle(V3d_View)& theViewToClone = Handle(V3d_View)(),
-                                                             const Standard_Boolean theIsVirtual = false);
+  Standard_EXPORT static TCollection_AsciiString ViewerInit (const ViewerTest_VinitParams& theParams);
+
+  //! Creates view.
+  static TCollection_AsciiString ViewerInit (const TCollection_AsciiString& theViewName = "")
+  {
+    ViewerTest_VinitParams aParams;
+    aParams.ViewName = theViewName;
+    return ViewerInit (aParams);
+  }
+
+  //! Creates view.
+  static TCollection_AsciiString ViewerInit (const Standard_Integer thePxLeft,
+                                             const Standard_Integer thePxTop,
+                                             const Standard_Integer thePxWidth,
+                                             const Standard_Integer thePxHeight,
+                                             const TCollection_AsciiString& theViewName,
+                                             const TCollection_AsciiString& theDisplayName = "",
+                                             const Handle(V3d_View)& theViewToClone = Handle(V3d_View)(),
+                                             const Standard_Boolean theIsVirtual = false)
+  {
+    ViewerTest_VinitParams aParams;
+    aParams.Offset.SetValues ((float )thePxLeft, (float)thePxTop);
+    aParams.Size.SetValues ((float)thePxWidth, (float)thePxHeight);
+    aParams.ViewName = theViewName;
+    aParams.DisplayName = theDisplayName;
+    aParams.ViewToClone = theViewToClone;
+    aParams.IsVirtual = theIsVirtual;
+    return ViewerInit (aParams);
+  }
 
   Standard_EXPORT static void RemoveViewName (const TCollection_AsciiString& theName);
 
@@ -72,6 +105,10 @@ public:
 
   Standard_EXPORT static TCollection_AsciiString GetCurrentViewName();
 
+  //! Make the view active
+  Standard_EXPORT static void ActivateView (const Handle(V3d_View)& theView,
+                                            Standard_Boolean theToUpdate);
+
   //! Removes view and clear all maps
   //! with information about its resources if necessary
   Standard_EXPORT static void RemoveView (const TCollection_AsciiString& theViewName,
@@ -207,6 +244,10 @@ public:
     return parseZLayer (theArg, true, theLayer);
   }
 
+  //! Auxiliary method to parse transformation persistence flags
+  Standard_EXPORT static Standard_Boolean ParseCorner (Standard_CString theArg,
+                                                       Aspect_TypeOfTriedronPosition& theCorner);
+
 public: //! @name deprecated methods
 
   //! Parses RGB(A) color argument(s) specified within theArgVec[0], theArgVec[1], theArgVec[2] and theArgVec[3].
index 61f45871fad7561c3e1abe9f8122f4ba7aa712a9..7194a1ac562b4d390ef97f84854a77cc1db7fea0 100644 (file)
@@ -108,10 +108,20 @@ void ViewerTest_AutoUpdater::Update()
       if (!myContext.IsNull())
       {
         myContext->UpdateCurrentViewer();
+        if (!myView.IsNull()
+          && myView->IsSubview()
+          && myView->ParentView()->Viewer() != myContext->CurrentViewer())
+        {
+          myView->ParentView()->RedrawImmediate();
+        }
       }
       else if (!myView.IsNull())
       {
         myView->Redraw();
+        if (myView->IsSubview())
+        {
+          myView->ParentView()->RedrawImmediate();
+        }
       }
     }
   }
index 772dd1f98469f583b678773e039b6c189dd1528e..37c192af286d1ff6126e6172995895749c29f38b 100644 (file)
@@ -145,6 +145,30 @@ bool ViewerTest_EventManager::UpdateMouseClick (const Graphic3d_Vec2i& thePoint,
   return AIS_ViewController::UpdateMouseClick (thePoint, theButton, theModifiers, theIsDoubleClick);
 }
 
+//=======================================================================
+//function : UpdateMouseButtons
+//purpose  :
+//=======================================================================
+bool ViewerTest_EventManager::UpdateMouseScroll (const Aspect_ScrollDelta& theDelta)
+{
+  if (!myView.IsNull()
+    && (myView->IsSubview()
+    || !myView->Subviews().IsEmpty()))
+  {
+    Handle(V3d_View) aParent = !myView->IsSubview() ? myView : myView->ParentView();
+    Handle(V3d_View) aPickedView = aParent->PickSubview (theDelta.Point);
+    if (!aPickedView.IsNull()
+      && aPickedView != myView)
+    {
+      // switch input focus to another subview
+      OnSubviewChanged (myCtx, myView, aPickedView);
+      return true;
+    }
+  }
+
+  return AIS_ViewController::UpdateMouseScroll (theDelta);
+}
+
 //=======================================================================
 //function : UpdateMouseButtons
 //purpose  :
@@ -154,6 +178,22 @@ bool ViewerTest_EventManager::UpdateMouseButtons (const Graphic3d_Vec2i& thePoin
                                                   Aspect_VKeyFlags theModifiers,
                                                   bool theIsEmulated)
 {
+  if (!myView.IsNull()
+    && myMousePressed == Aspect_VKeyMouse_NONE
+    && theButtons != Aspect_VKeyMouse_NONE
+    && (myView->IsSubview()
+    || !myView->Subviews().IsEmpty()))
+  {
+    Handle(V3d_View) aParent = !myView->IsSubview() ? myView : myView->ParentView();
+    Handle(V3d_View) aPickedView = aParent->PickSubview (thePoint);
+    if (!aPickedView.IsNull()
+      && aPickedView != myView)
+    {
+      // switch input focus to another subview
+      OnSubviewChanged (myCtx, myView, aPickedView);
+    }
+  }
+
   SetAllowRotation (!ViewerTest_V3dView::IsCurrentViewIn2DMode());
 
   if (theButtons == Aspect_VKeyMouse_LeftButton)
@@ -232,22 +272,45 @@ void ViewerTest_EventManager::handleViewRedraw (const Handle(AIS_InteractiveCont
 //==============================================================================
 void ViewerTest_EventManager::ProcessConfigure (bool theIsResized)
 {
-  if (!myView.IsNull())
+  if (myView.IsNull())
   {
-    if (!theIsResized
-     // track window moves to reverse stereo pair
-     && myView->RenderingParams().StereoMode != Graphic3d_StereoMode_RowInterlaced
-     && myView->RenderingParams().StereoMode != Graphic3d_StereoMode_ColumnInterlaced
-     && myView->RenderingParams().StereoMode != Graphic3d_StereoMode_ChessBoard)
-    {
-      return;
-    }
+    return;
+  }
 
-    myView->Window()->DoResize();
-    myView->MustBeResized();
-    myView->Invalidate();
-    FlushViewEvents (myCtx, myView, true);
+  if (!theIsResized
+    // track window moves to reverse stereo pair
+    && myView->RenderingParams().StereoMode != Graphic3d_StereoMode_RowInterlaced
+    && myView->RenderingParams().StereoMode != Graphic3d_StereoMode_ColumnInterlaced
+    && myView->RenderingParams().StereoMode != Graphic3d_StereoMode_ChessBoard)
+  {
+    return;
+  }
+
+  Handle(V3d_View) aParent = !myView->IsSubview()
+                            ? myView
+                            : myView->ParentView();
+  aParent->Window()->DoResize();
+  aParent->MustBeResized();
+  aParent->Invalidate();
+  for (const Handle(V3d_View)& aChildIter : aParent->Subviews())
+  {
+    aChildIter->Window()->DoResize();
+    aChildIter->MustBeResized();
+    aChildIter->Invalidate();
   }
+
+  FlushViewEvents (myCtx, myView, true);
+}
+
+//==============================================================================
+//function : OnSubviewChanged
+//purpose  :
+//==============================================================================
+void ViewerTest_EventManager::OnSubviewChanged (const Handle(AIS_InteractiveContext)& ,
+                                                const Handle(V3d_View)& ,
+                                                const Handle(V3d_View)& theNewView)
+{
+  ViewerTest::ActivateView (theNewView, false);
 }
 
 //==============================================================================
index 0bfbe32727eb46824b01840b6df9112435e01f31..39398a23c5293a543600af1778487798afd910a9 100644 (file)
@@ -79,6 +79,9 @@ public:
     myPickPntArgVec[2] = theArgZ;
   }
 
+  //! Update mouse scroll event.
+  Standard_EXPORT virtual bool UpdateMouseScroll (const Aspect_ScrollDelta& theDelta) Standard_OVERRIDE;
+
   //! Handle mouse button click event.
   Standard_EXPORT virtual bool UpdateMouseClick (const Graphic3d_Vec2i& thePoint,
                                                   Aspect_VKeyMouse theButton,
@@ -116,6 +119,12 @@ public:
   //! Handle KeyPress event.
   Standard_EXPORT void ProcessKeyPress (Aspect_VKey theKey);
 
+  //! Callback called on Selection of another (sub)view.
+  //! This method is expected to be called from rendering thread.
+  Standard_EXPORT virtual void OnSubviewChanged (const Handle(AIS_InteractiveContext)& theCtx,
+                                                 const Handle(V3d_View)& theOldView,
+                                                 const Handle(V3d_View)& theNewView) Standard_OVERRIDE;
+
 protected:
 
   //! Register hot-keys for specified Action.
index d3ff434e149bcad49793ca32a3545bd5383fdbf1..1e4b15f9d05abd52fd7be6bb04221143415cab70 100644 (file)
@@ -932,7 +932,10 @@ static Standard_Integer VListColors (Draw_Interpretor& theDI,
   Handle(V3d_View) aView;
   if (!aDumpFile.IsEmpty())
   {
-    ViewerTest::ViewerInit (0, 0, anImgParams.Width, anImgParams.Height, "TmpDriver/TmpViewer/TmpView");
+    ViewerTest_VinitParams aParams;
+    aParams.Size.SetValues ((float )anImgParams.Width, (float)anImgParams.Height);
+    aParams.ViewName = "TmpDriver/TmpViewer/TmpView";
+    ViewerTest::ViewerInit (aParams);
     aView = ViewerTest::CurrentView();
     aView->SetImmediateUpdate (false);
     aView->SetBgGradientStyle (Aspect_GradientFillMethod_None, false);
index e660e85026f1efb3a440bfe36b82e7a93f7c525e..29f6e79c3fa99be1fab542e55dffca0654280d1f 100644 (file)
@@ -477,28 +477,23 @@ TCollection_AsciiString ViewerTest::GetCurrentViewName ()
 //purpose  : Create the window viewer and initialize all the global variable
 //==============================================================================
 
-TCollection_AsciiString ViewerTest::ViewerInit (const Standard_Integer thePxLeft,
-                                                const Standard_Integer thePxTop,
-                                                const Standard_Integer thePxWidth,
-                                                const Standard_Integer thePxHeight,
-                                                const TCollection_AsciiString& theViewName,
-                                                const TCollection_AsciiString& theDisplayName,
-                                                const Handle(V3d_View)& theViewToClone,
-                                                const Standard_Boolean theIsVirtual)
+TCollection_AsciiString ViewerTest::ViewerInit (const ViewerTest_VinitParams& theParams)
 {
   // Default position and dimension of the viewer window.
   // Note that left top corner is set to be sufficiently small to have
   // window fit in the small screens (actual for remote desktops, see #23003).
   // The position corresponds to the window's client area, thus some
   // gap is added for window frame to be visible.
-  Standard_Integer aPxLeft  = 20,  aPxTop    = 40;
-  Standard_Integer aPxWidth = 409, aPxHeight = 409;
+  Graphic3d_Vec2d aPxTopLeft (20, 40);
+  Graphic3d_Vec2d aPxSize (409, 409);
   Standard_Boolean isDefViewSize = Standard_True;
   Standard_Boolean toCreateViewer = Standard_False;
-  const Standard_Boolean isVirtual = Draw_VirtualWindows || theIsVirtual;
-  if (!theViewToClone.IsNull())
+  const Standard_Boolean isVirtual = Draw_VirtualWindows || theParams.IsVirtual;
+  if (!theParams.ViewToClone.IsNull())
   {
-    theViewToClone->Window()->Size (aPxWidth, aPxHeight);
+    Graphic3d_Vec2i aCloneSize;
+    theParams.ViewToClone->Window()->Size (aCloneSize.x(), aCloneSize.y());
+    aPxSize = Graphic3d_Vec2d (aCloneSize);
     isDefViewSize = Standard_False;
   #if !defined(__EMSCRIPTEN__)
     (void )isDefViewSize;
@@ -522,40 +517,21 @@ TCollection_AsciiString ViewerTest::ViewerInit (const Standard_Integer thePxLeft
   }
 
   Handle(Graphic3d_GraphicDriver) aGraphicDriver;
-  ViewerTest_Names aViewNames(theViewName);
+  ViewerTest_Names aViewNames (theParams.ViewName);
   if (ViewerTest_myViews.IsBound1 (aViewNames.GetViewName()))
   {
     aViewNames.SetViewName (aViewNames.GetViewerName() + "/" + CreateName<Handle(V3d_View)>(ViewerTest_myViews, "View"));
   }
 
-  if (thePxLeft != 0)
-  {
-    aPxLeft = thePxLeft;
-  }
-  if (thePxTop != 0)
-  {
-    aPxTop = thePxTop;
-  }
-  if (thePxWidth != 0)
-  {
-    isDefViewSize = Standard_False;
-    aPxWidth = thePxWidth;
-  }
-  if (thePxHeight != 0)
-  {
-    isDefViewSize = Standard_False;
-    aPxHeight = thePxHeight;
-  }
-
   // Get graphic driver (create it or get from another view)
   const bool isNewDriver = !ViewerTest_myDrivers.IsBound1 (aViewNames.GetDriverName());
   if (isNewDriver)
   {
     // Get connection string
   #if defined(HAVE_XLIB)
-    if (!theDisplayName.IsEmpty())
+    if (!theParams.DisplayName.IsEmpty())
     {
-      SetDisplayConnection (new Aspect_DisplayConnection (theDisplayName));
+      SetDisplayConnection (new Aspect_DisplayConnection (theParams.DisplayName));
     }
     else
     {
@@ -569,7 +545,6 @@ TCollection_AsciiString ViewerTest::ViewerInit (const Standard_Integer thePxLeft
       SetDisplayConnection (new Aspect_DisplayConnection (aDispX));
     }
   #else
-    (void)theDisplayName; // avoid warning on unused argument
     SetDisplayConnection (new Aspect_DisplayConnection ());
   #endif
 
@@ -588,53 +563,89 @@ TCollection_AsciiString ViewerTest::ViewerInit (const Standard_Integer thePxLeft
     aGraphicDriver = ViewerTest_myDrivers.Find1 (aViewNames.GetDriverName());
   }
 
-  //Dispose the window if input parameters are default
-  if (!ViewerTest_myViews.IsEmpty() && thePxLeft == 0 && thePxTop == 0)
-  {
-    Standard_Integer aTop = 0,
-                     aLeft = 0,
-                     aRight = 0,
-                     aBottom = 0,
-                     aScreenWidth = 0,
-                     aScreenHeight = 0;
-
-    // Get screen resolution
+  // Get screen resolution
+  Graphic3d_Vec2i aScreenSize;
 #if defined(_WIN32)
-    RECT aWindowSize;
-    GetClientRect(GetDesktopWindow(), &aWindowSize);
-    aScreenHeight = aWindowSize.bottom;
-    aScreenWidth = aWindowSize.right;
+  RECT aWindowSize;
+  GetClientRect(GetDesktopWindow(), &aWindowSize);
+  aScreenSize.SetValues (aWindowSize.right, aWindowSize.bottom);
 #elif defined(HAVE_XLIB)
-    ::Display* aDispX = (::Display* )GetDisplayConnection()->GetDisplayAspect();
-    Screen* aScreen = DefaultScreenOfDisplay(aDispX);
-    aScreenWidth  = WidthOfScreen(aScreen);
-    aScreenHeight = HeightOfScreen(aScreen);
+  ::Display* aDispX = (::Display* )GetDisplayConnection()->GetDisplayAspect();
+  Screen* aScreen = DefaultScreenOfDisplay(aDispX);
+  aScreenSize.x() = WidthOfScreen(aScreen);
+  aScreenSize.y() = HeightOfScreen(aScreen);
 #elif defined(__APPLE__)
-    GetCocoaScreenResolution (aScreenWidth, aScreenHeight);
+  GetCocoaScreenResolution (aScreenSize.x(), aScreenSize.y());
 #else
-    // not implemented
+  // not implemented
 #endif
 
-    TCollection_AsciiString anOverlappedViewId("");
+  if (!theParams.ParentView.IsNull())
+  {
+    aPxTopLeft.SetValues (0, 0);
+  }
+  if (theParams.Offset.x() != 0)
+  {
+    aPxTopLeft.x() = theParams.Offset.x();
+  }
+  if (theParams.Offset.y() != 0)
+  {
+    aPxTopLeft.y() = theParams.Offset.y();
+  }
+  if (theParams.Size.x() != 0)
+  {
+    isDefViewSize = Standard_False;
+    aPxSize.x() = theParams.Size.x();
+    if (aPxSize.x() <= 1.0
+     && aScreenSize.x() > 0
+     && theParams.ParentView.IsNull())
+    {
+      aPxSize.x() = aPxSize.x() * double(aScreenSize.x());
+    }
+  }
+  if (theParams.Size.y() != 0)
+  {
+    isDefViewSize = Standard_False;
+    aPxSize.y() = theParams.Size.y();
+    if (aPxSize.y() <= 1.0
+     && aScreenSize.y() > 0
+     && theParams.ParentView.IsNull())
+    {
+      aPxSize.y() = aPxSize.y() * double(aScreenSize.y());
+    }
+  }
 
-    while (IsWindowOverlapped (aPxLeft, aPxTop, aPxLeft + aPxWidth, aPxTop + aPxHeight, anOverlappedViewId))
+  //Dispose the window if input parameters are default
+  if (!ViewerTest_myViews.IsEmpty()
+    && theParams.ParentView.IsNull()
+    && theParams.Offset.x() == 0
+    && theParams.Offset.y() == 0)
+  {
+    Standard_Integer aTop = 0, aLeft = 0, aRight = 0, aBottom = 0;
+    TCollection_AsciiString anOverlappedViewId("");
+    while (IsWindowOverlapped ((int )aPxTopLeft.x(), (int )aPxTopLeft.y(),
+                               (int )aPxTopLeft.x() + (int )aPxSize.x(),
+                               (int )aPxTopLeft.y() + (int )aPxSize.y(), anOverlappedViewId))
     {
       ViewerTest_myViews.Find1(anOverlappedViewId)->Window()->Position (aLeft, aTop, aRight, aBottom);
 
-      if (IsWindowOverlapped (aRight + 20, aPxTop, aRight + 20 + aPxWidth, aPxTop + aPxHeight, anOverlappedViewId)
-        && aRight + 2*aPxWidth + 40 > aScreenWidth)
+      if (IsWindowOverlapped (aRight + 20, (int )aPxTopLeft.y(), aRight + 20 + (int )aPxSize.x(),
+                              (int )aPxTopLeft.y() + (int )aPxSize.y(), anOverlappedViewId)
+        && aRight + 2 * aPxSize.x() + 40 > aScreenSize.x())
       {
-        if (aBottom + aPxHeight + 40 > aScreenHeight)
+        if (aBottom + aPxSize.y() + 40 > aScreenSize.y())
         {
-          aPxLeft = 20;
-          aPxTop = 40;
+          aPxTopLeft.x() = 20;
+          aPxTopLeft.y() = 40;
           break;
         }
-        aPxLeft = 20;
-        aPxTop = aBottom + 40;
+        aPxTopLeft.x() = 20;
+        aPxTopLeft.y() = aBottom + 40;
       }
       else
-        aPxLeft = aRight + 20;
+      {
+        aPxTopLeft.x() = aRight + 20;
+      }
     }
   }
 
@@ -688,61 +699,76 @@ TCollection_AsciiString ViewerTest::ViewerInit (const Standard_Integer thePxLeft
   }
 
   // Create window
-#if defined(_WIN32)
-  VT_GetWindow() = new WNT_Window (aTitle.ToCString(), WClass(),
-                                   isVirtual ? WS_POPUP : WS_OVERLAPPEDWINDOW,
-                                    aPxLeft, aPxTop,
-                                    aPxWidth, aPxHeight,
-                                    Quantity_NOC_BLACK);
-  VT_GetWindow()->RegisterRawInputDevices (WNT_Window::RawInputMask_SpaceMouse);
-#elif defined(HAVE_XLIB)
-  VT_GetWindow() = new Xw_Window (aGraphicDriver->GetDisplayConnection(),
-                                  aTitle.ToCString(),
-                                  aPxLeft, aPxTop,
-                                  aPxWidth, aPxHeight);
-#elif defined(__APPLE__)
-  VT_GetWindow() = new Cocoa_Window (aTitle.ToCString(),
-                                     aPxLeft, aPxTop,
-                                     aPxWidth, aPxHeight);
-  ViewerTest_SetCocoaEventManagerView (VT_GetWindow());
-#elif defined(__EMSCRIPTEN__)
-  // current EGL implementation in Emscripten supports only one global WebGL canvas returned by Module.canvas property;
-  // the code should be revised for handling multiple canvas elements (which is technically also possible)
-  TCollection_AsciiString aCanvasId = getModuleCanvasId();
-  if (!aCanvasId.IsEmpty())
+  if (!theParams.ParentView.IsNull())
   {
-    aCanvasId = TCollection_AsciiString("#") + aCanvasId;
+    VT_GetWindow() = Handle(ViewerTest_Window)::DownCast (theParams.ParentView->Window());
   }
-
-  VT_GetWindow() = new Wasm_Window (aCanvasId);
-  Graphic3d_Vec2i aRealSize;
-  VT_GetWindow()->Size (aRealSize.x(), aRealSize.y());
-  if (!isDefViewSize || (aRealSize.x() <= 0 && aRealSize.y() <= 0))
+  else
   {
-    // Wasm_Window wraps an existing HTML element without creating a new one.
-    // Keep size defined on a web page instead of defaulting to 409x409 (as in case of other platform),
-    // but resize canvas if vinit has been called with explicitly specified dimensions.
-    VT_GetWindow()->SetSizeLogical (Graphic3d_Vec2d (aPxWidth, aPxHeight));
+  #if defined(_WIN32)
+    VT_GetWindow() = new WNT_Window (aTitle.ToCString(), WClass(),
+                                     isVirtual ? WS_POPUP : WS_OVERLAPPEDWINDOW,
+                                     (int )aPxTopLeft.x(), (int )aPxTopLeft.y(),
+                                     (int )aPxSize.x(), (int )aPxSize.y(),
+                                     Quantity_NOC_BLACK);
+    VT_GetWindow()->RegisterRawInputDevices (WNT_Window::RawInputMask_SpaceMouse);
+  #elif defined(HAVE_XLIB)
+    VT_GetWindow() = new Xw_Window (aGraphicDriver->GetDisplayConnection(),
+                                    aTitle.ToCString(),
+                                    (int )aPxTopLeft.x(), (int )aPxTopLeft.y(),
+                                    (int )aPxSize.x(), (int )aPxSize.y());
+  #elif defined(__APPLE__)
+    VT_GetWindow() = new Cocoa_Window (aTitle.ToCString(),
+                                       (int )aPxTopLeft.x(), (int )aPxTopLeft.y(),
+                                       (int )aPxSize.x(), (int )aPxSize.y());
+    ViewerTest_SetCocoaEventManagerView (VT_GetWindow());
+  #elif defined(__EMSCRIPTEN__)
+    // current EGL implementation in Emscripten supports only one global WebGL canvas returned by Module.canvas property;
+    // the code should be revised for handling multiple canvas elements (which is technically also possible)
+    TCollection_AsciiString aCanvasId = getModuleCanvasId();
+    if (!aCanvasId.IsEmpty())
+    {
+      aCanvasId = TCollection_AsciiString("#") + aCanvasId;
+    }
+
+    VT_GetWindow() = new Wasm_Window (aCanvasId);
+    Graphic3d_Vec2i aRealSize;
+    VT_GetWindow()->Size (aRealSize.x(), aRealSize.y());
+    if (!isDefViewSize || (aRealSize.x() <= 0 && aRealSize.y() <= 0))
+    {
+      // Wasm_Window wraps an existing HTML element without creating a new one.
+      // Keep size defined on a web page instead of defaulting to 409x409 (as in case of other platform),
+      // but resize canvas if vinit has been called with explicitly specified dimensions.
+      VT_GetWindow()->SetSizeLogical (Graphic3d_Vec2d (aPxSize));
+    }
+  #else
+    // not implemented
+    VT_GetWindow() = new Aspect_NeutralWindow();
+    VT_GetWindow()->SetSize ((int )aPxSize.x(), (int )aPxSize.y());
+  #endif
+    VT_GetWindow()->SetVirtual (isVirtual);
   }
-#else
-  // not implemented
-  VT_GetWindow() = new Aspect_NeutralWindow();
-  VT_GetWindow()->SetSize (aPxWidth, aPxHeight);
-#endif
-  VT_GetWindow()->SetVirtual (isVirtual);
 
   // View setup
   Handle(V3d_View) aView;
-  if (!theViewToClone.IsNull())
+  if (!theParams.ViewToClone.IsNull())
   {
-    aView = new ViewerTest_V3dView (a3DViewer, theViewToClone);
+    aView = new ViewerTest_V3dView (a3DViewer, theParams.ViewToClone);
   }
   else
   {
     aView = new ViewerTest_V3dView (a3DViewer, a3DViewer->DefaultTypeOfView());
   }
 
-  aView->SetWindow (VT_GetWindow());
+  aView->View()->SetSubviewComposer (theParams.IsComposer);
+  if (!theParams.ParentView.IsNull())
+  {
+    aView->SetWindow (theParams.ParentView, aPxSize, theParams.Corner, aPxTopLeft, theParams.SubviewMargins);
+  }
+  else
+  {
+    aView->SetWindow (VT_GetWindow());
+  }
   ViewerTest::GetAISContext()->RedrawImmediate (a3DViewer);
 
   ViewerTest::CurrentView(aView);
@@ -952,10 +978,7 @@ static int VDriver (Draw_Interpretor& theDi, Standard_Integer theArgsNb, const c
 //==============================================================================
 static int VInit (Draw_Interpretor& theDi, Standard_Integer theArgsNb, const char** theArgVec)
 {
-  TCollection_AsciiString aViewName, aDisplayName;
-  Standard_Integer aPxLeft = 0, aPxTop = 0, aPxWidth = 0, aPxHeight = 0;
-  Standard_Boolean isVirtual = false;
-  Handle(V3d_View) aCopyFrom;
+  ViewerTest_VinitParams aParams;
   TCollection_AsciiString aName, aValue;
   int is2dMode = -1, aDpiAware = -1;
   for (Standard_Integer anArgIt = 1; anArgIt < theArgsNb; ++anArgIt)
@@ -966,91 +989,128 @@ static int VInit (Draw_Interpretor& theDi, Standard_Integer theArgsNb, const cha
     if (anArgIt + 1 < theArgsNb
      && anArgCase == "-name")
     {
-      aViewName = theArgVec[++anArgIt];
+      aParams.ViewName = theArgVec[++anArgIt];
     }
     else if (anArgIt + 1 < theArgsNb
           && (anArgCase == "-left"
-           || anArgCase == "-l"))
+           || anArgCase == "-l")
+           && Draw::ParseReal (theArgVec[anArgIt + 1], aParams.Offset.x()))
     {
-      aPxLeft = Draw::Atoi (theArgVec[++anArgIt]);
+      ++anArgIt;
     }
     else if (anArgIt + 1 < theArgsNb
           && (anArgCase == "-top"
-           || anArgCase == "-t"))
+           || anArgCase == "-t")
+           && Draw::ParseReal (theArgVec[anArgIt + 1], aParams.Offset.y()))
     {
-      aPxTop = Draw::Atoi (theArgVec[++anArgIt]);
+      ++anArgIt;
     }
     else if (anArgIt + 1 < theArgsNb
           && (anArgCase == "-width"
-           || anArgCase == "-w"))
+           || anArgCase == "-w")
+           && Draw::ParseReal (theArgVec[anArgIt + 1], aParams.Size.x()))
     {
-      aPxWidth = Draw::Atoi (theArgVec[++anArgIt]);
+      ++anArgIt;
     }
     else if (anArgIt + 1 < theArgsNb
           && (anArgCase == "-height"
-           || anArgCase == "-h"))
+           || anArgCase == "-h")
+           && Draw::ParseReal (theArgVec[anArgIt + 1], aParams.Size.y()))
     {
-      aPxHeight = Draw::Atoi (theArgVec[++anArgIt]);
+      ++anArgIt;
+    }
+    else if (anArgIt + 1 < theArgsNb
+          && (anArgCase == "-pos"
+           || anArgCase == "-position"
+           || anArgCase == "-corner")
+          && ViewerTest::ParseCorner (theArgVec[anArgIt + 1], aParams.Corner))
+    {
+      ++anArgIt;
+    }
+    else if (anArgIt + 2 < theArgsNb
+          && anArgCase == "-margins"
+          && Draw::ParseInteger (theArgVec[anArgIt + 1], aParams.SubviewMargins.x())
+          && Draw::ParseInteger (theArgVec[anArgIt + 2], aParams.SubviewMargins.y()))
+    {
+      anArgIt += 2;
     }
     else if (anArgCase == "-virtual"
           || anArgCase == "-offscreen")
     {
-      isVirtual = true;
-      if (anArgIt + 1 < theArgsNb
-       && Draw::ParseOnOff (theArgVec[anArgIt + 1], isVirtual))
-      {
-        ++anArgIt;
-      }
+      aParams.IsVirtual = Draw::ParseOnOffIterator (theArgsNb, theArgVec, anArgIt);;
+    }
+    else if (anArgCase == "-composer")
+    {
+      aParams.IsComposer = Draw::ParseOnOffIterator (theArgsNb, theArgVec, anArgIt);
     }
     else if (anArgCase == "-exitonclose")
     {
-      ViewerTest_EventManager::ToExitOnCloseView() = true;
-      if (anArgIt + 1 < theArgsNb
-       && Draw::ParseOnOff (theArgVec[anArgIt + 1], ViewerTest_EventManager::ToExitOnCloseView()))
-      {
-        ++anArgIt;
-      }
+      ViewerTest_EventManager::ToExitOnCloseView() = Draw::ParseOnOffIterator (theArgsNb, theArgVec, anArgIt);;
     }
     else if (anArgCase == "-closeonescape"
           || anArgCase == "-closeonesc")
     {
-      ViewerTest_EventManager::ToCloseViewOnEscape() = true;
-      if (anArgIt + 1 < theArgsNb
-       && Draw::ParseOnOff (theArgVec[anArgIt + 1], ViewerTest_EventManager::ToCloseViewOnEscape()))
-      {
-        ++anArgIt;
-      }
+      ViewerTest_EventManager::ToCloseViewOnEscape() = Draw::ParseOnOffIterator (theArgsNb, theArgVec, anArgIt);;
     }
     else if (anArgCase == "-2d_mode"
           || anArgCase == "-2dmode"
           || anArgCase == "-2d")
     {
-      bool toEnable = true;
-      if (anArgIt + 1 < theArgsNb
-       && Draw::ParseOnOff (theArgVec[anArgIt + 1], toEnable))
-      {
-        ++anArgIt;
-      }
+      bool toEnable = Draw::ParseOnOffIterator (theArgsNb, theArgVec, anArgIt);;
       is2dMode = toEnable ? 1 : 0;
     }
     else if (anArgIt + 1 < theArgsNb
           && (anArgCase == "-disp"
            || anArgCase == "-display"))
     {
-      aDisplayName = theArgVec[++anArgIt];
+      aParams.DisplayName = theArgVec[++anArgIt];
     }
     else if (anArgCase == "-dpiaware")
     {
       aDpiAware = Draw::ParseOnOffIterator (theArgsNb, theArgVec, anArgIt) ? 1 : 0;
     }
     else if (!ViewerTest::CurrentView().IsNull()
-          &&  aCopyFrom.IsNull()
+          &&  aParams.ViewToClone.IsNull()
           && (anArgCase == "-copy"
            || anArgCase == "-clone"
            || anArgCase == "-cloneactive"
            || anArgCase == "-cloneactiveview"))
     {
-      aCopyFrom = ViewerTest::CurrentView();
+      aParams.ViewToClone = ViewerTest::CurrentView();
+    }
+    else if (!ViewerTest::CurrentView().IsNull()
+           && aParams.ParentView.IsNull()
+           && anArgCase == "-subview")
+    {
+      aParams.ParentView = ViewerTest::CurrentView();
+      if (aParams.ParentView.IsNull())
+      {
+        Message::SendFail() << "Syntax error: cannot create of subview without parent";
+        return 1;
+      }
+      if (aParams.ParentView->IsSubview())
+      {
+        aParams.ParentView = aParams.ParentView->ParentView();
+      }
+    }
+    else if (!ViewerTest::CurrentView().IsNull()
+           && aParams.ParentView.IsNull()
+           && anArgCase == "-parent"
+           && anArgIt + 1 < theArgsNb)
+    {
+      TCollection_AsciiString aParentStr (theArgVec[++anArgIt]);
+      ViewerTest_Names aViewNames (aParentStr);
+      if (!ViewerTest_myViews.IsBound1 (aViewNames.GetViewName()))
+      {
+        Message::SendFail() << "Syntax error: parent view '" << aParentStr << "' not found";
+        return 1;
+      }
+
+      aParams.ParentView = ViewerTest_myViews.Find1(aViewNames.GetViewName());
+      if (aParams.ParentView->IsSubview())
+      {
+        aParams.ParentView = aParams.ParentView->ParentView();
+      }
     }
     // old syntax
     else if (ViewerTest::SplitParameter (anArg, aName, aValue))
@@ -1058,32 +1118,32 @@ static int VInit (Draw_Interpretor& theDi, Standard_Integer theArgsNb, const cha
       aName.LowerCase();
       if (aName == "name")
       {
-        aViewName = aValue;
+        aParams.ViewName = aValue;
       }
       else if (aName == "l"
             || aName == "left")
       {
-        aPxLeft = aValue.IntegerValue();
+        aParams.Offset.x() = (float)aValue.RealValue();
       }
       else if (aName == "t"
             || aName == "top")
       {
-        aPxTop = aValue.IntegerValue();
+        aParams.Offset.y() = (float)aValue.RealValue();
       }
       else if (aName == "disp"
             || aName == "display")
       {
-        aDisplayName = aValue;
+        aParams.DisplayName = aValue;
       }
       else if (aName == "w"
             || aName == "width")
       {
-        aPxWidth = aValue.IntegerValue();
+        aParams.Size.x() = (float )aValue.RealValue();
       }
       else if (aName == "h"
             || aName == "height")
       {
-        aPxHeight = aValue.IntegerValue();
+        aParams.Size.y() = (float)aValue.RealValue();
       }
       else
       {
@@ -1091,9 +1151,9 @@ static int VInit (Draw_Interpretor& theDi, Standard_Integer theArgsNb, const cha
         return 1;
       }
     }
-    else if (aViewName.IsEmpty())
+    else if (aParams.ViewName.IsEmpty())
     {
-      aViewName = anArg;
+      aParams.ViewName = anArg;
     }
     else
     {
@@ -1134,15 +1194,15 @@ static int VInit (Draw_Interpretor& theDi, Standard_Integer theArgsNb, const cha
 #else
   (void )aDpiAware;
 #if !defined(HAVE_XLIB)
-  if (!aDisplayName.IsEmpty())
+  if (!aParams.DisplayName.IsEmpty())
   {
-    aDisplayName.Clear();
+    aParams.DisplayName.Clear();
     Message::SendWarning() << "Warning: display parameter will be ignored.\n";
   }
 #endif
 #endif
 
-  ViewerTest_Names aViewNames (aViewName);
+  ViewerTest_Names aViewNames (aParams.ViewName);
   if (ViewerTest_myViews.IsBound1 (aViewNames.GetViewName()))
   {
     TCollection_AsciiString aCommand = TCollection_AsciiString ("vactivate ") + aViewNames.GetViewName();
@@ -1154,8 +1214,7 @@ static int VInit (Draw_Interpretor& theDi, Standard_Integer theArgsNb, const cha
     return 0;
   }
 
-  TCollection_AsciiString aViewId = ViewerTest::ViewerInit (aPxLeft, aPxTop, aPxWidth, aPxHeight,
-                                                            aViewName, aDisplayName, aCopyFrom, isVirtual);
+  TCollection_AsciiString aViewId = ViewerTest::ViewerInit (aParams);
   if (is2dMode != -1)
   {
     ViewerTest_V3dView::SetCurrentView2DMode (is2dMode == 1);
@@ -1422,29 +1481,59 @@ static TCollection_AsciiString FindViewIdByWindowHandle (Aspect_Drawable theWind
 void ActivateView (const TCollection_AsciiString& theViewName,
                    Standard_Boolean theToUpdate = Standard_True)
 {
-  const Handle(V3d_View) aView = ViewerTest_myViews.Find1(theViewName);
-  if (aView.IsNull())
+  if (const Handle(V3d_View) aView = ViewerTest_myViews.Find1(theViewName))
+  {
+    ViewerTest::ActivateView (aView, theToUpdate);
+  }
+}
+
+//==============================================================================
+//function : ActivateView
+//purpose  :
+//==============================================================================
+void ViewerTest::ActivateView (const Handle(V3d_View)& theView,
+                               Standard_Boolean theToUpdate)
+{
+  Handle(V3d_View) aView = theView;
+  const TCollection_AsciiString* aViewName = ViewerTest_myViews.Seek2 (aView);
+  if (aViewName == nullptr)
   {
     return;
   }
 
   Handle(AIS_InteractiveContext) anAISContext = FindContextByView(aView);
-  if (!anAISContext.IsNull())
+  if (anAISContext.IsNull())
+  {
+    return;
+  }
+
+  if (const Handle(V3d_View)& aCurrentView = ViewerTest::CurrentView())
   {
-    if (const Handle(V3d_View)& aCurrentView = ViewerTest::CurrentView())
+    if (!aCurrentView->Window().IsNull())
     {
       aCurrentView->Window()->SetTitle (TCollection_AsciiString ("3D View - ") + ViewerTest_myViews.Find2 (aCurrentView));
     }
+  }
 
-    ViewerTest::CurrentView (aView);
-    ViewerTest::SetAISContext (anAISContext);
-    aView->Window()->SetTitle (TCollection_AsciiString("3D View - ") + theViewName + "(*)");
-    VT_GetWindow() = Handle(ViewerTest_Window)::DownCast(ViewerTest::CurrentView()->Window());
-    SetDisplayConnection(ViewerTest::CurrentView()->Viewer()->Driver()->GetDisplayConnection());
-    if (theToUpdate)
-    {
-      ViewerTest::CurrentView()->Redraw();
-    }
+  ViewerTest::CurrentView (aView);
+  ViewerTest::SetAISContext (anAISContext);
+  if (aView->IsSubview())
+  {
+    aView->ParentView()->Window()->SetTitle (TCollection_AsciiString("3D View - ") + *aViewName + "(*)");
+    VT_GetWindow() = Handle(ViewerTest_Window)::DownCast(aView->View()->ParentView()->Window());
+  }
+  else
+  {
+    VT_GetWindow() = Handle(ViewerTest_Window)::DownCast(aView->Window());
+  }
+  if (!VT_GetWindow().IsNull())
+  {
+    VT_GetWindow()->SetTitle (TCollection_AsciiString("3D View - ") + *aViewName + "(*)");
+  }
+  SetDisplayConnection(aView->Viewer()->Driver()->GetDisplayConnection());
+  if (theToUpdate)
+  {
+    aView->Redraw();
   }
 }
 
@@ -1476,22 +1565,33 @@ void ViewerTest::RemoveView (const TCollection_AsciiString& theViewName, const S
     return;
   }
 
+  Handle(V3d_View) aView = ViewerTest_myViews.Find1(theViewName);
+  Handle(AIS_InteractiveContext) aCurrentContext = FindContextByView(aView);
+  ViewerTest_ContinuousRedrawer& aRedrawer = ViewerTest_ContinuousRedrawer::Instance();
+  aRedrawer.Stop (aView);
+  if (!aView->Subviews().IsEmpty())
+  {
+    NCollection_Sequence<Handle(V3d_View)> aSubviews = aView->Subviews();
+    for (const Handle(V3d_View)& aSubviewIter : aSubviews)
+    {
+      RemoveView (aSubviewIter, isContextRemoved);
+    }
+  }
+
   // Activate another view if it's active now
   if (ViewerTest_myViews.Find1(theViewName) == ViewerTest::CurrentView())
   {
     if (ViewerTest_myViews.Extent() > 1)
     {
-      TCollection_AsciiString aNewViewName;
       for (NCollection_DoubleMap <TCollection_AsciiString, Handle(V3d_View)>::Iterator anIter (ViewerTest_myViews);
            anIter.More(); anIter.Next())
       {
         if (anIter.Key1() != theViewName)
         {
-          aNewViewName = anIter.Key1();
+          ActivateView (anIter.Value(), true);
           break;
         }
       }
-      ActivateView (aNewViewName);
     }
     else
     {
@@ -1506,14 +1606,11 @@ void ViewerTest::RemoveView (const TCollection_AsciiString& theViewName, const S
   }
 
   // Delete view
-  Handle(V3d_View) aView = ViewerTest_myViews.Find1(theViewName);
-  Handle(AIS_InteractiveContext) aCurrentContext = FindContextByView(aView);
-  ViewerTest_ContinuousRedrawer& aRedrawer = ViewerTest_ContinuousRedrawer::Instance();
-  aRedrawer.Stop (aView);
-
-  // Remove view resources
   ViewerTest_myViews.UnBind1(theViewName);
-  aView->Window()->Unmap();
+  if (!aView->Window().IsNull())
+  {
+    aView->Window()->Unmap();
+  }
   aView->Remove();
 
 #if defined(HAVE_XLIB)
@@ -3196,47 +3293,13 @@ static int VZBuffTrihedron (Draw_Interpretor& /*theDI*/,
     {
       if (++anArgIter >= theArgNb)
       {
-        Message::SendFail() << "Error: wrong syntax at '" << anArg << "'";
+        Message::SendFail() << "Syntax error at '" << anArg << "'";
         return 1;
       }
 
-      TCollection_AsciiString aPosName (theArgVec[anArgIter]);
-      aPosName.LowerCase();
-      if (aPosName == "center")
-      {
-        aPosition = Aspect_TOTP_CENTER;
-      }
-      else if (aPosName == "left_lower"
-            || aPosName == "lower_left"
-            || aPosName == "leftlower"
-            || aPosName == "lowerleft")
-      {
-        aPosition = Aspect_TOTP_LEFT_LOWER;
-      }
-      else if (aPosName == "left_upper"
-            || aPosName == "upper_left"
-            || aPosName == "leftupper"
-            || aPosName == "upperleft")
-      {
-        aPosition = Aspect_TOTP_LEFT_UPPER;
-      }
-      else if (aPosName == "right_lower"
-            || aPosName == "lower_right"
-            || aPosName == "rightlower"
-            || aPosName == "lowerright")
-      {
-        aPosition = Aspect_TOTP_RIGHT_LOWER;
-      }
-      else if (aPosName == "right_upper"
-            || aPosName == "upper_right"
-            || aPosName == "rightupper"
-            || aPosName == "upperright")
-      {
-        aPosition = Aspect_TOTP_RIGHT_UPPER;
-      }
-      else
+      if (!ViewerTest::ParseCorner (theArgVec[anArgIter], aPosition))
       {
-        Message::SendFail() << "Error: wrong syntax at '" << anArg << "' - unknown position '" << aPosName << "'";
+        Message::SendFail() << "Syntax error at '" << anArg << "' - unknown position '" << theArgVec[anArgIter] << "'";
         return 1;
       }
     }
@@ -6158,15 +6221,13 @@ static int VDiffImage (Draw_Interpretor& theDI, Standard_Integer theArgNb, const
     theDI.Eval (aCommand.ToCString());
   }
 
-  Standard_Integer aPxLeft = 0;
-  Standard_Integer aPxTop  = 0;
-  Standard_Integer aWinSizeX = int(anImgRef->SizeX() * 2);
-  Standard_Integer aWinSizeY = !aDiff.IsNull() && !aPrsNameDiff.IsEmpty()
-                              ? int(anImgRef->SizeY() * 2)
-                              : int(anImgRef->SizeY());
-  TCollection_AsciiString aDisplayName;
-  TCollection_AsciiString aViewId = ViewerTest::ViewerInit (aPxLeft, aPxTop, aWinSizeX, aWinSizeY,
-                                                            aViewName, aDisplayName);
+  ViewerTest_VinitParams aParams;
+  aParams.ViewName = aViewName;
+  aParams.Size.x() = float(anImgRef->SizeX() * 2);
+  aParams.Size.y() = !aDiff.IsNull() && !aPrsNameDiff.IsEmpty()
+                   ? float(anImgRef->SizeY() * 2)
+                   : float(anImgRef->SizeY());
+  TCollection_AsciiString aViewId = ViewerTest::ViewerInit (aParams);
 
   Standard_Real aRatio = anImgRef->Ratio();
   Standard_Real aSizeX = 1.0;
@@ -13801,8 +13862,9 @@ Makes specified driver active when ActiveName argument is specified.
 
   addCmd ("vinit", VInit, /* [vinit] */ R"(
 vinit [-name viewName] [-left leftPx] [-top topPx] [-width widthPx] [-height heightPx]
-      [-exitOnClose] [-closeOnEscape] [-cloneActive] [-virtual {on|off}=off] [-2d_mode {on|off}=off]
-      [-display displayName] [-dpiAware {on|off}]
+      [-exitOnClose] [-closeOnEscape] [-cloneActive] [-virtual {0|1}]=0 [-2d_mode {0|1}]=0
+      [-display displayName] [-dpiAware {0|1}]=0
+      [-subview] [-parent OtherView] [-composer {0|1}]=0 [-margins DX DY]=0
 Creates new View window with specified name viewName.
 By default the new view is created in the viewer and in graphic driver shared with active view.
  -name {driverName/viewerName/viewName | viewerName/viewName | viewName}
@@ -13817,6 +13879,7 @@ Display name will be used within creation of graphic driver, when specified.
  -exitOnClose when specified, closing the view will exit application.
  -closeOnEscape when specified, view will be closed on pressing Escape.
  -virtual create an offscreen window within interactive session
+ -subview create a subview within another view
  -2d_mode when on, view will not react on rotate scene events
  -dpiAware override dpi aware hint (Windows platform)
 Additional commands for operations with views: vclose, vactivate, vviewlist.
index bb35bd39b51fcc8664e83630d4680c72cd9ab744..083ed649d44f6b5d4c50a08f7b4a5b63ad333ea9 100644 (file)
@@ -585,7 +585,7 @@ static Standard_Integer show (Draw_Interpretor& di, Standard_Integer argc, const
   TCollection_AsciiString   aViewName = TCollection_AsciiString ("Driver1/Document_") + argv[1] + "/View1";
   if (!TPrsStd_AISViewer::Find (aRoot, aDocViewer))
   {
-    ViewerTest::ViewerInit (0, 0, 0, 0, aViewName.ToCString(), "");
+    ViewerTest::ViewerInit (aViewName);
     aDocViewer = TPrsStd_AISViewer::New (aRoot, ViewerTest::GetAISContext());
   }
 
@@ -1322,7 +1322,7 @@ static Standard_Integer testDoc (Draw_Interpretor&,
   aD1->Open(anApp);
   
   TCollection_AsciiString  aViewName ("Driver1/DummyDocument/View1");
-  ViewerTest::ViewerInit (0, 0, 0, 0, aViewName.ToCString(), "");
+  ViewerTest::ViewerInit (aViewName);
   TPrsStd_AISViewer::New (aD1->GetData()->Root(), ViewerTest::GetAISContext());
 
   // get shape tool for shape verification
diff --git a/tests/opengl/data/general/multiview1 b/tests/opengl/data/general/multiview1
new file mode 100644 (file)
index 0000000..a783aec
--- /dev/null
@@ -0,0 +1,34 @@
+puts "========"
+puts "0032886: Visualization, V3d_View - introduce interface for creating a subview"
+puts "V1/RootView + V2/ViewLeft + V3/ViewRight"
+puts "========"
+
+pload MODELING VISUALIZATION
+vinit V1/RootView -width 1024 -height 512 -composer 0
+vbackground GRAY20
+
+vinit V2/ViewLeft -subView -width 0.5 -height 1.0
+vbackground GRAY30
+
+box b 1 2 3
+vdisplay -dispMode 1 b
+vaspects b -faceBoundaryDraw 1
+vfit
+vviewcube vc
+
+vinit V3/ViewRight -parent V1/RootView -width 0.5 -height 1.0 -left 0.5
+vbackground GRAY40
+psphere s 1
+vdisplay -dispMode 1 s
+vaspects s -material SILVER
+vfit
+vzbufftrihedron
+
+vactivate V1/RootView
+vdump $::imagedir/${::casename}.png
+
+vactivate V2/ViewLeft
+vdump $::imagedir/${::casename}_left.png
+
+vactivate V3/ViewRight
+vdump $::imagedir/${::casename}_right.png
diff --git a/tests/opengl/data/general/multiview1ssaa b/tests/opengl/data/general/multiview1ssaa
new file mode 100644 (file)
index 0000000..f6d88d4
--- /dev/null
@@ -0,0 +1,36 @@
+puts "========"
+puts "0032886: Visualization, V3d_View - introduce interface for creating a subview"
+puts "V1/RootView + V2/ViewLeft + V3/ViewRight + SSAA"
+puts "========"
+
+pload MODELING VISUALIZATION
+vinit V1/RootView -width 1024 -height 512 -composer 1
+vbackground GRAY20
+
+vinit V2/ViewLeft -subView -width 0.5 -height 1.0
+vrenderparams -rendScale 2
+vbackground GRAY30
+
+box b 1 2 3
+vdisplay -dispMode 1 b
+vaspects b -faceBoundaryDraw 1
+vfit
+vviewcube vc
+
+vinit V3/ViewRight -parent V1/RootView -width 0.5 -height 1.0 -left 0.5
+vrenderparams -rendScale 2
+vbackground GRAY40
+psphere s 1
+vdisplay -dispMode 1 s
+vaspects s -material SILVER
+vfit
+vzbufftrihedron
+
+vactivate V1/RootView
+vdump $::imagedir/${::casename}.png
+
+vactivate V2/ViewLeft
+vdump $::imagedir/${::casename}_left.png
+
+vactivate V3/ViewRight
+vdump $::imagedir/${::casename}_right.png
diff --git a/tests/opengl/data/general/multiview2 b/tests/opengl/data/general/multiview2
new file mode 100644 (file)
index 0000000..35f819e
--- /dev/null
@@ -0,0 +1,40 @@
+puts "========"
+puts "0032886: Visualization, V3d_View - introduce interface for creating a subview"
+puts "V1/RootView + V2/ViewLeft + V2/ViewRightTop + V2/ViewRightBottom"
+puts "========"
+
+pload MODELING VISUALIZATION
+vinit V1/RootView -width 768 -height 512 -composer 1
+vbackground GRAY20
+
+vinit V2/ViewLeft -parent V1/RootView -width 0.5  -height 1.0
+vbackground GRAY30
+vaxo
+vcamera -persp
+box b 1 2 3
+vdisplay -dispMode 1 b
+vaspects b -faceBoundaryDraw 1
+vfit
+vzbufftrihedron
+
+vinit V2/ViewRightTop -parent V1/RootView -width 0.5 -height 0.5 -corner topRight
+vbackground GRAY40
+vaxo
+vfit
+
+vinit V2/ViewRightBottom -parent V1/RootView -width 0.5 -height 0.5 -corner bottomRight
+vbackground GRAY50
+vaxo
+vfit
+
+vactivate V1/RootView
+vdump $::imagedir/${::casename}.png
+
+vactivate V2/ViewLeft
+vdump $::imagedir/${::casename}_left.png
+
+vactivate V2/ViewRightTop
+vdump $::imagedir/${::casename}_righttop.png
+
+vactivate V2/ViewRightBottom
+vdump $::imagedir/${::casename}_rightbottom.png
diff --git a/tests/opengl/data/general/multiview3 b/tests/opengl/data/general/multiview3
new file mode 100644 (file)
index 0000000..73b3efb
--- /dev/null
@@ -0,0 +1,32 @@
+puts "========"
+puts "0032886: Visualization, V3d_View - introduce interface for creating a subview"
+puts "V1/RootView + V2/View + V2/ThumbView at corner"
+puts "========"
+
+pload MODELING VISUALIZATION
+vinit V1/RootView -width 380 -height 520
+vbackground GRAY20
+
+vinit V2/View -parent V1/RootView -width 1.0  -height 1.0
+vbackground GRAY30
+vaxo
+vcamera -persp
+box b 1 2 3
+vdisplay -dispMode 1 b
+vaspects b -faceBoundaryDraw 1
+vfit
+vzbufftrihedron
+
+vinit V2/ThumbView -parent V1/RootView -width 0.25 -height 0.25 -corner bottomRight -top 10 -left 10
+vbackground GRAY40
+vaxo
+vfit
+
+vactivate V1/RootView
+vdump $::imagedir/${::casename}.png
+
+vactivate V2/View
+vdump $::imagedir/${::casename}_view.png
+
+vactivate V2/ThumbView
+vdump $::imagedir/${::casename}_thumb.png
diff --git a/tests/opengl/data/general/multiview4 b/tests/opengl/data/general/multiview4
new file mode 100644 (file)
index 0000000..15c4496
--- /dev/null
@@ -0,0 +1,26 @@
+puts "========"
+puts "0032886: Visualization, V3d_View - introduce interface for creating a subview"
+puts "V1/RootView + V1/ThumbView at corner"
+puts "========"
+
+pload MODELING VISUALIZATION
+vinit V1/RootView -width 380 -height 520 -composer 0
+vbackground GRAY20
+vaxo
+vcamera -persp
+box b 1 2 3
+vdisplay -dispMode 1 b
+vaspects b -faceBoundaryDraw 1
+vfit
+vzbufftrihedron
+
+vinit V1/ThumbView -parent V1/RootView -width 0.25 -height 0.25 -corner bottomRight -top 10 -left 10
+vbackground GRAY40
+vaxo
+vfit
+
+vactivate V1/RootView
+vdump $::imagedir/${::casename}.png
+
+vactivate V1/ThumbView
+vdump $::imagedir/${::casename}_thumb.png
diff --git a/tests/opengl/data/general/multiview5 b/tests/opengl/data/general/multiview5
new file mode 100644 (file)
index 0000000..efdb643
--- /dev/null
@@ -0,0 +1,26 @@
+puts "========"
+puts "0032886: Visualization, V3d_View - introduce interface for creating a subview"
+puts "V1/CompView + V1/ThumbView at corner"
+puts "========"
+
+pload MODELING VISUALIZATION
+vinit V1/CompView -width 380 -height 520 -composer 1
+vbackground GRAY20
+vaxo
+vcamera -persp
+box b 1 2 3
+vdisplay -dispMode 1 b
+vaspects b -faceBoundaryDraw 1
+vfit
+vzbufftrihedron
+
+vinit V1/ThumbView -parent V1/CompView -width 0.25 -height 0.25 -corner bottomRight -top 10 -left 10
+vbackground GRAY40
+vaxo
+vfit
+
+vactivate V1/CompView
+vdump $::imagedir/${::casename}.png
+
+vactivate V1/ThumbView
+vdump $::imagedir/${::casename}_thumb.png
diff --git a/tests/opengl/data/general/multiview6 b/tests/opengl/data/general/multiview6
new file mode 100644 (file)
index 0000000..dac9621
--- /dev/null
@@ -0,0 +1,53 @@
+puts "========"
+puts "0032886: Visualization, V3d_View - introduce interface for creating a subview"
+puts "V1/CompView + V2/ViewLeft + V2/ViewRightTop + V2/ViewRightBottom + margins"
+puts "========"
+
+pload MODELING VISUALIZATION
+vinit V1/CompView -width 768 -height 512 -composer 1
+vbackground GRAY20
+
+vinit V2/ViewLeft -parent V1/CompView -width 0.5  -height 1.0 -margins 2 2
+vbackground GRAY30
+vaxo
+vcamera -persp
+box b 1 2 3
+vdisplay -dispMode 1 b
+vaspects b -faceBoundaryDraw 1
+vfit
+vviewcube vc
+
+vinit V2/ViewRightTop -parent V1/CompView -width 0.5 -height 0.5 -corner topRight -margins 2 2
+vbackground GRAY40
+vaxo
+vfit
+verase vc -view
+
+vinit V3/ViewRightBottom -parent V1/CompView -width 0.5 -height 0.5 -corner bottomRight -margins 2 2
+vbackground GRAY50
+psphere s 1
+vdisplay -dispMode 1 s
+vaspects s -material SILVER
+vaxo
+vfit
+vzbufftrihedron
+
+vinit V3/ThumbView -parent V1/CompView -width 100 -height 150 -corner topLeft -top 10 -left 10 -margins 2 2
+vbackground GRAY40
+vaxo
+vfit
+
+vactivate V1/CompView
+vdump $::imagedir/${::casename}.png
+
+vactivate V2/ViewLeft
+vdump $::imagedir/${::casename}_left.png
+
+vactivate V2/ViewRightTop
+vdump $::imagedir/${::casename}_righttop.png
+
+vactivate V3/ThumbView
+vdump $::imagedir/${::casename}_thumb.png
+
+vactivate V3/ViewRightBottom
+vdump $::imagedir/${::casename}_rightbottom.png