From a3b2aaefac83ad928e1ae2718189ad93c7908d25 Mon Sep 17 00:00:00 2001
From: mkrylova <mkrylova@opencascade.com>
Date: Fri, 26 Mar 2021 12:31:22 +0300
Subject: [PATCH] 0032183: Visualization - implement
 AIS_LightSource::ProcessDragging() for rotating directional light

- Added sensitive sphere for a light source
- Implemented AIS_LightSource::ProcessDragging() interface for rotating directional light source
- Added possibility to turn on/off directional light source dragging
- Added test
---
 src/AIS/AIS_LightSource.cxx                   | 167 ++++++++++++++++++
 src/AIS/AIS_LightSource.hxx                   |  69 +++++++-
 src/Select3D/Select3D_SensitiveSphere.cxx     |   4 +-
 src/Select3D/Select3D_SensitiveSphere.hxx     |   4 +
 .../SelectMgr_RectangularFrustum.cxx          |   7 +-
 src/ViewerTest/ViewerTest_ViewerCommands.cxx  |  28 +++
 tests/v3d/light_source/dyn_highlight          |  46 +++++
 7 files changed, 310 insertions(+), 15 deletions(-)
 create mode 100644 tests/v3d/light_source/dyn_highlight

diff --git a/src/AIS/AIS_LightSource.cxx b/src/AIS/AIS_LightSource.cxx
index cd5f97835d..42a25a9a4d 100644
--- a/src/AIS/AIS_LightSource.cxx
+++ b/src/AIS/AIS_LightSource.cxx
@@ -16,7 +16,9 @@
 #include <AIS_LightSource.hxx>
 
 #include <AIS_InteractiveContext.hxx>
+#include <gp_Quaternion.hxx>
 #include <Graphic3d_ArrayOfPoints.hxx>
+#include <Graphic3d_ArrayOfPolylines.hxx>
 #include <Graphic3d_ArrayOfSegments.hxx>
 #include <Graphic3d_ArrayOfTriangles.hxx>
 #include <Graphic3d_CView.hxx>
@@ -27,6 +29,7 @@
 #include <Prs3d_ToolCylinder.hxx>
 #include <Prs3d_ToolSphere.hxx>
 #include <Select3D_SensitivePoint.hxx>
+#include <Select3D_SensitiveSphere.hxx>
 #include <V3d_View.hxx>
 
 IMPLEMENT_STANDARD_RTTIEXT(AIS_LightSource, AIS_InteractiveObject)
@@ -65,6 +68,105 @@ Standard_Boolean AIS_LightSourceOwner::HandleMouseClick (const Graphic3d_Vec2i&
   return false;
 }
 
+//=======================================================================
+//function : HilightWithColor
+//purpose  :
+//=======================================================================
+void AIS_LightSourceOwner::HilightWithColor (const Handle(PrsMgr_PresentationManager)& thePM,
+                                             const Handle(Prs3d_Drawer)& theStyle,
+                                             const Standard_Integer theMode)
+{
+  Handle(AIS_LightSource) aLightSource = Handle(AIS_LightSource)::DownCast (mySelectable);
+  if (aLightSource.IsNull())
+  {
+    return;
+  }
+
+  if (aLightSource->Light()->Type() == Graphic3d_TypeOfLightSource_Directional && aLightSource->myIsDraggable)
+  {
+    Handle(Prs3d_Presentation) aPrs = aLightSource->GetHilightPresentation (thePM);
+    const Graphic3d_ZLayerId aZLayer = theStyle->ZLayer() != -1
+                                     ? theStyle->ZLayer()
+                                     : (thePM->IsImmediateModeOn() ? Graphic3d_ZLayerId_Top : aLightSource->ZLayer());
+    aPrs->Clear();
+    if (aPrs->GetZLayer() != aZLayer)
+    {
+      aPrs->SetZLayer (aZLayer);
+    }
+    Handle(Graphic3d_ArrayOfPoints) aPoints = new Graphic3d_ArrayOfPoints (1);
+    const gp_Pnt aDetPnt = aLightSource->mySensSphere->LastDetectedPoint();
+    if (aDetPnt.X() == RealLast())
+    {
+      return;
+    }
+    aPoints->AddVertex (aDetPnt);
+    Handle(Graphic3d_Group) aGroup = aPrs->NewGroup();
+    const Handle(Prs3d_PointAspect) aPointAspect = new Prs3d_PointAspect (Aspect_TOM_O_POINT, theStyle->Color(), 3.0f);
+    aGroup->SetGroupPrimitivesAspect (aPointAspect->Aspect());
+    aGroup->AddPrimitiveArray (aPoints);
+
+    const Standard_Real aRadius = aLightSource->Size() * 0.5;
+    const Standard_Integer aNbPnts = int (aLightSource->ArcSize() * 180 / (M_PI * aRadius));
+    TColgp_Array1OfPnt aCircPoints (0, aNbPnts);
+    const gp_Dir aDirNorm (gp_Vec (gp::Origin(), aDetPnt));
+    gp_Dir aDirNormToPln (gp::DY());
+    if (!gp::DX().IsParallel (aDirNorm, Precision::Angular()))
+    {
+      aDirNormToPln = gp::DX().Crossed (aDirNorm);
+    }
+    for (Standard_Integer aStep = 0; aStep < aNbPnts; ++aStep)
+    {
+      aCircPoints.SetValue (aStep, (aDetPnt.Rotated (gp_Ax1 (gp::Origin(), aDirNormToPln), M_PI / 90 * (aStep - aNbPnts / 2))));
+    }
+
+    Handle(Graphic3d_Group) aCircGroup = aPrs->NewGroup();
+    Handle(Graphic3d_ArrayOfPolylines) aPolylines = new Graphic3d_ArrayOfPolylines (aNbPnts * 2, 2);
+    aPolylines->AddBound (aNbPnts);
+
+    for (Standard_Integer anIdx = 0; anIdx < aNbPnts; ++anIdx)
+    {
+      aPolylines->AddVertex (aCircPoints.Value (anIdx).Rotated (gp_Ax1 (gp::Origin(), aDirNorm), M_PI / 2));
+    }
+    aPolylines->AddBound (aNbPnts);
+    for (Standard_Integer anIdx = 0; anIdx < aNbPnts; ++anIdx)
+    {
+      aPolylines->AddVertex (aCircPoints.Value (anIdx));
+    }
+    aCircGroup->AddPrimitiveArray (aPolylines, Standard_False);
+    aCircGroup->SetGroupPrimitivesAspect (theStyle->ArrowAspect()->Aspect());
+    if (thePM->IsImmediateModeOn())
+    {
+      thePM->AddToImmediateList (aPrs);
+    }
+    else
+    {
+      aPrs->Display();
+    }
+  }
+  else
+  {
+    base_type::HilightWithColor (thePM, theStyle, theMode);;
+  }
+}
+
+//=======================================================================
+//function : IsForcedHilight
+//purpose  :
+//=======================================================================
+Standard_Boolean AIS_LightSourceOwner::IsForcedHilight() const
+{
+  Handle(AIS_LightSource) aLightSource = Handle(AIS_LightSource)::DownCast (mySelectable);
+  if (aLightSource.IsNull())
+  {
+    return Standard_False;
+  }
+  if (aLightSource->Light()->Type() == Graphic3d_TypeOfLightSource_Directional)
+  {
+    return Standard_True;
+  }
+  return Standard_False;
+}
+
 // =======================================================================
 // function : Constructor
 // purpose  :
@@ -77,8 +179,10 @@ AIS_LightSource::AIS_LightSource (const Handle(Graphic3d_CLight)& theLight)
   myNbArrows (5),
   myNbSplitsQuadric (theLight->Type() == Graphic3d_TypeOfLightSource_Ambient ? 10 : 30),
   myNbSplitsArrow (20),
+  mySensSphereArcSize (25),
   myIsZoomable (theLight->Type() == Graphic3d_TypeOfLightSource_Positional
              || theLight->Type() == Graphic3d_TypeOfLightSource_Spot),
+  myIsDraggable (theLight->Type() == Graphic3d_TypeOfLightSource_Directional),
   myToDisplayName (true),
   myToDisplayRange (true),
   myToSwitchOnClick (true)
@@ -133,6 +237,63 @@ AIS_LightSource::AIS_LightSource (const Handle(Graphic3d_CLight)& theLight)
   }
 }
 
+//=======================================================================
+//function : ProcessDragging
+//purpose  :
+//=======================================================================
+Standard_Boolean AIS_LightSource::ProcessDragging (const Handle(AIS_InteractiveContext)& theCtx,
+                                                   const Handle(V3d_View)& theView,
+                                                   const Handle(SelectMgr_EntityOwner)& theOwner,
+                                                   const Graphic3d_Vec2i& theDragFrom,
+                                                   const Graphic3d_Vec2i& theDragTo,
+                                                   const AIS_DragAction theAction)
+{
+  if (Light()->Type() != Graphic3d_TypeOfLightSource_Directional)
+  {
+    return Standard_False;
+  }
+
+  switch (theAction)
+  {
+    case AIS_DragAction_Start:
+    {
+      myStartTransform = theDragFrom;
+      myLocTrsfStart = LocalTransformation();
+      return Standard_True;
+    }
+    case AIS_DragAction_Update:
+    {
+      theCtx->MainSelector()->Pick (myStartTransform.x(), myStartTransform.y(), theView);
+      gp_Pnt aStartPosition = mySensSphere->LastDetectedPoint();
+      theCtx->MainSelector()->Pick (theDragTo.x(), theDragTo.y(), theView);
+      gp_Pnt aCurrPosition = mySensSphere->LastDetectedPoint();
+      if (aCurrPosition.X() != RealLast() && aStartPosition.Distance (aCurrPosition) > Precision::Confusion())
+      {
+        gp_Quaternion aQRot;
+        aQRot.SetRotation (gp_Vec (gp_Pnt (0, 0, 0), aStartPosition), gp_Vec (gp_Pnt (0, 0, 0), aCurrPosition));
+        gp_Trsf aTrsf;
+        aTrsf.SetRotation (aQRot);
+        SetLocalTransformation (myLocTrsfStart * aTrsf);
+        myLocTrsfStart = LocalTransformation();
+        myStartTransform = theDragTo;
+        theOwner->Selectable()->ClearDynamicHighlight (theCtx->MainPrsMgr());
+        theCtx->HilightWithColor (this, Handle(Prs3d_Drawer)(), false);
+      }
+      return Standard_True;
+    }
+    case AIS_DragAction_Abort:
+    {
+      return Standard_True;
+    }
+    case AIS_DragAction_Stop:
+    {
+      GetHilightPresentation (theCtx->MainPrsMgr())->Clear();
+      break;
+    }
+  }
+  return Standard_False;
+}
+
 // =======================================================================
 // function : updateLightAspects
 // purpose  :
@@ -681,6 +842,12 @@ void AIS_LightSource::ComputeSelection (const Handle(SelectMgr_Selection)& theSe
 
   Handle(AIS_LightSourceOwner) anEntityOwner = new AIS_LightSourceOwner (this, 15);
   {
+    if (myLightSource->Type() == Graphic3d_TypeOfLightSource_Directional)
+    {
+      mySensSphere = new Select3D_SensitiveSphere (anEntityOwner, gp::Origin(), mySize * 0.5);
+      theSel->Add (mySensSphere);
+    }
+
     Handle(Select3D_SensitivePoint) aSensPosition = new Select3D_SensitivePoint (anEntityOwner, gp::Origin());
     aSensPosition->SetSensitivityFactor (12);
     if (!myTransformPersistence.IsNull()
diff --git a/src/AIS/AIS_LightSource.hxx b/src/AIS/AIS_LightSource.hxx
index 5ff70250cc..1d9a433ab7 100644
--- a/src/AIS/AIS_LightSource.hxx
+++ b/src/AIS/AIS_LightSource.hxx
@@ -21,6 +21,7 @@
 #include <SelectMgr_EntityOwner.hxx>
 
 class Prs3d_ShadingAspect;
+class Select3D_SensitiveSphere;
 
 //! Interactive object for a light source.
 //! Each type of light source has it's own presentation:
@@ -90,6 +91,19 @@ public: //! @name Light properties
     }
   }
 
+  //! Returns Sensitive sphere arc size in pixels; 20 by default.
+  Standard_Integer ArcSize() const { return mySensSphereArcSize; }
+
+  //! Sets the size of sensitive sphere arc.
+  void SetArcSize (Standard_Integer theSize)
+  {
+    if (mySensSphereArcSize != theSize)
+    {
+      mySensSphereArcSize = theSize;
+      SetToUpdate();
+    }
+  }
+
   //! Returns TRUE if transform-persistence is allowed;
   //! TRUE by default for Ambient and Directional lights
   //! and FALSE by default for Positional and Spot lights.
@@ -105,6 +119,15 @@ public: //! @name Light properties
     }
   }
 
+  //! Sets if dragging is allowed.
+  void SetDraggable (bool theIsDraggable)
+  {
+    if (myIsDraggable != theIsDraggable)
+    {
+      myIsDraggable = theIsDraggable;
+    }
+  }
+
   //! Returns TRUE if mouse click will turn light on/off; TRUE by default.
   bool ToSwitchOnClick() const { return myToSwitchOnClick; }
 
@@ -182,6 +205,21 @@ protected:
   Standard_EXPORT virtual void ComputeSelection (const Handle(SelectMgr_Selection)& theSel,
                                                  const Standard_Integer theMode) Standard_OVERRIDE;
 
+  //! Drag object in the viewer.
+  //! @param[in] theCtx      interactive context
+  //! @param[in] theView     active View
+  //! @param[in] theOwner    the owner of detected entity
+  //! @param[in] theDragFrom drag start point
+  //! @param[in] theDragTo   drag end point
+  //! @param[in] theAction   drag action
+  //! @return FALSE if object rejects dragging action (e.g. AIS_DragAction_Start)
+  Standard_EXPORT virtual Standard_Boolean ProcessDragging (const Handle(AIS_InteractiveContext)& theCtx,
+                                                            const Handle(V3d_View)& theView,
+                                                            const Handle(SelectMgr_EntityOwner)& theOwner,
+                                                            const Graphic3d_Vec2i& theDragFrom,
+                                                            const Graphic3d_Vec2i& theDragTo,
+                                                            const AIS_DragAction theAction) Standard_OVERRIDE;
+
   //! Sets new local transformation, which is propagated to Graphic3d_CLight instance.
   Standard_EXPORT virtual void setLocalTransformation (const Handle(TopLoc_Datum3D)& theTrsf) Standard_OVERRIDE;
 
@@ -217,18 +255,23 @@ protected:
   Handle(Graphic3d_AspectMarker3d) myDisabledMarkerAspect;  //!< disabled light source marker style
   Handle(Graphic3d_AspectLine3d)   myArrowLineAspectShadow; //!< arrow shadow style
   Handle(Graphic3d_MarkerImage)    myMarkerImages[2];       //!< icon of disabled (0) and enabled (1) light
+  Handle(Select3D_SensitiveSphere) mySensSphere;            //!< sensitive sphere of directional light source
   Aspect_TypeOfMarker              myMarkerTypes[2];        //!< icon of disabled (0) and enabled (1) light
   Aspect_TypeOfMarker              myCodirMarkerType;       //!< icon of arrow co-directional to camera direction (look from)
   Aspect_TypeOfMarker              myOpposMarkerType;       //!< icon of arrow opposite to camera direction (look at)
 
-  Standard_Real    mySize;            //!< presentation size
-  Standard_Integer myNbArrows;        //!< number of directional light arrows
-  Standard_Integer myNbSplitsQuadric; //!< tessellation level for quadric surfaces
-  Standard_Integer myNbSplitsArrow;   //!< tessellation level for arrows
-  Standard_Boolean myIsZoomable;      //!< flag to allow/disallow transform-persistence when possible
-  Standard_Boolean myToDisplayName;   //!< flag to show/hide name
-  Standard_Boolean myToDisplayRange;  //!< flag to show/hide range of positional/spot light
-  Standard_Boolean myToSwitchOnClick; //!< flag to handle mouse click to turn light on/off
+  Graphic3d_Vec2i  myStartTransform;    //!< position of starting transformation
+  gp_Trsf          myLocTrsfStart;      //!< object transformation before transformation
+  Standard_Real    mySize;              //!< presentation size
+  Standard_Integer myNbArrows;          //!< number of directional light arrows
+  Standard_Integer myNbSplitsQuadric;   //!< tessellation level for quadric surfaces
+  Standard_Integer myNbSplitsArrow;     //!< tessellation level for arrows
+  Standard_Integer mySensSphereArcSize; //! sensitive sphere arc size in pixels
+  Standard_Boolean myIsZoomable;        //!< flag to allow/disallow transform-persistence when possible
+  Standard_Boolean myIsDraggable;       //!< flag to allow/disallow rotate directional light source by dragging
+  Standard_Boolean myToDisplayName;     //!< flag to show/hide name
+  Standard_Boolean myToDisplayRange;    //!< flag to show/hide range of positional/spot light
+  Standard_Boolean myToSwitchOnClick;   //!< flag to handle mouse click to turn light on/off
 
 };
 
@@ -248,6 +291,16 @@ public:
                                                              Aspect_VKeyFlags theModifiers,
                                                              bool theIsDoubleClick) Standard_OVERRIDE;
 
+  //! Highlights selectable object's presentation with display mode in presentation manager with given highlight style.
+  //! Also a check for auto-highlight is performed - if selectable object manages highlighting on its own,
+  //! execution will be passed to SelectMgr_SelectableObject::HilightOwnerWithColor method.
+  Standard_EXPORT virtual void HilightWithColor (const Handle(PrsMgr_PresentationManager)& thePrsMgr,
+                                                 const Handle(Prs3d_Drawer)& theStyle,
+                                                 const Standard_Integer theMode) Standard_OVERRIDE;
+
+  //! Always update dynamic highlighting.
+  Standard_EXPORT virtual Standard_Boolean IsForcedHilight() const Standard_OVERRIDE;
+
 };
 
 #endif // _AIS_LightSource_HeaderFile
diff --git a/src/Select3D/Select3D_SensitiveSphere.cxx b/src/Select3D/Select3D_SensitiveSphere.cxx
index b289ca3fb3..f452db3cea 100644
--- a/src/Select3D/Select3D_SensitiveSphere.cxx
+++ b/src/Select3D/Select3D_SensitiveSphere.cxx
@@ -26,6 +26,7 @@ Select3D_SensitiveSphere::Select3D_SensitiveSphere (const Handle(SelectMgr_Entit
                                                     const Standard_Real theRadius)
 : Select3D_SensitiveEntity (theOwnerId),
   myCenter (theCenter),
+  myLastDetectedPoint (RealLast(), RealLast(), RealLast()),
   myRadius (theRadius)
 {
 }
@@ -37,6 +38,7 @@ Select3D_SensitiveSphere::Select3D_SensitiveSphere (const Handle(SelectMgr_Entit
 Standard_Boolean Select3D_SensitiveSphere::Matches (SelectBasics_SelectingVolumeManager& theMgr,
                                                     SelectBasics_PickResult& thePickResult)
 {
+  myLastDetectedPoint = gp_Pnt (RealLast(), RealLast(), RealLast());
   if (theMgr.GetActiveSelectionType() != SelectMgr_SelectionType_Point)
   {
     if (!theMgr.IsOverlapAllowed())
@@ -53,7 +55,7 @@ Standard_Boolean Select3D_SensitiveSphere::Matches (SelectBasics_SelectingVolume
   {
     return Standard_False;
   }
-
+  myLastDetectedPoint = thePickResult.PickedPoint();
   thePickResult.SetDistToGeomCenter (theMgr.DistToGeometryCenter (myCenter));
   return Standard_True;
 }
diff --git a/src/Select3D/Select3D_SensitiveSphere.hxx b/src/Select3D/Select3D_SensitiveSphere.hxx
index 9a976c697a..3202401b3c 100644
--- a/src/Select3D/Select3D_SensitiveSphere.hxx
+++ b/src/Select3D/Select3D_SensitiveSphere.hxx
@@ -55,8 +55,12 @@ public:
   //! Returns center of the sphere with transformation applied
   virtual gp_Pnt CenterOfGeometry() const Standard_OVERRIDE { return myCenter; };
 
+  //! Returns the position of detected point on the sphere.
+  const gp_Pnt& LastDetectedPoint() const { return myLastDetectedPoint; }
+
 protected:
   gp_Pnt        myCenter;
+  gp_Pnt        myLastDetectedPoint;
   Standard_Real myRadius;
 };
 
diff --git a/src/SelectMgr/SelectMgr_RectangularFrustum.cxx b/src/SelectMgr/SelectMgr_RectangularFrustum.cxx
index c47f770bf3..9b8925f12d 100644
--- a/src/SelectMgr/SelectMgr_RectangularFrustum.cxx
+++ b/src/SelectMgr/SelectMgr_RectangularFrustum.cxx
@@ -764,11 +764,6 @@ Standard_Boolean SelectMgr_RectangularFrustum::OverlapsSphere (const gp_Pnt& the
 {
   Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
     "Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
-  if (!hasSphereOverlap (theCenter, theRadius))
-  {
-    return Standard_False;
-  }
-
   Standard_Real aTimeEnter = 0.0, aTimeLeave = 0.0;
   if (!RaySphereIntersection (theCenter, theRadius, myNearPickedPnt, myViewRayDir, aTimeEnter, aTimeLeave))
   {
@@ -780,7 +775,7 @@ Standard_Boolean SelectMgr_RectangularFrustum::OverlapsSphere (const gp_Pnt& the
   {
     thePickResult.SetDepth (aTimeLeave * myScale);
   }
-  gp_Pnt aPntOnSphere (myNearPickedPnt.XYZ() + myViewRayDir.XYZ() * thePickResult.Depth());
+  gp_Pnt aPntOnSphere (myNearPickedPnt.XYZ() + myViewRayDir.XYZ() * thePickResult.Depth() / myScale);
   gp_Vec aNormal (aPntOnSphere.XYZ() - theCenter.XYZ());
   thePickResult.SetPickedPoint (aPntOnSphere);
   thePickResult.SetSurfaceNormal (aNormal);
diff --git a/src/ViewerTest/ViewerTest_ViewerCommands.cxx b/src/ViewerTest/ViewerTest_ViewerCommands.cxx
index 43c6067ffd..534f07e332 100644
--- a/src/ViewerTest/ViewerTest_ViewerCommands.cxx
+++ b/src/ViewerTest/ViewerTest_ViewerCommands.cxx
@@ -10754,6 +10754,14 @@ static int VLight (Draw_Interpretor& theDi,
       const bool isZoomable = Draw::ParseOnOffIterator (theArgsNb, theArgVec, anArgIt);
       aLightPrs->SetZoomable (isZoomable);
     }
+    else if (!aLightPrs.IsNull()
+         && (anArgCase == "-showdraggable"
+          || anArgCase == "-prsdraggable"
+          || anArgCase == "-draggable"))
+    {
+      const bool isDraggable = Draw::ParseOnOffIterator (theArgsNb, theArgVec, anArgIt);
+      aLightPrs->SetDraggable (isDraggable);
+    }
     else if (!aLightPrs.IsNull()
           && (anArgCase == "-showname"
            || anArgCase == "-prsname"))
@@ -10786,6 +10794,23 @@ static int VLight (Draw_Interpretor& theDi,
 
       aLightPrs->SetSize (aSize);
     }
+    else if (!aLightPrs.IsNull()
+          && (anArgCase == "-dirarcsize"
+           || anArgCase == "-arcsize"
+           || anArgCase == "-arc")
+          && anArgIt + 1 < theArgsNb)
+    {
+      Standard_Integer aSize = 0;
+      if (!Draw::ParseInteger (theArgVec[anArgIt + 1], aSize)
+       || aSize <= 0
+       || aLightPrs->Light()->Type() != Graphic3d_TypeOfLightSource_Directional)
+      {
+        Message::SendFail() << "Syntax error at argument '" << anArg << "'";
+        return 1;
+      }
+      ++anArgIt;
+      aLightPrs->SetArcSize (aSize);
+    }
     else if (!aLightNew.IsNull()
           && aLightNew->Type() != Graphic3d_TypeOfLightSource_Ambient
           && (anArgCase == "-castshadow"
@@ -14887,6 +14912,7 @@ void ViewerTest::ViewerCommands(Draw_Interpretor& theCommands)
       "\n\t\t:   [-spotExponent value] [-spotAngle angleDeg]"
       "\n\t\t:   [-smoothAngle value] [-smoothRadius value]"
       "\n\t\t:   [-display] [-showName 1|0] [-showRange 1|0] [-prsZoomable 1|0] [-prsSize Value]"
+      "\n\t\t:   [-arcSize Value]"
       "\n\t\t: Command manages light sources. Without arguments shows list of lights."
       "\n\t\t: Arguments affecting the list of defined/active lights:"
       "\n\t\t:   -clear       remove all light sources"
@@ -14931,7 +14957,9 @@ void ViewerTest::ViewerCommands(Draw_Interpretor& theCommands)
       "\n\t\t:   -showName    shows/hides the name of light source; 1 by default"
       "\n\t\t:   -showRange   shows/hides the range of spot/positional light source; 1 by default"
       "\n\t\t:   -prsZoomable makes light presentation zoomable/non-zoomable"
+      "\n\t\t:   -prsDraggable makes light presentation draggable/non-draggable"
       "\n\t\t:   -prsSize     sets light presentation size"
+      "\n\t\t:   -arcSize     sets arc presentation size(in pixels) for rotation directional light source; 25 by default"
       "\n\t\t: Examples:"
       "\n\t\t:   vlight redlight -type POSITIONAL -headlight 1 -pos 0 1 1 -color RED"
       "\n\t\t:   vlight redlight -delete",
diff --git a/tests/v3d/light_source/dyn_highlight b/tests/v3d/light_source/dyn_highlight
new file mode 100644
index 0000000000..1af3e7e3da
--- /dev/null
+++ b/tests/v3d/light_source/dyn_highlight
@@ -0,0 +1,46 @@
+puts "================================="
+puts "0032183: Visualization - implement AIS_LightSource::ProcessDragging() for rotating directional light"
+puts "Tests dynamic highlighting of directional light source"
+puts "================================="
+
+pload MODELING VISUALIZATION
+vclear
+vinit View1 -height 480 -width 640
+vlight -clear
+vbackground -color GRAY
+vrenderparams -shadingModel PHONG
+box b 10 10 10 10 10 10
+vdisplay b -dispMode 1
+vaspects b -material Brass
+vfit
+
+vlight -add ambient -color WHITE -name AMBIENT -display
+vlight -add directional -dir 1 0 0 -name DIR1 -color BLUE -display
+vlight -add directional -dir 0 1 0 -name DIR2 -color RED -display -prsSize 200 -arcsize 50
+
+vmoveto 350 202
+vdump $imagedir/${casename}_dyn_highlighting_red_light_draggable.png
+if { ![string match "*Select3D_SensitiveSphere*" [vstate -entities]] } { puts "Error: sphere should be detected" }
+vmoveto 354 101
+if { ![string match "*Select3D_SensitiveSphere*" [vstate -entities]] } { puts "Error: sphere should be detected" }
+vmoveto 445 169
+if { ![string match "*Select3D_SensitiveSphere*" [vstate -entities]] } { puts "Error: sphere should be detected" }
+vmoveto 259 239
+if { ![string match "*Select3D_SensitiveSphere*" [vstate -entities]] } { puts "Error: sphere should be detected" }
+
+vmoveto 122 50
+vdump $imagedir/${casename}_dyn_highlighting_blue_light_draggable.png
+if { ![string match "*Select3D_SensitiveSphere*" [vstate -entities]] } { puts "Error: sphere should be detected" }
+vmoveto 126 25
+if { ![string match "*Select3D_SensitiveSphere*" [vstate -entities]] } { puts "Error: sphere should be detected" }
+vmoveto 149 46
+if { ![string match "*Select3D_SensitiveSphere*" [vstate -entities]] } { puts "Error: sphere should be detected" }
+vmoveto 101 47
+if { ![string match "*Select3D_SensitiveSphere*" [vstate -entities]] } { puts "Error: sphere should be detected" }
+
+vlight -change 2 -prsDraggable 0
+vmoveto 350 202
+vdump $imagedir/${casename}_dyn_highlighting_red_light_non_draggable.png
+vlight -change 1 -prsDraggable 0
+vmoveto 122 50
+vdump $imagedir/${casename}_dyn_highlighting_blue_light_non_draggable.png
\ No newline at end of file