]> OCCT Git - occt.git/commitdiff
0032570: Visualization, AIS_AnimationObject - define rotation around axis
authormzernova <mzernova@opencascade.com>
Mon, 27 Dec 2021 23:57:13 +0000 (23:57 +0000)
committerVadim Glukhikh <vadim.glukhikh@opencascade.com>
Wed, 1 Feb 2023 23:26:46 +0000 (23:26 +0000)
When using AIS_AnimationObject, linear interpolation is performed from one gp_Trsf transformation to another.
But when an object rotates around a specific axis, the object moves not along a linear trajectory,
but along a circle. Therefore, a separate class AIS_AnimationAxisRotation was created that
allows to animate rotation around a specific axis.

Test case tests/v3d/bugs/bug32570 was added.

src/AIS/AIS_AnimationAxisRotation.cxx [new file with mode: 0644]
src/AIS/AIS_AnimationAxisRotation.hxx [new file with mode: 0644]
src/AIS/AIS_AnimationObject.cxx
src/AIS/AIS_AnimationObject.hxx
src/AIS/AIS_BaseAnimationObject.cxx [new file with mode: 0644]
src/AIS/AIS_BaseAnimationObject.hxx [new file with mode: 0644]
src/AIS/FILES
src/ViewerTest/ViewerTest_ViewerCommands.cxx
tests/v3d/bugs/bug32570 [new file with mode: 0644]

diff --git a/src/AIS/AIS_AnimationAxisRotation.cxx b/src/AIS/AIS_AnimationAxisRotation.cxx
new file mode 100644 (file)
index 0000000..a8345cc
--- /dev/null
@@ -0,0 +1,51 @@
+// Copyright (c) 2023 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <AIS_AnimationAxisRotation.hxx>
+
+IMPLEMENT_STANDARD_RTTIEXT(AIS_AnimationAxisRotation, AIS_BaseAnimationObject)
+
+//=============================================================================
+//function : Constructor
+//purpose  :
+//=============================================================================
+AIS_AnimationAxisRotation::AIS_AnimationAxisRotation (const TCollection_AsciiString& theAnimationName,
+                                                      const Handle(AIS_InteractiveContext)& theContext,
+                                                      const Handle(AIS_InteractiveObject)& theObject,
+                                                      const gp_Ax1& theAxis,
+                                                      const Standard_Real theAngleStart,
+                                                      const Standard_Real theAngleEnd)
+: AIS_BaseAnimationObject (theAnimationName, theContext, theObject),
+  myRotAxis    (theAxis),
+  myAngleStart (theAngleStart),
+  myAngleEnd   (theAngleEnd)
+{
+  //
+}
+
+//=============================================================================
+//function : update
+//purpose  :
+//=============================================================================
+void AIS_AnimationAxisRotation::update (const AIS_AnimationProgress& theProgress)
+{
+  if (myObject.IsNull())
+  {
+    return;
+  }
+
+  gp_Trsf aTrsf;
+  Standard_Real aCurrentAngle = (1.0 - theProgress.LocalNormalized) * myAngleStart + theProgress.LocalNormalized * myAngleEnd;
+  aTrsf.SetRotation (myRotAxis, aCurrentAngle);
+  updateTrsf (aTrsf);
+}
diff --git a/src/AIS/AIS_AnimationAxisRotation.hxx b/src/AIS/AIS_AnimationAxisRotation.hxx
new file mode 100644 (file)
index 0000000..599f3ac
--- /dev/null
@@ -0,0 +1,53 @@
+// Copyright (c) 2023 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _AIS_AnimationAxisRotation_HeaderFile
+#define _AIS_AnimationAxisRotation_HeaderFile
+
+#include <AIS_BaseAnimationObject.hxx>
+#include <gp_TrsfNLerp.hxx>
+
+//! Animation defining object transformation.
+class AIS_AnimationAxisRotation : public AIS_BaseAnimationObject
+{
+  DEFINE_STANDARD_RTTIEXT(AIS_AnimationAxisRotation, AIS_BaseAnimationObject)
+public:
+
+  //! Constructor with initialization.
+  //! @param[in] theAnimationName animation identifier
+  //! @param[in] theContext       interactive context where object have been displayed
+  //! @param[in] theObject        object to apply rotation
+  //! @param[in] theAxis          rotation axis
+  //! @param[in] theAngleStart    rotation angle at the start of animation
+  //! @param[in] theAngleEnd      rotation angle at the end   of animation
+  Standard_EXPORT AIS_AnimationAxisRotation (const TCollection_AsciiString& theAnimationName,
+                                             const Handle(AIS_InteractiveContext)& theContext,
+                                             const Handle(AIS_InteractiveObject)& theObject,
+                                             const gp_Ax1& theAxis,
+                                             const Standard_Real theAngleStart,
+                                             const Standard_Real theAngleEnd);
+
+protected:
+
+  //! Update the progress.
+  Standard_EXPORT virtual void update (const AIS_AnimationProgress& theProgress) Standard_OVERRIDE;
+
+private:
+
+  gp_Ax1         myRotAxis;     //!< rotation axis
+  Standard_Real  myAngleStart;  //!< start angle for rotation
+  Standard_Real  myAngleEnd;    //!< end angle for rotation
+
+};
+
+#endif // _AIS_AnimationAxisRotation_HeaderFile
index faff48f05419fc7460ee286f4979c45bd599609d..dbe6bd637b5007fb13d264edb00bf5ed9e7b9fa7 100644 (file)
 
 #include <AIS_AnimationObject.hxx>
 
-#include <AIS_InteractiveContext.hxx>
-#include <V3d_View.hxx>
-
-IMPLEMENT_STANDARD_RTTIEXT(AIS_AnimationObject, AIS_Animation)
+IMPLEMENT_STANDARD_RTTIEXT(AIS_AnimationObject, AIS_BaseAnimationObject)
 
 //=============================================================================
 //function : Constructor
@@ -28,9 +25,7 @@ AIS_AnimationObject::AIS_AnimationObject (const TCollection_AsciiString& theAnim
                                           const Handle(AIS_InteractiveObject)&  theObject,
                                           const gp_Trsf& theTrsfStart,
                                           const gp_Trsf& theTrsfEnd)
-: AIS_Animation (theAnimationName),
-  myContext  (theContext),
-  myObject   (theObject),
+: AIS_BaseAnimationObject (theAnimationName, theContext, theObject),
   myTrsfLerp (theTrsfStart, theTrsfEnd)
 {
   //
@@ -49,52 +44,5 @@ void AIS_AnimationObject::update (const AIS_AnimationProgress& theProgress)
 
   gp_Trsf aTrsf;
   myTrsfLerp.Interpolate (theProgress.LocalNormalized, aTrsf);
-  if (!myContext.IsNull())
-  {
-    myContext->SetLocation (myObject, aTrsf);
-    invalidateViewer();
-  }
-  else
-  {
-    myObject->SetLocalTransformation (aTrsf);
-  }
-}
-
-//=============================================================================
-//function : invalidateViewer
-//purpose  :
-//=============================================================================
-void AIS_AnimationObject::invalidateViewer()
-{
-  if (myContext.IsNull())
-  {
-    return;
-  }
-
-  const Standard_Boolean isImmediate = myContext->CurrentViewer()->ZLayerSettings (myObject->ZLayer()).IsImmediate();
-  if (!isImmediate)
-  {
-    myContext->CurrentViewer()->Invalidate();
-    return;
-  }
-
-  // Invalidate immediate view only if it is going out of z-fit range.
-  // This might be sub-optimal performing this for each animated objects in case of many animated objects.
-  for (V3d_ListOfView::Iterator aDefViewIter = myContext->CurrentViewer()->DefinedViewIterator();
-       aDefViewIter.More(); aDefViewIter.Next())
-  {
-    const Handle(V3d_View)& aView = aDefViewIter.Value();
-    const Bnd_Box aMinMaxBox  = aView->View()->MinMaxValues (Standard_False);
-    const Bnd_Box aGraphicBox = aView->View()->MinMaxValues (Standard_True);
-    Standard_Real aZNear = 0.0;
-    Standard_Real aZFar  = 0.0;
-    if (aView->Camera()->ZFitAll (aDefViewIter.Value()->AutoZFitScaleFactor(), aMinMaxBox, aGraphicBox, aZNear, aZFar))
-    {
-      if (aZNear < aView->Camera()->ZNear()
-       || aZFar  > aView->Camera()->ZFar())
-      {
-        aDefViewIter.Value()->Invalidate();
-      }
-    }
-  }
+  updateTrsf (aTrsf);
 }
index 5202c231e287f90c7dbf5db4ecf6cb3d26d76403..f3892896fca07bd7c61eb7cca15e37ebd2335d78 100644 (file)
 #ifndef _AIS_AnimationObject_HeaderFile
 #define _AIS_AnimationObject_HeaderFile
 
-#include <AIS_Animation.hxx>
-#include <AIS_InteractiveContext.hxx>
+#include <AIS_BaseAnimationObject.hxx>
 #include <gp_TrsfNLerp.hxx>
 
 //! Animation defining object transformation.
-class AIS_AnimationObject : public AIS_Animation
+class AIS_AnimationObject : public AIS_BaseAnimationObject
 {
-  DEFINE_STANDARD_RTTIEXT(AIS_AnimationObject, AIS_Animation)
+  DEFINE_STANDARD_RTTIEXT(AIS_AnimationObject, AIS_BaseAnimationObject)
 public:
 
   //! Constructor with initialization.
   //! Note that start/end transformations specify exactly local transformation of the object,
   //! not the transformation to be applied to existing local transformation.
-  //! @param theAnimationName animation identifier
-  //! @param theContext       interactive context where object have been displayed
-  //! @param theObject        object to apply local transformation
-  //! @param theTrsfStart     local transformation at the start of animation (e.g. theObject->LocalTransformation())
-  //! @param theTrsfEnd       local transformation at the end   of animation
+  //! @param[in] theAnimationName animation identifier
+  //! @param[in] theContext       interactive context where object have been displayed
+  //! @param[in] theObject        object to apply local transformation
+  //! @param[in] theTrsfStart     local transformation at the start of animation (e.g. theObject->LocalTransformation())
+  //! @param[in] theTrsfEnd       local transformation at the end   of animation
   Standard_EXPORT AIS_AnimationObject (const TCollection_AsciiString& theAnimationName,
                                        const Handle(AIS_InteractiveContext)& theContext,
                                        const Handle(AIS_InteractiveObject)&  theObject,
@@ -44,17 +43,10 @@ protected:
   //! Update the progress.
   Standard_EXPORT virtual void update (const AIS_AnimationProgress& theProgress) Standard_OVERRIDE;
 
-  //! Invalidate the viewer for proper update.
-  Standard_EXPORT void invalidateViewer();
+private:
 
-protected:
-
-  Handle(AIS_InteractiveContext) myContext;   //!< context where object is displayed
-  Handle(AIS_InteractiveObject)  myObject;    //!< presentation object to set location
-  gp_TrsfNLerp                   myTrsfLerp;  //!< interpolation tool
+  gp_TrsfNLerp  myTrsfLerp; //!< interpolation tool
 
 };
 
-DEFINE_STANDARD_HANDLE(AIS_AnimationObject, AIS_Animation)
-
 #endif // _AIS_AnimationObject_HeaderFile
diff --git a/src/AIS/AIS_BaseAnimationObject.cxx b/src/AIS/AIS_BaseAnimationObject.cxx
new file mode 100644 (file)
index 0000000..d9c28e3
--- /dev/null
@@ -0,0 +1,88 @@
+// Copyright (c) 2023 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <AIS_BaseAnimationObject.hxx>
+
+#include <V3d_View.hxx>
+
+IMPLEMENT_STANDARD_RTTIEXT(AIS_BaseAnimationObject, AIS_Animation)
+
+//=============================================================================
+//function : Constructor
+//purpose  :
+//=============================================================================
+AIS_BaseAnimationObject::AIS_BaseAnimationObject (const TCollection_AsciiString& theAnimationName,
+                                                  const Handle(AIS_InteractiveContext)& theContext,
+                                                  const Handle(AIS_InteractiveObject)&  theObject)
+: AIS_Animation (theAnimationName),
+  myContext  (theContext),
+  myObject   (theObject)
+{
+  //
+}
+
+//=============================================================================
+//function : updateTrsf
+//purpose  :
+//=============================================================================
+void AIS_BaseAnimationObject::updateTrsf (const gp_Trsf& theTrsf)
+{
+  if (!myContext.IsNull())
+  {
+    myContext->SetLocation (myObject, theTrsf);
+    invalidateViewer();
+  }
+  else
+  {
+    myObject->SetLocalTransformation (theTrsf);
+  }
+}
+
+//=============================================================================
+//function : invalidateViewer
+//purpose  :
+//=============================================================================
+void AIS_BaseAnimationObject::invalidateViewer()
+{
+  if (myContext.IsNull())
+  {
+    return;
+  }
+
+  const Standard_Boolean isImmediate = myContext->CurrentViewer()->ZLayerSettings (myObject->ZLayer()).IsImmediate();
+  if (!isImmediate)
+  {
+    myContext->CurrentViewer()->Invalidate();
+    return;
+  }
+
+  // Invalidate immediate view only if it is going out of z-fit range.
+  // This might be sub-optimal performing this for each animated objects in case of many animated objects.
+  for (V3d_ListOfView::Iterator aDefViewIter = myContext->CurrentViewer()->DefinedViewIterator();
+       aDefViewIter.More(); aDefViewIter.Next())
+  {
+    const Handle(V3d_View)& aView = aDefViewIter.Value();
+    const Bnd_Box aMinMaxBox  = aView->View()->MinMaxValues (Standard_False);
+    const Bnd_Box aGraphicBox = aView->View()->MinMaxValues (Standard_True);
+    Standard_Real aZNear = 0.0;
+    Standard_Real aZFar  = 0.0;
+    if (aView->Camera()->ZFitAll (aDefViewIter.Value()->AutoZFitScaleFactor(), aMinMaxBox, aGraphicBox, aZNear, aZFar))
+    {
+      if (aZNear < aView->Camera()->ZNear()
+       || aZFar  > aView->Camera()->ZFar())
+      {
+        aDefViewIter.Value()->Invalidate();
+      }
+    }
+  }
+}
diff --git a/src/AIS/AIS_BaseAnimationObject.hxx b/src/AIS/AIS_BaseAnimationObject.hxx
new file mode 100644 (file)
index 0000000..8df788d
--- /dev/null
@@ -0,0 +1,49 @@
+// Copyright (c) 2023 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _AIS_BaseAnimationObject_HeaderFile
+#define _AIS_BaseAnimationObject_HeaderFile
+
+#include <AIS_Animation.hxx>
+#include <AIS_InteractiveContext.hxx>
+
+//! Animation defining object transformation.
+class AIS_BaseAnimationObject : public AIS_Animation
+{
+  DEFINE_STANDARD_RTTIEXT(AIS_BaseAnimationObject, AIS_Animation)
+protected:
+
+  //! Constructor with initialization.
+  //! @param[in] theAnimationName animation identifier
+  //! @param[in] theContext       interactive context where object have been displayed
+  //! @param[in] theObject        object to apply local transformation
+  Standard_EXPORT AIS_BaseAnimationObject (const TCollection_AsciiString& theAnimationName,
+                                           const Handle(AIS_InteractiveContext)& theContext,
+                                           const Handle(AIS_InteractiveObject)&  theObject);
+
+  //! Update the transformation.
+  Standard_EXPORT void updateTrsf (const gp_Trsf& theTrsf);
+
+private:
+
+  //! Invalidate the viewer for proper update.
+  Standard_EXPORT void invalidateViewer();
+
+protected:
+
+  Handle(AIS_InteractiveContext) myContext;   //!< context where object is displayed
+  Handle(AIS_InteractiveObject)  myObject;    //!< presentation object to set location
+
+};
+
+#endif // _AIS_BaseAnimationObject_HeaderFile
index beaf64b459b63fff6c8dbd3851a96ca9afeff2c8..980326d9552a7b80156199a6269faedf44c8ed65 100644 (file)
@@ -2,6 +2,8 @@ AIS.hxx
 AIS_Animation.cxx
 AIS_Animation.hxx
 AIS_AnimationTimer.hxx
+AIS_AnimationAxisRotation.cxx
+AIS_AnimationAxisRotation.hxx
 AIS_AnimationCamera.cxx
 AIS_AnimationCamera.hxx
 AIS_AnimationObject.cxx
@@ -12,6 +14,8 @@ AIS_Axis.cxx
 AIS_Axis.hxx
 AIS_BadEdgeFilter.cxx
 AIS_BadEdgeFilter.hxx
+AIS_BaseAnimationObject.cxx
+AIS_BaseAnimationObject.hxx
 AIS_C0RegularityFilter.cxx
 AIS_C0RegularityFilter.hxx
 AIS_CameraFrustum.cxx
index dd2389fa337d3b72702b4a413b01e6820b17394e..15ad3942b30bb711a7d89c385ccf69e105cbc0bd 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <ViewerTest.hxx>
 
+#include <AIS_AnimationAxisRotation.hxx>
 #include <AIS_AnimationCamera.hxx>
 #include <AIS_AnimationObject.hxx>
 #include <AIS_Axis.hxx>
@@ -7596,6 +7597,11 @@ static Standard_Integer VAnimation (Draw_Interpretor& theDI,
       gp_XYZ        aLocPnts [2] = { aTrsfs[0].TranslationPart(),     aTrsfs[1].TranslationPart() };
       Standard_Real aScales  [2] = { aTrsfs[0].ScaleFactor(),         aTrsfs[1].ScaleFactor() };
       Standard_Boolean isTrsfSet = Standard_False;
+
+      gp_Ax1 anAxis;
+      Standard_Real anAngles[2] = { 0.0, 0.0 };
+      Standard_Boolean isAxisRotationSet = Standard_False;
+
       Standard_Integer aTrsfArgIter = anArgIter + 1;
       for (; aTrsfArgIter < theArgNb; ++aTrsfArgIter)
       {
@@ -7643,13 +7649,45 @@ static Standard_Integer VAnimation (Draw_Interpretor& theDI,
           }
           aScales[anIndex] = aScaleStr.RealValue();
         }
+        else if (aTrsfArg == "-axis")
+        {
+          isAxisRotationSet = Standard_True;
+          gp_XYZ anOrigin, aDirection;
+          if (aTrsfArgIter + 6 >= theArgNb
+          || !parseXYZ (theArgVec + aTrsfArgIter + 1, anOrigin)
+          || !parseXYZ (theArgVec + aTrsfArgIter + 4, aDirection))
+          {
+            Message::SendFail() << "Syntax error at " << aTrsfArg;
+            return 1;
+          }
+          anAxis.SetLocation  (anOrigin);
+          anAxis.SetDirection (aDirection);
+          aTrsfArgIter += 6;
+        }
+        else if (aTrsfArg.StartsWith ("-ang"))
+        {
+          isAxisRotationSet = Standard_True;
+          if (++aTrsfArgIter >= theArgNb)
+          {
+            Message::SendFail() << "Syntax error at " << aTrsfArg;
+            return 1;
+          }
+
+          const TCollection_AsciiString anAngleStr (theArgVec[aTrsfArgIter]);
+          if (!anAngleStr.IsRealValue (Standard_True))
+          {
+            Message::SendFail() << "Syntax error at " << aTrsfArg;
+            return 1;
+          }
+          anAngles[anIndex] = anAngleStr.RealValue();
+        }
         else
         {
           anArgIter = aTrsfArgIter - 1;
           break;
         }
       }
-      if (!isTrsfSet)
+      if (!isTrsfSet && !isAxisRotationSet)
       {
         Message::SendFail() << "Syntax error at " << anArg;
         return 1;
@@ -7658,15 +7696,23 @@ static Standard_Integer VAnimation (Draw_Interpretor& theDI,
       {
         anArgIter = theArgNb;
       }
+      Handle(AIS_BaseAnimationObject) anObjAnimation;
+      if (isTrsfSet)
+      {
+        aTrsfs[0].SetRotation        (aRotQuats[0]);
+        aTrsfs[1].SetRotation        (aRotQuats[1]);
+        aTrsfs[0].SetTranslationPart (aLocPnts[0]);
+        aTrsfs[1].SetTranslationPart (aLocPnts[1]);
+        aTrsfs[0].SetScaleFactor     (aScales[0]);
+        aTrsfs[1].SetScaleFactor     (aScales[1]);
 
-      aTrsfs[0].SetRotation        (aRotQuats[0]);
-      aTrsfs[1].SetRotation        (aRotQuats[1]);
-      aTrsfs[0].SetTranslationPart (aLocPnts[0]);
-      aTrsfs[1].SetTranslationPart (aLocPnts[1]);
-      aTrsfs[0].SetScaleFactor     (aScales[0]);
-      aTrsfs[1].SetScaleFactor     (aScales[1]);
-
-      Handle(AIS_AnimationObject) anObjAnimation = new AIS_AnimationObject (anAnimation->Name(), aCtx, anObject, aTrsfs[0], aTrsfs[1]);
+        anObjAnimation = new AIS_AnimationObject (anAnimation->Name(), aCtx, anObject, aTrsfs[0], aTrsfs[1]);
+      }
+      else
+      {
+        anObjAnimation = new AIS_AnimationAxisRotation (anAnimation->Name(), aCtx, anObject, anAxis,
+                                                        anAngles[0] * (M_PI / 180.0), anAngles[1] * (M_PI / 180.0));
+      }
       replaceAnimation (aParentAnimation, anAnimation, anObjAnimation);
     }
     else if (anArg == "-viewtrsf"
@@ -14394,6 +14440,11 @@ Object animation:
  -rotX   object Orientations pair (quaternions)
  -scaleX object Scale factors pair (quaternions)
 
+  vanim name -object [-axis OX OY OZ DX DY DZ] [-ang1 A] [-ang2 A]
+ -axis   rotation axis
+ -ang1   start rotation angle in degrees
+ -ang2   end   rotation angle in degrees
+
 Custom callback:
   vanim name -invoke "Command Arg1 Arg2 %Pts %LocalPts %Normalized ArgN"
 
diff --git a/tests/v3d/bugs/bug32570 b/tests/v3d/bugs/bug32570
new file mode 100644 (file)
index 0000000..7d529d4
--- /dev/null
@@ -0,0 +1,28 @@
+puts "============"
+puts "0032570: Visualization, AIS_AnimationObject - define rotation around axis"
+puts "============"
+puts ""
+
+pload MODELING VISUALIZATION
+box b1 2 100 100 -preview
+box b2 2 100 100 -preview
+
+vinit View1
+vdisplay b1 -dispMode 0
+vdisplay b2 -dispMode 1
+
+vanimation anim -object b2 -axis 2 100 0 0 0 1 -angle1 0 -angle2 90 -duration 2
+#stop at the middle of the animation (45 degrees)
+vanimation anim -play 1 0
+vanimation anim -stop
+vfit
+vdump ${imagedir}/${casename}.png
+set loc1 [vlocation b2]
+
+vlocation b2 -reset -rotate 2 100 0 0 0 1 45
+set loc2 [vlocation b2]
+
+if {$loc1 != $loc2} { puts "Error: the location at the middle of animation is different from the location after rotating by 45 degrees" }
+
+puts "Put the following command to start interactive animation:"
+puts "    vanimation anim -play"