0031620: Samples - update Android JNI sample to use AIS_ViewController
authorkgv <kgv@opencascade.com>
Sat, 20 Jun 2020 00:18:12 +0000 (03:18 +0300)
committerkgv <kgv@opencascade.com>
Mon, 22 Jun 2020 08:53:57 +0000 (11:53 +0300)
Multi-touch input is now redirected to AIS_ViewController.
GLSurfaceView.RENDERMODE_WHEN_DIRTY is now used by 3D Viewer.
AIS_ViewCube is now displayed instead of trihedron.

samples/java/jniviewer/app/src/main/java/com/opencascade/jnisample/OcctJniRenderer.java
samples/java/jniviewer/app/src/main/java/com/opencascade/jnisample/OcctJniView.java
samples/java/jniviewer/app/src/main/jni/OcctJni_Viewer.cxx
samples/java/jniviewer/app/src/main/jni/OcctJni_Viewer.hxx

index 731037a..66e6e82 100644 (file)
@@ -34,11 +34,13 @@ public class OcctJniRenderer implements GLSurfaceView.Renderer
   };
 
   //! Empty constructor.
-  OcctJniRenderer()
+  OcctJniRenderer (GLSurfaceView theView,
+                   float theScreenDensity)
   {
+    myView = theView; // this makes cyclic dependency, but it is OK for JVM
     if (OcctJniActivity.areNativeLoaded)
     {
-      myCppViewer = cppCreate();
+      myCppViewer = cppCreate (theScreenDensity);
     }
   }
 
@@ -56,7 +58,10 @@ public class OcctJniRenderer implements GLSurfaceView.Renderer
   {
     if (myCppViewer != 0)
     {
-      cppRedraw (myCppViewer);
+      if (cppRedraw (myCppViewer))
+      {
+        myView.requestRender(); // this method is allowed from any thread
+      }
     }
   }
 
@@ -77,48 +82,39 @@ public class OcctJniRenderer implements GLSurfaceView.Renderer
     }
   }
 
-  //! Initialize rotation (remember first point position)
-  public void onStartRotation (int theStartX, int theStartY)
+  //! Add touch point.
+  public void onAddTouchPoint (int theId, float theX, float theY)
   {
     if (myCppViewer != 0)
     {
-      cppStartRotation (myCppViewer, theStartX, theStartY);
+      cppAddTouchPoint (myCppViewer, theId, theX, theY);
     }
   }
 
-  //! Perform rotation (relative to first point)
-  public void onRotation (int theX, int theY)
+  //! Update touch point.
+  public void onUpdateTouchPoint (int theId, float theX, float theY)
   {
     if (myCppViewer != 0)
     {
-      cppOnRotation (myCppViewer, theX, theY);
+      cppUpdateTouchPoint (myCppViewer, theId, theX, theY);
     }
   }
 
-  //! Perform panning
-  public void onPanning (int theDX, int theDY)
+  //! Remove touch point.
+  public void onRemoveTouchPoint (int theId)
   {
     if (myCppViewer != 0)
     {
-      cppOnPanning (myCppViewer, theDX, theDY);
+      cppRemoveTouchPoint (myCppViewer, theId);
     }
   }
 
-  //! Perform selection
-  public void onClick (int theX, int theY)
+  //! Select in 3D Viewer.
+  public void onSelectInViewer (float theX, float theY)
   {
     if (myCppViewer != 0)
     {
-      cppOnClick (myCppViewer, theX, theY);
-    }
-  }
-
-  //! Stop previously active action (e.g. discard first rotation point)
-  public void onStopAction()
-  {
-    if (myCppViewer != 0)
-    {
-      cppStopAction (myCppViewer);
+      cppSelectInViewer (myCppViewer, theX, theY);
     }
   }
 
@@ -157,7 +153,7 @@ public class OcctJniRenderer implements GLSurfaceView.Renderer
   }
 
   //! Create instance of C++ class
-  private native long cppCreate();
+  private native long cppCreate (float theDispDensity);
 
   //! Destroy instance of C++ class
   private native void cppDestroy (long theCppPtr);
@@ -171,11 +167,21 @@ public class OcctJniRenderer implements GLSurfaceView.Renderer
   //! Open CAD file
   private native void cppOpen    (long theCppPtr, String thePath);
 
-  //! Handle detection in the viewer
-  private native void cppMoveTo  (long theCppPtr, int theX, int theY);
+  //! Add touch point
+  private native void cppAddTouchPoint (long theCppPtr, int theId, float theX, float theY);
+
+  //! Update touch point
+  private native void cppUpdateTouchPoint (long theCppPtr, int theId, float theX, float theY);
+
+  //! Remove touch point
+  private native void cppRemoveTouchPoint (long theCppPtr, int theId);
+
+  //! Select in 3D Viewer.
+  private native void cppSelectInViewer (long theCppPtr, float theX, float theY);
 
   //! Redraw OCCT viewer
-  private native void cppRedraw  (long theCppPtr);
+  //! Returns TRUE if more frames are requested.
+  private native boolean cppRedraw  (long theCppPtr);
 
   //! Fit All
   private native void cppFitAll  (long theCppPtr);
@@ -198,21 +204,7 @@ public class OcctJniRenderer implements GLSurfaceView.Renderer
   //! Move camera
   private native void cppSetZnegProj (long theCppPtr);
 
-  //! Initialize rotation
-  private native void cppStartRotation (long theCppPtr, int theStartX, int theStartY);
-
-  //! Perform rotation
-  private native void cppOnRotation    (long theCppPtr, int theX,  int theY);
-
-  //! Perform panning
-  private native void cppOnPanning     (long theCppPtr, int theDX, int theDY);
-
-  //! Perform selection
-  private native void cppOnClick       (long theCppPtr, int theX,  int theY);
-
-  //! Stop action (rotation / panning / scaling)
-  private native void cppStopAction    (long theCppPtr);
-
+  private GLSurfaceView myView = null; //!< back reference to the View
   private long myCppViewer = 0;   //!< pointer to c++ class instance
 
 }
index 7909d9c..51e9b5d 100644 (file)
@@ -38,6 +38,9 @@ class OcctJniView extends GLSurfaceView
   {
     super (theContext, theAttrs);
 
+    android.util.DisplayMetrics aDispInfo = theContext.getResources().getDisplayMetrics();
+    myScreenDensity = aDispInfo.density;
+
     setPreserveEGLContextOnPause (true);
     setEGLContextFactory (new ContextFactory());
     setEGLConfigChooser  (new ConfigChooser());
@@ -45,8 +48,9 @@ class OcctJniView extends GLSurfaceView
     RelativeLayout.LayoutParams aLParams = new RelativeLayout.LayoutParams (LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
     aLParams.addRule (RelativeLayout.ALIGN_TOP);
 
-    myRenderer = new OcctJniRenderer();
+    myRenderer = new OcctJniRenderer (this, myScreenDensity);
     setRenderer (myRenderer);
+    setRenderMode (GLSurfaceView.RENDERMODE_WHEN_DIRTY); // render on request to spare battery
   }
 
   //! Open file.
@@ -54,6 +58,7 @@ class OcctJniView extends GLSurfaceView
   {
     final String aPath = thePath;
     queueEvent (new Runnable() { public void run() { myRenderer.open (aPath); }});
+    requestRender();
   }
 
   //! Create OpenGL ES 2.0+ context
@@ -202,77 +207,44 @@ class OcctJniView extends GLSurfaceView
   //! Callback to handle touch events
   @Override public boolean onTouchEvent (MotionEvent theEvent)
   {
-    int aPointerIndex = theEvent.getActionIndex();
-    int aPointerId    = theEvent.getPointerId (aPointerIndex);
-    int aMaskedAction = theEvent.getActionMasked();
+    final int aMaskedAction = theEvent.getActionMasked();
     switch (aMaskedAction)
     {
       case MotionEvent.ACTION_DOWN:
       case MotionEvent.ACTION_POINTER_DOWN:
       {
-        PointF aPntLast = null;
-        if (myActivePointers.size() >= 1)
+        final int aPointerIndex = theEvent.getActionIndex();
+        final int aPointerId    = theEvent.getPointerId (aPointerIndex);
+        final PointF aPnt = new PointF (theEvent.getX (aPointerIndex), theEvent.getY (aPointerIndex));
+
+        if (theEvent.getPointerCount() == 1)
         {
-          aPntLast = myActivePointers.get (myActivePointers.keyAt (0));
+          mySelectPoint = aPnt;
         }
-
-        final PointF aPnt = new PointF();
-        aPnt.x = theEvent.getX (aPointerIndex);
-        aPnt.y = theEvent.getY (aPointerIndex);
-        myActivePointers.put (aPointerId, aPnt);
-
-        switch (myActivePointers.size())
+        else
         {
-          case 1:
-          {
-            final int aStartX = (int )aPnt.x;
-            final int aStartY = (int )aPnt.y;
-            queueEvent (new Runnable() { public void run() { myRenderer.onStartRotation (aStartX, aStartY); }});
-            break;
-          }
-          case 2:
-          {
-            myPanFrom.x = (aPntLast.x + aPnt.x) * 0.5f;
-            myPanFrom.y = (aPntLast.y + aPnt.y) * 0.5f;
-            break;
-          }
+          mySelectPoint = null;
         }
 
+        queueEvent (new Runnable() { public void run() { myRenderer.onAddTouchPoint (aPointerId, aPnt.x, aPnt.y); }});
         break;
       }
       case MotionEvent.ACTION_MOVE:
       {
         for (int aNbPointers = theEvent.getPointerCount(), aPntIter = 0; aPntIter < aNbPointers; ++aPntIter)
         {
-          PointF aPnt = myActivePointers.get (theEvent.getPointerId (aPntIter));
-          if (aPnt != null)
-          {
-            aPnt.x = theEvent.getX (aPntIter);
-            aPnt.y = theEvent.getY (aPntIter);
-          }
+          final int aPointerId = theEvent.getPointerId (aPntIter);
+          final PointF aPnt = new PointF (theEvent.getX (aPntIter), theEvent.getY (aPntIter));
+          queueEvent (new Runnable() { public void run() { myRenderer.onUpdateTouchPoint (aPointerId, aPnt.x, aPnt.y); }});
         }
-
-        switch (myActivePointers.size())
+        if (mySelectPoint != null)
         {
-          case 1:
-          {
-            PointF aPnt = myActivePointers.get (theEvent.getPointerId (0));
-            final int anX = (int )aPnt.x;
-            final int anY = (int )aPnt.y;
-            queueEvent (new Runnable() { public void run() { myRenderer.onRotation (anX, anY); }});
-            break;
-          }
-          case 2:
+          final float aTouchThreshold = 5.0f * myScreenDensity;
+          final int aPointerIndex = theEvent.getActionIndex();
+          final PointF aDelta = new PointF (theEvent.getX (aPointerIndex) - mySelectPoint.x, theEvent.getY (aPointerIndex) - mySelectPoint.y);
+          if (Math.abs (aDelta.x) > aTouchThreshold || Math.abs (aDelta.y) > aTouchThreshold)
           {
-            PointF aPnt1 = myActivePointers.get (myActivePointers.keyAt (0));
-            PointF aPnt2 = myActivePointers.get (myActivePointers.keyAt (1));
-            PointF aPntAver = new PointF ((aPnt1.x + aPnt2.x) * 0.5f,
-                                          (aPnt1.y + aPnt2.y) * 0.5f);
-            final int aDX = (int )(aPntAver.x - myPanFrom.x);
-            final int aDY = (int )(myPanFrom.y -aPntAver.y);
-            myPanFrom.x = aPntAver.x;
-            myPanFrom.y = aPntAver.y;
-            queueEvent (new Runnable() { public void run() { myRenderer.onPanning (aDX, aDY); }});
+            mySelectPoint = null;
           }
         }
         break;
@@ -281,30 +253,21 @@ class OcctJniView extends GLSurfaceView
       case MotionEvent.ACTION_POINTER_UP:
       case MotionEvent.ACTION_CANCEL:
       {
-        myActivePointers.remove (aPointerId);
-        if (myActivePointers.size() == 0)
+        if (mySelectPoint != null)
         {
-          final int aPressX      = (int )theEvent.getX (aPointerIndex);
-          final int aPressY      = (int )theEvent.getY (aPointerIndex);
-          double    aPressTimeMs = theEvent.getEventTime() - theEvent.getDownTime();
-          if (aPressTimeMs < 100.0)
-          {
-            queueEvent (new Runnable() { public void run() { myRenderer.onClick (aPressX, aPressY); }});
-            break;
-          }
+          final float aSelX = mySelectPoint.x;
+          final float aSelY = mySelectPoint.y;
+          queueEvent (new Runnable() { public void run() { myRenderer.onSelectInViewer (aSelX, aSelY); }});
+          mySelectPoint = null;
         }
-        else if (myActivePointers.size() == 1)
-        {
-          PointF    aPnt    = myActivePointers.get (myActivePointers.keyAt (0));
-          final int aStartX = (int )aPnt.x;
-          final int aStartY = (int )aPnt.y;
-          queueEvent (new Runnable() { public void run() { myRenderer.onStartRotation (aStartX, aStartY); }});
-        }
-        //queueEvent (new Runnable() { public void run() { myRenderer.onStopAction(); }});
-        break;
+
+        final int aPointerIndex = theEvent.getActionIndex();
+        final int aPointerId    = theEvent.getPointerId (aPointerIndex);
+        final PointF aPnt = new PointF (theEvent.getX (aPointerIndex), theEvent.getY (aPointerIndex));
+        queueEvent (new Runnable() { public void run() { myRenderer.onRemoveTouchPoint (aPointerId); }});
       }
     }
-    ///invalidate();
+    requestRender();
     return true;
   }
 
@@ -312,21 +275,20 @@ class OcctJniView extends GLSurfaceView
   public void fitAll()
   {
     queueEvent (new Runnable() { public void run() { myRenderer.fitAll(); }});
+    requestRender();
   }
 
   //! Move camera
   public void setProj (final OcctJniRenderer.TypeOfOrientation theProj)
   {
     queueEvent (new Runnable() { public void run() { myRenderer.setProj (theProj); }});
+    requestRender();
   }
 
   //! OCCT viewer
-  private OcctJniRenderer     myRenderer = null;
-
-  //! Touch events cache
-  private SparseArray<PointF> myActivePointers = new SparseArray<PointF>();
-
-  //! Starting point for panning event
-  private PointF              myPanFrom  = new PointF (0.0f, 0.0f);
+  private OcctJniRenderer myRenderer = null;
+  private int    mySelectId = -1;
+  private PointF mySelectPoint = null;
+  private float  myScreenDensity = 1.0f;
 
 }
index eac9b52..9c23b3d 100644 (file)
@@ -14,6 +14,7 @@
 #include "OcctJni_Viewer.hxx"
 #include "OcctJni_MsgPrinter.hxx"
 
+#include <AIS_ViewCube.hxx>
 #include <AIS_Shape.hxx>
 #include <Aspect_NeutralWindow.hxx>
 #include <Image_AlienPixMap.hxx>
@@ -24,6 +25,7 @@
 #include <OpenGl_GraphicDriver.hxx>
 #include <OSD_Environment.hxx>
 #include <OSD_Timer.hxx>
+#include <Prs3d_DatumAspect.hxx>
 #include <Standard_Version.hxx>
 
 #include <BRepPrimAPI_MakeBox.hxx>
@@ -107,8 +109,11 @@ Standard_Boolean setResourceEnv (const TCollection_AsciiString& theVarName,
 // function : OcctJni_Viewer
 // purpose  :
 // =======================================================================
-OcctJni_Viewer::OcctJni_Viewer()
+OcctJni_Viewer::OcctJni_Viewer (float theDispDensity)
+: myDevicePixelRatio (theDispDensity),
+  myIsJniMoreFrames (false)
 {
+  SetTouchToleranceScale (theDispDensity);
 #ifndef NDEBUG
   // Register printer for logging messages into global Android log.
   // Should never be used in production (or specify higher gravity for logging only failures).
@@ -124,6 +129,51 @@ OcctJni_Viewer::OcctJni_Viewer()
   setResourceEnv ("CSF_SHMessage", aResRoot + "/XSMessage", "SHAPE.us", Standard_False);
 }
 
+// ================================================================
+// Function : dumpGlInfo
+// Purpose  :
+// ================================================================
+void OcctJni_Viewer::dumpGlInfo (bool theIsBasic)
+{
+  TColStd_IndexedDataMapOfStringString aGlCapsDict;
+  myView->DiagnosticInformation (aGlCapsDict, Graphic3d_DiagnosticInfo_Basic); //theIsBasic ? Graphic3d_DiagnosticInfo_Basic : Graphic3d_DiagnosticInfo_Complete);
+  if (theIsBasic)
+  {
+    TCollection_AsciiString aViewport;
+    aGlCapsDict.FindFromKey ("Viewport", aViewport);
+    aGlCapsDict.Clear();
+    aGlCapsDict.Add ("Viewport", aViewport);
+  }
+  aGlCapsDict.Add ("Display scale", TCollection_AsciiString(myDevicePixelRatio));
+
+  // beautify output
+  {
+    TCollection_AsciiString* aGlVer   = aGlCapsDict.ChangeSeek ("GLversion");
+    TCollection_AsciiString* aGlslVer = aGlCapsDict.ChangeSeek ("GLSLversion");
+    if (aGlVer   != NULL
+     && aGlslVer != NULL)
+    {
+      *aGlVer = *aGlVer + " [GLSL: " + *aGlslVer + "]";
+      aGlslVer->Clear();
+    }
+  }
+
+  TCollection_AsciiString anInfo;
+  for (TColStd_IndexedDataMapOfStringString::Iterator aValueIter (aGlCapsDict); aValueIter.More(); aValueIter.Next())
+  {
+    if (!aValueIter.Value().IsEmpty())
+    {
+      if (!anInfo.IsEmpty())
+      {
+        anInfo += "\n";
+      }
+      anInfo += aValueIter.Key() + ": " + aValueIter.Value();
+    }
+  }
+
+  Message::Send (anInfo, Message_Warning);
+}
+
 // =======================================================================
 // function : init
 // purpose  :
@@ -157,15 +207,6 @@ bool OcctJni_Viewer::init()
     return false;
   }
 
-  TCollection_AsciiString anEglInfo = TCollection_AsciiString()
-      + "\n  EGLVersion:     " + eglQueryString (anEglDisplay, EGL_VERSION)
-      + "\n  EGLVendor:      " + eglQueryString (anEglDisplay, EGL_VENDOR)
-      + "\n  EGLClient APIs: " + eglQueryString (anEglDisplay, EGL_CLIENT_APIS)
-      + "\n  GLvendor:       " + (const char* )glGetString (GL_VENDOR)
-      + "\n  GLdevice:       " + (const char* )glGetString (GL_RENDERER)
-      + "\n  GLversion:      " + (const char* )glGetString (GL_VERSION) + " [GLSL: " + (const char* )glGetString (GL_SHADING_LANGUAGE_VERSION) + "]";
-    ::Message::DefaultMessenger()->Send (anEglInfo, Message_Info);
-
   if (!myViewer.IsNull())
   {
     Handle(OpenGl_GraphicDriver) aDriver = Handle(OpenGl_GraphicDriver)::DownCast (myViewer->Driver());
@@ -179,6 +220,7 @@ bool OcctJni_Viewer::init()
 
     aWindow->SetSize (aWidth, aHeight);
     myView->SetWindow (aWindow, (Aspect_RenderingContext )anEglContext);
+    dumpGlInfo (true);
     return true;
   }
 
@@ -192,6 +234,17 @@ bool OcctJni_Viewer::init()
     return false;
   }
 
+  myTextStyle = new Prs3d_TextAspect();
+  myTextStyle->SetFont (Font_NOF_ASCII_MONO);
+  myTextStyle->SetHeight (12);
+  myTextStyle->Aspect()->SetColor (Quantity_NOC_GRAY95);
+  myTextStyle->Aspect()->SetColorSubTitle (Quantity_NOC_BLACK);
+  myTextStyle->Aspect()->SetDisplayType (Aspect_TODT_SHADOW);
+  myTextStyle->Aspect()->SetTextFontAspect (Font_FA_Bold);
+  myTextStyle->Aspect()->SetTextZoomable (false);
+  myTextStyle->SetHorizontalJustification (Graphic3d_HTA_LEFT);
+  myTextStyle->SetVerticalJustification (Graphic3d_VTA_BOTTOM);
+
   // create viewer
   myViewer = new V3d_Viewer (aDriver);
   myViewer->SetDefaultBackgroundColor (Quantity_NOC_BLACK);
@@ -200,15 +253,22 @@ bool OcctJni_Viewer::init()
 
   // create AIS context
   myContext = new AIS_InteractiveContext (myViewer);
-  //myContext->SetDisplayMode (AIS_WireFrame, false);
+  myContext->SetPixelTolerance (int(myDevicePixelRatio * 6.0)); // increase tolerance and adjust to hi-dpi screens
   myContext->SetDisplayMode (AIS_Shaded, false);
 
   Handle(Aspect_NeutralWindow) aWindow = new Aspect_NeutralWindow();
   aWindow->SetSize (aWidth, aHeight);
   myView = myViewer->CreateView();
+  myView->SetImmediateUpdate (false);
+  myView->ChangeRenderingParams().Resolution = (unsigned int )(96.0 * myDevicePixelRatio + 0.5);
+  myView->ChangeRenderingParams().ToShowStats = true;
+  myView->ChangeRenderingParams().CollectedStats = (Graphic3d_RenderingParams::PerfCounters ) (Graphic3d_RenderingParams::PerfCounters_FrameRate | Graphic3d_RenderingParams::PerfCounters_Triangles);
+  myView->ChangeRenderingParams().StatsTextAspect = myTextStyle->Aspect();
+  myView->ChangeRenderingParams().StatsTextHeight = (int )myTextStyle->Height();
 
   myView->SetWindow (aWindow, (Aspect_RenderingContext )anEglContext);
-  myView->TriedronDisplay (Aspect_TOTP_RIGHT_LOWER, Quantity_NOC_WHITE, 0.08, V3d_ZBUFFER);
+  dumpGlInfo (false);
+  //myView->TriedronDisplay (Aspect_TOTP_RIGHT_LOWER, Quantity_NOC_WHITE, 0.08 * myDevicePixelRatio, V3d_ZBUFFER);
 
   initContent();
   return true;
@@ -244,8 +304,8 @@ void OcctJni_Viewer::resize (int theWidth,
   //myView->MustBeResized(); // can be used instead of SetWindow() when EGLsurface has not been changed
 
   EGLContext anEglContext = eglGetCurrentContext();
-  myView->SetImmediateUpdate (Standard_False);
   myView->SetWindow (aWindow, (Aspect_RenderingContext )anEglContext);
+  dumpGlInfo (true);
   //saveSnapshot ("/sdcard/Download/tt.png", theWidth, theHeight);
 }
 
@@ -257,6 +317,28 @@ void OcctJni_Viewer::initContent()
 {
   myContext->RemoveAll (Standard_False);
 
+  if (myViewCube.IsNull())
+  {
+    myViewCube = new AIS_ViewCube();
+    {
+      // setup view cube size
+      static const double THE_CUBE_SIZE = 60.0;
+      myViewCube->SetSize (myDevicePixelRatio * THE_CUBE_SIZE, false);
+      myViewCube->SetBoxFacetExtension (myViewCube->Size() * 0.15);
+      myViewCube->SetAxesPadding (myViewCube->Size() * 0.10);
+      myViewCube->SetFontHeight  (THE_CUBE_SIZE * 0.16);
+    }
+    // presentation parameters
+    myViewCube->SetTransformPersistence (new Graphic3d_TransformPers (Graphic3d_TMF_TriedronPers, Aspect_TOTP_RIGHT_LOWER, Graphic3d_Vec2i (200, 200)));
+    myViewCube->Attributes()->SetDatumAspect (new Prs3d_DatumAspect());
+    myViewCube->Attributes()->DatumAspect()->SetTextAspect (myTextStyle);
+    // animation parameters
+    myViewCube->SetViewAnimation (myViewAnimation);
+    myViewCube->SetFixedAnimationLoop (false);
+    myViewCube->SetAutoStartAnimation (true);
+  }
+  myContext->Display (myViewCube, false);
+
   OSD_Timer aTimer;
   aTimer.Start();
   if (!myShape.IsNull())
@@ -389,6 +471,10 @@ bool OcctJni_Viewer::open (const TCollection_AsciiString& thePath)
   if (!myContext.IsNull())
   {
     myContext->RemoveAll (Standard_False);
+    if (!myViewCube.IsNull())
+    {
+      myContext->Display (myViewCube, false);
+    }
   }
   if (thePath.IsEmpty())
   {
@@ -529,18 +615,33 @@ bool OcctJni_Viewer::saveSnapshot (const TCollection_AsciiString& thePath,
   return true;
 }
 
+// ================================================================
+// Function : handleViewRedraw
+// Purpose  :
+// ================================================================
+void OcctJni_Viewer::handleViewRedraw (const Handle(AIS_InteractiveContext)& theCtx,
+                                       const Handle(V3d_View)& theView)
+{
+  AIS_ViewController::handleViewRedraw (theCtx, theView);
+  myIsJniMoreFrames = myToAskNextFrame;
+}
+
 // =======================================================================
 // function : redraw
 // purpose  :
 // =======================================================================
-void OcctJni_Viewer::redraw()
+bool OcctJni_Viewer::redraw()
 {
   if (myView.IsNull())
   {
-    return;
+    return false;
   }
 
-  myView->Redraw();
+  // handle user input
+  myIsJniMoreFrames = false;
+  myView->InvalidateImmediate();
+  FlushViewEvents (myContext, myView, true);
+  return myIsJniMoreFrames;
 }
 
 // =======================================================================
@@ -558,89 +659,13 @@ void OcctJni_Viewer::fitAll()
   myView->Invalidate();
 }
 
-// =======================================================================
-// function : startRotation
-// purpose  :
-// =======================================================================
-void OcctJni_Viewer::startRotation (int theStartX,
-                                    int theStartY)
-{
-  if (myView.IsNull())
-  {
-    return;
-  }
-
-  myView->StartRotation (theStartX, theStartY, 0.45);
-  myView->Invalidate();
-}
-
-// =======================================================================
-// function : onRotation
-// purpose  :
-// =======================================================================
-void OcctJni_Viewer::onRotation (int theX,
-                                 int theY)
-{
-  if (myView.IsNull())
-  {
-    return;
-  }
-
-  myView->Rotation (theX, theY);
-  myView->Invalidate();
-}
-
-// =======================================================================
-// function : onPanning
-// purpose  :
-// =======================================================================
-void OcctJni_Viewer::onPanning (int theDX,
-                                int theDY)
-{
-  if (myView.IsNull())
-  {
-    return;
-  }
-
-  myView->Pan (theDX, theDY);
-  myView->Invalidate();
-}
-
-// =======================================================================
-// function : onClick
-// purpose  :
-// =======================================================================
-void OcctJni_Viewer::onClick (int theX,
-                              int theY)
-{
-  if (myView.IsNull())
-  {
-    return;
-  }
-
-  myContext->MoveTo (theX, theY, myView, Standard_False);
-  myContext->Select (Standard_False);
-  myView->Invalidate();
-}
-
-// =======================================================================
-// function : stopAction
-// purpose  :
-// =======================================================================
-void OcctJni_Viewer::stopAction()
-{
-  if (myView.IsNull())
-  {
-    return;
-  }
-}
-
 #define jexp extern "C" JNIEXPORT
 
 jexp jlong JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppCreate (JNIEnv* theEnv,
-                                                                             jobject theObj)
+                                                                             jobject theObj,
+                                                                             jfloat  theDispDensity)
 {
-  return jlong(new OcctJni_Viewer());
+  return jlong(new OcctJni_Viewer (theDispDensity));
 }
 
 jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppDestroy (JNIEnv* theEnv,
@@ -690,11 +715,11 @@ jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppOpen (JNIEnv
   ((OcctJni_Viewer* )theCppPtr)->open (aPath);
 }
 
-jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppRedraw (JNIEnv* theEnv,
-                                                                            jobject theObj,
-                                                                            jlong   theCppPtr)
+jexp jboolean JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppRedraw (JNIEnv* theEnv,
+                                                                                jobject theObj,
+                                                                                jlong   theCppPtr)
 {
-  ((OcctJni_Viewer* )theCppPtr)->redraw();
+  return ((OcctJni_Viewer* )theCppPtr)->redraw() ? JNI_TRUE : JNI_FALSE;
 }
 
 jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppSetAxoProj (JNIEnv* theEnv,
@@ -753,47 +778,41 @@ jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppFitAll (JNIE
   ((OcctJni_Viewer* )theCppPtr)->fitAll();
 }
 
-jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppStartRotation (JNIEnv* theEnv,
+jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppAddTouchPoint (JNIEnv* theEnv,
                                                                                    jobject theObj,
                                                                                    jlong   theCppPtr,
-                                                                                   jint    theStartX,
-                                                                                   jint    theStartY)
+                                                                                   jint    theId,
+                                                                                   jfloat  theX,
+                                                                                   jfloat  theY)
 {
-  ((OcctJni_Viewer* )theCppPtr)->startRotation (theStartX, theStartY);
+  ((OcctJni_Viewer* )theCppPtr)->AddTouchPoint (theId, Graphic3d_Vec2d (theX, theY));
 }
 
-jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppOnRotation (JNIEnv* theEnv,
-                                                                                jobject theObj,
-                                                                                jlong   theCppPtr,
-                                                                                jint    theX,
-                                                                                jint    theY)
-{
-  ((OcctJni_Viewer* )theCppPtr)->onRotation (theX, theY);
-}
-
-jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppOnPanning (JNIEnv* theEnv,
-                                                                               jobject theObj,
-                                                                               jlong   theCppPtr,
-                                                                               jint    theDX,
-                                                                               jint    theDY)
+jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppUpdateTouchPoint (JNIEnv* theEnv,
+                                                                                   jobject theObj,
+                                                                                   jlong   theCppPtr,
+                                                                                   jint    theId,
+                                                                                   jfloat  theX,
+                                                                                   jfloat  theY)
 {
-  ((OcctJni_Viewer* )theCppPtr)->onPanning (theDX, theDY);
+  ((OcctJni_Viewer* )theCppPtr)->UpdateTouchPoint (theId, Graphic3d_Vec2d (theX, theY));
 }
 
-jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppOnClick (JNIEnv* theEnv,
-                                                                             jobject theObj,
-                                                                             jlong   theCppPtr,
-                                                                             jint    theX,
-                                                                             jint    theY)
+jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppRemoveTouchPoint (JNIEnv* theEnv,
+                                                                                   jobject theObj,
+                                                                                   jlong   theCppPtr,
+                                                                                   jint    theId)
 {
-  ((OcctJni_Viewer* )theCppPtr)->onClick (theX, theY);
+  ((OcctJni_Viewer* )theCppPtr)->RemoveTouchPoint (theId);
 }
 
-jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppStopAction (JNIEnv* theEnv,
-                                                                                jobject theObj,
-                                                                                jlong   theCppPtr)
+jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppSelectInViewer (JNIEnv* theEnv,
+                                                                                    jobject theObj,
+                                                                                    jlong   theCppPtr,
+                                                                                    jfloat  theX,
+                                                                                    jfloat  theY)
 {
-  ((OcctJni_Viewer* )theCppPtr)->stopAction();
+  ((OcctJni_Viewer* )theCppPtr)->SelectInViewer (Graphic3d_Vec2i ((int )theX, (int )theY));
 }
 
 jexp jlong JNICALL Java_com_opencascade_jnisample_OcctJniActivity_cppOcctMajorVersion (JNIEnv* theEnv,
index f8b8369..f34e016 100644 (file)
 // commercial license or contractual agreement.
 
 #include <AIS_InteractiveContext.hxx>
+#include <AIS_ViewController.hxx>
 #include <TopoDS_Shape.hxx>
 #include <V3d_Viewer.hxx>
 #include <V3d_View.hxx>
 
+class AIS_ViewCube;
+
 //! Main C++ back-end for activity.
-class OcctJni_Viewer
+class OcctJni_Viewer : public AIS_ViewController
 {
 
 public:
 
   //! Empty constructor
-  OcctJni_Viewer();
+  OcctJni_Viewer (float theDispDensity);
 
   //! Initialize the viewer
   bool init();
@@ -44,43 +47,45 @@ public:
                      int theHeight = 0);
 
   //! Viewer update.
-  void redraw();
+  //! Returns TRUE if more frames should be requested.
+  bool redraw();
 
   //! Move camera
-  void setProj (V3d_TypeOfOrientation theProj) { if (!myView.IsNull()) myView->SetProj (theProj); }
+  void setProj (V3d_TypeOfOrientation theProj)
+  {
+    if (myView.IsNull())
+    {
+      return;
+    }
+
+    myView->SetProj (theProj);
+    myView->Invalidate();
+  }
 
   //! Fit All.
   void fitAll();
 
-  //! Start rotation (remember first point position)
-  void startRotation (int theStartX,
-                      int theStartY);
-
-  //! Perform rotation (relative to first point)
-  void onRotation (int theX,
-                   int theY);
-
-  //! Perform panning
-  void onPanning (int theDX,
-                  int theDY);
-
-  //! Perform selection
-  void onClick (int theX,
-                int theY);
-
-  //! Stop previously started action
-  void stopAction();
-
 protected:
 
   //! Reset viewer content.
   void initContent();
 
+  //! Print information about OpenGL ES context.
+  void dumpGlInfo (bool theIsBasic);
+
+  //! Handle redraw.
+  virtual void handleViewRedraw (const Handle(AIS_InteractiveContext)& theCtx,
+                                 const Handle(V3d_View)& theView) override;
+
 protected:
 
   Handle(V3d_Viewer)             myViewer;
   Handle(V3d_View)               myView;
   Handle(AIS_InteractiveContext) myContext;
+  Handle(Prs3d_TextAspect)       myTextStyle; //!< text style for OSD elements
+  Handle(AIS_ViewCube)           myViewCube;  //!< view cube object
   TopoDS_Shape                   myShape;
+  float                          myDevicePixelRatio; //!< device pixel ratio for handling high DPI displays
+  bool                           myIsJniMoreFrames; //!< need more frame flag
 
 };