Salome HOME
Merge V8_4_BR branch.
[modules/geom.git] / src / MeasureGUI / MeasureGUI_DimensionInteractor.cxx
1 // Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // GEOM GEOMGUI : GUI for Geometry component
24 // File   : MeasureGUI_DimensionInteractor.cxx
25 // Author : Anton POLETAEV, Open CASCADE S.A.S.
26
27 #include "MeasureGUI_DimensionInteractor.h"
28
29 #include <GEOM_Displayer.h>
30 #include <SalomeApp_Application.h>
31 #include <OCCViewer_ViewManager.h>
32 #include <OCCViewer_ViewWindow.h>
33 #include <OCCViewer_ViewPort3d.h>
34 #include <SUIT_ViewManager.h>
35 #include <SUIT_ViewWindow.h>
36 #include <SUIT_Desktop.h>
37
38 #include <QMouseEvent>
39
40 #include <AIS_InteractiveContext.hxx>
41 #include <AIS_LengthDimension.hxx>
42 #include <AIS_DiameterDimension.hxx>
43 #include <AIS_AngleDimension.hxx>
44 #include <Prs3d_DimensionAspect.hxx>
45 #include <Prs3d_TextAspect.hxx>
46 #include <gp_Dir.hxx>
47 #include <gp_Vec.hxx>
48 #include <gce_MakeDir.hxx>
49 #include <ElCLib.hxx>
50 #include <ElSLib.hxx>
51 #include <Extrema_ExtCC.hxx>
52 #include <Extrema_POnCurv.hxx>
53 #include <GeomAdaptor_Curve.hxx>
54 #include <GeomAPI_IntCS.hxx>
55 #include <Geom_Line.hxx>
56 #include <Geom_Plane.hxx>
57 #include <NCollection_Sequence.hxx>
58 #include <Quantity_Length.hxx>
59
60 //=================================================================================
61 // function : Constructor
62 // purpose  : 
63 //=================================================================================
64 MeasureGUI_DimensionInteractor::MeasureGUI_DimensionInteractor( GeometryGUI* theGUI,
65                                                                 QObject* theParent )
66 : QObject( theParent ),
67   myGeomGUI( theGUI ),
68   myIsEnabled( false ),
69   myVM( NULL ),
70   myViewer( NULL ),
71   myOperation( Operation_None )
72 {
73 }
74
75 //=================================================================================
76 // function : Deactivate
77 // purpose  : 
78 //=================================================================================
79 MeasureGUI_DimensionInteractor::~MeasureGUI_DimensionInteractor()
80 {
81   Disable();
82 }
83
84 //=================================================================================
85 // function : Enable
86 // purpose  : 
87 //=================================================================================
88 void MeasureGUI_DimensionInteractor::Enable()
89 {
90   if ( myIsEnabled )
91   {
92     return;
93   }
94
95   myIsEnabled = true;
96
97   // install event filtering on viewer windows
98   SalomeApp_Application* anApp = myGeomGUI->getApp();
99   if ( !anApp )
100   {
101     return;
102   }
103
104   myVM     = (OCCViewer_ViewManager*) anApp->getViewManager( OCCViewer_Viewer::Type(), false );
105   myViewer = (OCCViewer_Viewer*) myVM->getViewModel();
106   if ( !myVM || !myViewer )
107   {
108     return;
109   }
110
111   connect( myVM, SIGNAL( viewCreated( SUIT_ViewWindow* ) ), SLOT( OnViewCreated( SUIT_ViewWindow* ) ) );
112   connect( myVM, SIGNAL( deleteView ( SUIT_ViewWindow* ) ), SLOT( OnViewRemoved( SUIT_ViewWindow* ) ) );
113
114   QVector<SUIT_ViewWindow*>           aViews  = myVM->getViews();
115   QVector<SUIT_ViewWindow*>::iterator aViewIt = aViews.begin();
116   for ( ; aViewIt != aViews.end(); ++aViewIt )
117   {
118     ConnectView( *aViewIt );
119   }
120 }
121
122 //=================================================================================
123 // function : Disable
124 // purpose  : 
125 //=================================================================================
126 void MeasureGUI_DimensionInteractor::Disable()
127 {
128   if ( !myIsEnabled )
129   {
130     return;
131   }
132
133   myIsEnabled = false;
134
135   // remove event filtering from viewer windows
136   QVector<SUIT_ViewWindow*>           aViews  = myVM->getViews();
137   QVector<SUIT_ViewWindow*>::iterator aViewIt = aViews.begin();
138   for ( ; aViewIt != aViews.end(); ++aViewIt )
139   {
140     DisconnectView( *aViewIt );
141   }
142 }
143
144 //=================================================================================
145 // function : GetOperation
146 // purpose  : 
147 //=================================================================================
148 MeasureGUI_DimensionInteractor::Operation
149   MeasureGUI_DimensionInteractor::GetOperation( const Handle(SelectMgr_EntityOwner)& theEntity,
150                                                 const Qt::MouseButtons theButtons,
151                                                 const Qt::KeyboardModifiers theKeys )
152 {
153   if ( ( theButtons & Qt::LeftButton ) == 0 )
154   {
155     return Operation_None;
156   }
157
158   Handle(AIS_DimensionOwner) anOwner = Handle(AIS_DimensionOwner)::DownCast( theEntity );
159   if ( anOwner.IsNull() )
160   {
161     return Operation_None;
162   }
163
164   Standard_Real anAngTolerance = M_PI / 30.0; // 6 degree tolerance
165
166   switch ( anOwner->SelectionMode() )
167   {
168     case AIS_DSM_Line :
169     {
170       if ( ( theKeys & Qt::ControlModifier ) == 0 )
171       {
172         return Operation_MoveFlyoutInPlane;
173       }
174
175       // "free moving" is only available for length and diameter
176       if ( !myInteractedIO->IsKind( STANDARD_TYPE( AIS_LengthDimension ) )
177         && !myInteractedIO->IsKind( STANDARD_TYPE( AIS_DiameterDimension ) ) )
178       {
179         return Operation_None;
180       }
181
182       return Operation_MoveFlyoutFree;
183     }
184
185     case AIS_DSM_Text : return Operation_MoveText;
186     default :           return Operation_None;
187   }
188 }
189
190 //=================================================================================
191 // function : StartOperation
192 // purpose  : 
193 //=================================================================================
194 bool MeasureGUI_DimensionInteractor::StartOperation( const Operation theOp,
195                                                      const Handle(V3d_View)& theView,
196                                                      const int theX,
197                                                      const int theY )
198 {
199   Standard_Real anAngTolerance = M_PI / 30.0; // 6 degree tolerance
200
201   switch ( theOp )
202   {
203     case Operation_MoveFlyoutInPlane :
204     case Operation_MoveText :
205     {
206       // operation can be started if the plane in which the flyout 
207       // or text moved is not completely orthogonal to the screen
208       gp_Pln aPlane = myInteractedIO->GetPlane();
209       gp_Lin aProj  = Projection( theView, theX, theY );
210       Standard_Real aCrossAng = aProj.Direction().Angle( aPlane.Axis().Direction() );
211       return Abs( M_PI * 0.5 - aCrossAng ) > anAngTolerance;
212     }
213
214     case Operation_MoveFlyoutFree :
215     {
216       // operation can be started only for linear dimensions
217       if ( !myInteractedIO->IsKind( STANDARD_TYPE( AIS_LengthDimension ) )
218         && !myInteractedIO->IsKind( STANDARD_TYPE( AIS_DiameterDimension ) ) )
219       {
220         return false;
221       }
222
223       // determine whether the rotation operation is frontal or side
224       gp_Pnt aFirstPoint;
225       gp_Pnt aSecondPoint;
226
227       // get first and second point from which the flyout line is normally extended
228       Handle(AIS_LengthDimension) aLength = Handle(AIS_LengthDimension)::DownCast( myInteractedIO );
229       if ( !aLength.IsNull() )
230       {
231         aFirstPoint  = aLength->FirstPoint();
232         aSecondPoint = aLength->SecondPoint();
233       }
234
235       Handle(AIS_DiameterDimension) aDiameter = Handle(AIS_DiameterDimension)::DownCast( myInteractedIO );
236       if ( !aDiameter.IsNull() )
237       {
238         const gp_Pnt& aCenter = aDiameter->Circle().Location();
239         aFirstPoint    = aDiameter->AnchorPoint();
240         aSecondPoint   = gp_Pnt( aFirstPoint.XYZ() + gp_Vec( aFirstPoint, aCenter ).XYZ() * 2.0 );
241       }
242
243       gp_Dir aBaseDir = gce_MakeDir( aFirstPoint, aSecondPoint );
244       gp_Pln aPlane   = myInteractedIO->GetPlane();
245       gp_Dir aPlaneN  = aPlane.Axis().Direction();
246       gp_Lin aProj    = Projection( theView, theX, theY );
247
248       const gp_Dir& aProjDir = aProj.Direction();
249
250       // can turn only if looking from side
251       if ( aProjDir.IsParallel( aPlaneN, anAngTolerance ) ||
252            aProjDir.IsNormal( aBaseDir, anAngTolerance ) )
253       {
254         return false;
255       }
256
257       bool isProjected     = false;
258       gp_Pnt aPointOnPlane = ProjectPlane( theView, theX, theY, aPlane, isProjected );
259       if ( !isProjected )
260       {
261         return false;
262       }
263
264       gp_Vec aPointVector( aFirstPoint, aPointOnPlane );
265       gp_Pnt aPointOnBase = gp_Pnt( aFirstPoint.XYZ() + aBaseDir.XYZ() * aPointVector.Dot( aBaseDir ) );
266       myFreeMovePlane = gp_Pln( aPointOnBase, aBaseDir );
267
268       return true;
269     }
270
271     default:
272       return false;
273   }
274 }
275
276 //=================================================================================
277 // function : MoveFlyoutFree
278 // purpose  : 
279 //=================================================================================
280 void MeasureGUI_DimensionInteractor::MoveFlyoutFree( const Handle(V3d_View)& theView,
281                                                      const int theX,
282                                                      const int theY )
283 {
284   // project point onto dimension plane
285   bool isProjected     = false;
286   gp_Pln aPlane        = myFreeMovePlane;
287   gp_Pnt aPointOnPlane = ProjectPlane( theView, theX, theY, aPlane, isProjected );
288   if ( !isProjected )
289   {
290     return;
291   }
292
293   gp_Pnt aFirstPoint;
294   gp_Pnt aSecondPoint;
295
296   // get first and second point from which the flyout line is normally extended
297   Handle(AIS_LengthDimension) aLength = Handle(AIS_LengthDimension)::DownCast( myInteractedIO );
298   if ( !aLength.IsNull() )
299   {
300     aFirstPoint  = aLength->FirstPoint();
301     aSecondPoint = aLength->SecondPoint();
302   }
303
304   Handle(AIS_DiameterDimension) aDiameter = Handle(AIS_DiameterDimension)::DownCast( myInteractedIO );
305   if ( !aDiameter.IsNull() )
306   {
307     const gp_Pnt& aCenter = aDiameter->Circle().Location();
308     aFirstPoint    = aDiameter->AnchorPoint();
309     aSecondPoint   = gp_Pnt( aFirstPoint.XYZ() + gp_Vec( aFirstPoint, aCenter ).XYZ() * 2.0 );
310   }
311
312   gp_Vec aPointVector( aFirstPoint, aPointOnPlane );
313   gp_Dir aBaseDir     = gce_MakeDir( aFirstPoint, aSecondPoint );
314   gp_Pnt aPointOnBase = gp_Pnt( aFirstPoint.XYZ() + aBaseDir.XYZ() * aPointVector.Dot( aBaseDir ) );
315
316   // snapping tolerance
317   Quantity_Length aSize[2];
318   theView->Size( aSize[0], aSize[1] );
319   Standard_Real aSnapTolerance = 1e-2 * Max( aSize[0], aSize[1] );
320
321   gp_Dir aFlyoutDir = gce_MakeDir( aPointOnBase, aPointOnPlane );
322
323   // snapping to planes
324   NCollection_Sequence<gp_Pln> aSnapPlanes;
325
326   if ( aPointOnPlane.Distance( aPointOnBase ) > aSnapTolerance )
327   {
328     if ( !aBaseDir.IsParallel( gp::DZ(), Precision::Angular() ) )
329     {
330       aSnapPlanes.Append( gp_Pln( aFirstPoint, gp::DZ().Crossed( aBaseDir ) ) );
331     }
332     if ( !aBaseDir.IsParallel( gp::DX(), Precision::Angular() ) )
333     {
334       aSnapPlanes.Append( gp_Pln( aFirstPoint, gp::DX().Crossed( aBaseDir ) ) );
335     }
336     if ( !aBaseDir.IsParallel( gp::DY(), Precision::Angular() ) )
337     {
338       aSnapPlanes.Append( gp_Pln( aFirstPoint, gp::DY().Crossed( aBaseDir ) ) );
339     }
340   }
341
342   // snap "drag proj" to Flyout plane, relative XOY, YOZ, ZOX planes
343   NCollection_Sequence<gp_Pln>::Iterator aSnapIt( aSnapPlanes );
344
345   for ( ; aSnapIt.More(); aSnapIt.Next() )
346   {
347     const gp_Pln& aSnapPln = aSnapIt.Value();
348
349     // project direction into plane
350     gp_Vec aPlaneDir = gp_Vec( aSnapPln.Axis().Direction() );
351     gp_Vec aFlyoutProj = gp_Vec( aFlyoutDir ) - aPlaneDir * gp_Vec( aFlyoutDir ).Dot( aPlaneDir );
352
353     // snapping is not applicable
354     if ( aSnapPln.Contains( gp_Lin( aFirstPoint, aFlyoutDir ), Precision::Confusion(), Precision::Angular() ) )
355     {
356       continue;
357     }
358     if ( Abs( gp_Vec( aPointOnBase, aPointOnPlane ).Dot( aPlaneDir ) ) > aSnapTolerance )
359     {
360       continue;
361     }
362     if ( aFlyoutProj.Magnitude() <= Precision::Confusion() )
363     {
364       continue;
365     }
366
367     aFlyoutDir = gp_Dir( aFlyoutProj );
368
369     break;
370   }
371   
372   Standard_Real aNewFlyout = aPointOnPlane.Distance( aPointOnBase );
373
374   if ( aNewFlyout <= Precision::Confusion() )
375   {
376     myInteractedIO->SetFlyout( 0.0 );
377     myViewer->getAISContext()->Redisplay( myInteractedIO, Standard_True );
378     return;
379   }
380
381   gp_Pln aNewPlane = gp_Pln( aFirstPoint, aBaseDir.Crossed( aFlyoutDir ) );
382
383   myInteractedIO->SetFlyout( aNewFlyout );
384   myInteractedIO->SetCustomPlane( aNewPlane );
385
386   myViewer->getAISContext()->Redisplay( myInteractedIO, Standard_True );
387 }
388
389 //=================================================================================
390 // function : MoveFlyoutInPlane
391 // purpose  : 
392 //=================================================================================
393 void MeasureGUI_DimensionInteractor::MoveFlyoutInPlane( const Handle(V3d_View)& theView,
394                                                         const int theX,
395                                                         const int theY )
396 {
397   // project point onto dimension plane
398   bool isProjected     = false;
399   gp_Pln aPlane        = myInteractedIO->GetPlane();
400   gp_Pnt aPointOnPlane = ProjectPlane( theView, theX, theY, aPlane, isProjected );
401   if ( !isProjected )
402   {
403     return;
404   }
405
406   const gp_Dir& aPlaneN = aPlane.Axis().Direction();
407
408   // new flyout value
409   Standard_Real aFlyout = 0.0;
410
411   // extend flyout of linear dimension
412   if ( myInteractedIO->IsKind( STANDARD_TYPE( AIS_LengthDimension ) )
413     || myInteractedIO->IsKind( STANDARD_TYPE( AIS_DiameterDimension ) ) )
414   {
415     gp_Pnt aFirstPoint;
416     gp_Pnt aSecondPoint;
417
418     // get first and second point from which the flyout line is normally extended
419     Handle(AIS_LengthDimension) aLength = Handle(AIS_LengthDimension)::DownCast( myInteractedIO );
420     if ( !aLength.IsNull() )
421     {
422       aFirstPoint  = aLength->FirstPoint();
423       aSecondPoint = aLength->SecondPoint();
424     }
425
426     Handle(AIS_DiameterDimension) aDiameter = Handle(AIS_DiameterDimension)::DownCast( myInteractedIO );
427     if ( !aDiameter.IsNull() )
428     {
429       const gp_Pnt& aCenter = aDiameter->Circle().Location();
430       aFirstPoint    = aDiameter->AnchorPoint();
431       aSecondPoint   = gp_Pnt( aFirstPoint.XYZ() + gp_Vec( aFirstPoint, aCenter ).XYZ() * 2.0 );
432     }
433
434     gp_Lin aBaseLine( aFirstPoint, gce_MakeDir( aFirstPoint, aSecondPoint ) );
435
436     // check if the negative value is expected
437     gp_Dir aPositiveDir = aPlaneN.Crossed( aBaseLine.Direction() );
438     gp_Dir aPointDir    = gce_MakeDir( aBaseLine.Location(), aPointOnPlane );
439     Standard_Boolean isPositive = aPointDir.Dot( aPositiveDir ) > 0;
440
441     aFlyout = isPositive 
442       ?  aBaseLine.Distance( aPointOnPlane )
443       : -aBaseLine.Distance( aPointOnPlane );
444   }
445
446   // exten flyout of angular dimension
447   if ( myInteractedIO->IsKind( STANDARD_TYPE( AIS_AngleDimension ) ) )
448   {
449     Handle(AIS_AngleDimension) anAngle = Handle(AIS_AngleDimension)::DownCast( myInteractedIO );
450
451     const gp_Pnt& aCenterPoint = anAngle->CenterPoint();
452     const gp_Pnt& aFirstPoint  = anAngle->FirstPoint();
453     const gp_Pnt& aSecondPoint = anAngle->SecondPoint();
454
455     gp_Dir aFirstDir  = gce_MakeDir( aCenterPoint, aFirstPoint );
456     gp_Dir aSecondDir = gce_MakeDir( aCenterPoint, aSecondPoint );
457
458     Standard_Real aDirAngle = aFirstDir.Angle( aSecondDir );
459
460     //                 p1 center p2
461     //                   \ dir /
462     // the direction are: \ | /
463     //            first    \|/   second
464     //             dir      c      dir
465     // 
466
467     gp_Dir aCenterDir = aFirstDir.Rotated( gp_Ax1( aCenterPoint, aPlaneN ), -aDirAngle * 0.5 );
468
469     gp_Dir aPointDir  = gce_MakeDir( aCenterPoint, aPointOnPlane );
470
471     Standard_Boolean isPositive = aPointDir.Dot( aCenterDir ) > 0;
472
473     Standard_Real aPointAngle = aPointDir.AngleWithRef(  aCenterDir,  aPlaneN );
474     if ( !isPositive )
475     {
476       aPointAngle = ( M_PI - Abs( aPointAngle ) ) * ( -Sign( 1.0, aPointAngle ) );
477     }
478
479     gp_Vec aPointVec = gp_Vec( aCenterPoint, aPointOnPlane );
480
481     // calculate flyout for each separate case of point location
482     if ( aPointAngle < -aDirAngle * 0.5 ) // outside of second dir
483     {
484       aFlyout = aPointVec.Dot( aFirstDir );
485     }
486     else if ( aPointAngle > aDirAngle * 0.5 ) // outside of first dir
487     {
488       aFlyout = aPointVec.Dot( aSecondDir );
489     }
490     else // between first and second direction
491     {
492       aFlyout = isPositive
493         ?  aPointVec.Magnitude()
494         : -aPointVec.Magnitude();
495     }
496   }
497
498   myInteractedIO->SetFlyout( aFlyout );
499
500   myViewer->getAISContext()->Redisplay( myInteractedIO, Standard_True );
501 }
502
503 //=================================================================================
504 // function : MoveText
505 // purpose  : 
506 //=================================================================================
507 void MeasureGUI_DimensionInteractor::MoveText( const Handle(V3d_View)& theView,
508                                                const int theX,
509                                                const int theY )
510 {
511   // project point onto dimension plane
512   bool isProjected     = false;
513   gp_Pln aPlane        = myInteractedIO->GetPlane();
514   gp_Pnt aPointOnPlane = ProjectPlane( theView, theX, theY, aPlane, isProjected );
515   if ( !isProjected )
516   {
517     return;
518   }
519
520   const gp_Dir& aPlaneN = aPlane.Axis().Direction();
521
522   Prs3d_DimensionTextHorizontalPosition aHPos   = myInteractedIO->DimensionAspect()->TextHorizontalPosition();
523   Prs3d_DimensionTextVerticalPosition   aVPos   = myInteractedIO->DimensionAspect()->TextVerticalPosition();
524   Prs3d_DimensionArrowOrientation       aArrPos = myInteractedIO->DimensionAspect()->ArrowOrientation();
525
526   Standard_Real aHeight = myInteractedIO->DimensionAspect()->TextAspect()->Height() * 0.5;
527
528   // move text of linear dimension
529   if ( myInteractedIO->IsKind( STANDARD_TYPE( AIS_LengthDimension ) )
530     || myInteractedIO->IsKind( STANDARD_TYPE( AIS_DiameterDimension ) ) )
531   {
532     gp_Pnt aFirstPoint;
533     gp_Pnt aSecondPoint;
534
535     // get first and second point from which the flyout line is normally extended
536     Handle(AIS_LengthDimension) aLength = Handle(AIS_LengthDimension)::DownCast( myInteractedIO );
537     if ( !aLength.IsNull() )
538     {
539       aFirstPoint  = aLength->FirstPoint();
540       aSecondPoint = aLength->SecondPoint();
541     }
542
543     Handle(AIS_DiameterDimension) aDiameter = Handle(AIS_DiameterDimension)::DownCast( myInteractedIO );
544     if ( !aDiameter.IsNull() )
545     {
546       const gp_Pnt& aCenter = aDiameter->Circle().Location();
547       aFirstPoint    = aDiameter->AnchorPoint();
548       aSecondPoint   = gp_Pnt( aFirstPoint.XYZ() + gp_Vec( aFirstPoint, aCenter ).XYZ() * 2.0 );
549     }
550
551     // get flyout line
552     Standard_Real aFlyout   = myInteractedIO->GetFlyout();
553     gp_Dir aBaseDir         = gce_MakeDir( aFirstPoint, aSecondPoint );
554     gp_Dir aFlyoutDir       = aFlyout >= 0.0 
555       ?  aPlaneN.Crossed( gce_MakeDir( aFirstPoint, aSecondPoint ) )
556       : -aPlaneN.Crossed( gce_MakeDir( aFirstPoint, aSecondPoint ) );
557     gp_Vec aFlyoutTranslate = gp_Vec( aFlyoutDir ) * Abs( myInteractedIO->GetFlyout() );
558     gp_Lin aFlyoutLine      = gp_Lin( aFirstPoint, aBaseDir ).Translated( aFlyoutTranslate );
559
560     // check if positive or negative offset along flyout line
561     gp_Vec aFlyout2Point = gp_Vec( aFlyoutLine.Location(), aPointOnPlane );
562
563     Standard_Real aPosY = aFlyout2Point.Dot( aFlyoutDir );
564     Standard_Real aPosX = aFlyout2Point.Dot( aFlyoutLine.Direction() );
565
566     if ( aPosX < 0.0 )
567     {
568       aHPos   = Prs3d_DTHP_Left;
569       aArrPos = Prs3d_DAO_External;
570     }
571     else if ( aPosX > aFirstPoint.Distance( aSecondPoint ) )
572     {
573       aHPos   = Prs3d_DTHP_Right;
574       aArrPos = Prs3d_DAO_External;
575     }
576     else
577     {
578       aHPos   = Prs3d_DTHP_Center;
579       aArrPos = Prs3d_DAO_Fit;
580     }
581
582     if ( aPosY > aHeight )
583     {
584       aVPos = Prs3d_DTVP_Above;
585     }
586     else if ( aPosY < -aHeight )
587     {
588       aVPos = Prs3d_DTVP_Below;
589     }
590     else
591     {
592       aVPos = Prs3d_DTVP_Center;
593     }
594   }
595
596   // move text of angular dimension
597   if ( myInteractedIO->IsKind( STANDARD_TYPE( AIS_AngleDimension ) ) )
598   {
599     Handle(AIS_AngleDimension) anAngle = Handle(AIS_AngleDimension)::DownCast( myInteractedIO );
600
601     const gp_Pnt& aCenterPoint = anAngle->CenterPoint();
602     const gp_Pnt& aFirstPoint  = anAngle->FirstPoint();
603     const gp_Pnt& aSecondPoint = anAngle->SecondPoint();
604
605     Standard_Boolean isPositive = anAngle->GetFlyout() > 0.0;
606
607     gp_Dir aFirstDir  = gce_MakeDir( aCenterPoint, aFirstPoint );
608     gp_Dir aSecondDir = gce_MakeDir( aCenterPoint, aSecondPoint );
609     if ( anAngle->GetFlyout() < 0.0 )
610     {
611       aFirstDir.Reverse();
612       aSecondDir.Reverse();
613     }
614
615     Standard_Real aDirAngle = aFirstDir.Angle( aSecondDir );
616
617     //                 p1 center p2
618     //                   \ dir /
619     // the direction are: \ | /
620     //            first    \|/   second
621     //             dir      c      dir
622
623     gp_Dir aCenterDir = aFirstDir.Rotated( gp_Ax1( aCenterPoint, aPlaneN ), -aDirAngle * 0.5 );
624
625     gp_Dir aPointDir = gce_MakeDir( aCenterPoint, aPointOnPlane );
626
627     Standard_Real aPointAngle = aPointDir.AngleWithRef( aCenterDir, aPlaneN );
628
629     gp_Vec aPointVec = gp_Vec( aCenterPoint, aPointOnPlane );
630
631     Standard_Real aPosY = 0.0;
632
633     // calculate flyout for each separate case of point location
634     if ( aPointAngle < -aDirAngle * 0.5 ) // outside of second dir
635     {
636       aHPos   = Prs3d_DTHP_Left;
637       aArrPos = Prs3d_DAO_External;
638       aPosY   = aPointVec.Dot( aFirstDir ) - Abs( anAngle->GetFlyout() );
639     }
640     else if ( aPointAngle > aDirAngle * 0.5 ) // outside of first dir
641     {
642       aHPos   = Prs3d_DTHP_Right;
643       aArrPos = Prs3d_DAO_External;
644       aPosY   = aPointVec.Dot( aSecondDir ) - Abs( anAngle->GetFlyout() );
645     }
646     else // between first and second direction
647     {
648       aHPos   = Prs3d_DTHP_Center;
649       aArrPos = Prs3d_DAO_Fit;
650       aPosY   = aPointVec.Magnitude() - Abs( anAngle->GetFlyout() );
651     }
652
653     if ( aPosY > aHeight )
654     {
655       aVPos = Prs3d_DTVP_Above;
656     }
657     else if ( aPosY < -aHeight )
658     {
659       aVPos = Prs3d_DTVP_Below;
660     }
661     else
662     {
663       aVPos = Prs3d_DTVP_Center;
664     }
665   }
666
667   myInteractedIO->DimensionAspect()->SetTextVerticalPosition( aVPos );
668   myInteractedIO->DimensionAspect()->SetTextHorizontalPosition( aHPos );
669   myInteractedIO->DimensionAspect()->SetArrowOrientation( aArrPos );
670   myInteractedIO->SetToUpdate();
671
672   myViewer->getAISContext()->Redisplay( myInteractedIO, Standard_True );
673 }
674
675 //=================================================================================
676 // function : eventFilter
677 // purpose  : 
678 //=================================================================================
679 bool MeasureGUI_DimensionInteractor::eventFilter( QObject* theObject, QEvent* theEvent )
680 {
681   OCCViewer_ViewPort3d* aViewPort = (OCCViewer_ViewPort3d*) theObject;
682
683   Handle(V3d_View) aView3d = aViewPort->getView();
684
685   switch ( theEvent->type() )
686   {
687     // check whether it is the "dimension modify" operation or not.
688     case QEvent::MouseButtonPress :
689     {
690       Handle(AIS_InteractiveContext) anAISContext = myViewer->getAISContext();
691
692       QMouseEvent* aMouseEv = dynamic_cast<QMouseEvent*>( theEvent );
693
694       // init detection
695       anAISContext->MoveTo( aMouseEv->x(), aMouseEv->y(), aView3d, Standard_True );
696       if ( !anAISContext->HasDetected() )
697       {
698         return false;
699       }
700
701       // commented by mpa 18.03.2015: since OCCT version 6.8.0 it's impossible
702       // to change position of the dimensions presentations (flyout, text),
703       // because anAISContext has 2 detected objects.
704
705       // check that there is only one detected entity
706       //anAISContext->InitDetected();
707       //if ( anAISContext->MoreDetected() )
708       //{
709       //  return false;
710       //}
711
712       Handle(SelectMgr_EntityOwner) aDetectedOwner = anAISContext->DetectedOwner();
713       if( aDetectedOwner.IsNull() )
714       {
715         return false;
716       }
717
718       myInteractedIO = Handle(AIS_Dimension)::DownCast( aDetectedOwner->Selectable() );
719
720       // try to start operation for the detected entity
721       Operation aStartOp = GetOperation( aDetectedOwner, aMouseEv->buttons(), aMouseEv->modifiers() );
722       if ( aStartOp == Operation_None )
723       {
724         return false;
725       }
726
727       if ( !StartOperation( aStartOp, aView3d, aMouseEv->x(), aMouseEv->y() ) )
728       {
729         return false;
730       }
731
732       myOperation = aStartOp;
733
734       mySelection.Clear();
735
736       for ( anAISContext->InitSelected(); anAISContext->MoreSelected(); anAISContext->NextSelected() )
737       {
738         mySelection.Append( anAISContext->SelectedOwner() );
739       }
740
741       anAISContext->ClearSelected( Standard_False );
742       anAISContext->AddOrRemoveSelected( aDetectedOwner, Standard_True );
743
744       emit InteractionStarted( myInteractedIO );
745
746       return true;
747     }
748
749     // stop event processing on mouse release
750     case QEvent::MouseButtonRelease :
751     {
752       if ( myOperation != Operation_None )
753       {
754         Handle(AIS_InteractiveContext) anAISContext = myViewer->getAISContext();
755
756         anAISContext->ClearSelected( Standard_False );
757         SeqOfOwners::Iterator anIt( mySelection );
758         for( ; anIt.More(); anIt.Next() )
759         {
760           anAISContext->AddOrRemoveSelected( anIt.Value(), Standard_False );
761         }
762
763         anAISContext->UpdateCurrentViewer();
764
765         mySelection.Clear();
766
767         myOperation = Operation_None;
768
769         emit InteractionFinished( myInteractedIO );
770
771         return true;
772       }
773
774       return false;
775     }
776
777     // perform operation
778     case QEvent::MouseMove :
779     {
780       QMouseEvent* aMouseEv = (QMouseEvent*) theEvent;
781       switch( myOperation )
782       {
783         case Operation_MoveFlyoutFree    : MoveFlyoutFree   ( aView3d, aMouseEv->x(), aMouseEv->y() ); return true;
784         case Operation_MoveFlyoutInPlane : MoveFlyoutInPlane( aView3d, aMouseEv->x(), aMouseEv->y() ); return true;
785         case Operation_MoveText          : MoveText         ( aView3d, aMouseEv->x(), aMouseEv->y() ); return true;
786         default : return false;
787       }
788     }
789
790     default: return false;
791   }
792 }
793
794 //=================================================================================
795 // function : ConnectView
796 // purpose  : 
797 //=================================================================================
798 void MeasureGUI_DimensionInteractor::ConnectView( SUIT_ViewWindow* theView )
799 {
800   ( (OCCViewer_ViewWindow*) theView )->getViewPort()->installEventFilter( this );
801 }
802
803 //=================================================================================
804 // function : DisconnectView
805 // purpose  : 
806 //=================================================================================
807 void MeasureGUI_DimensionInteractor::DisconnectView( SUIT_ViewWindow* theView )
808 {
809   ( (OCCViewer_ViewWindow*) theView )->getViewPort()->removeEventFilter( this );
810 }
811
812 //=================================================================================
813 // function : Projection
814 // purpose  : 
815 //=================================================================================
816 gp_Lin MeasureGUI_DimensionInteractor::Projection( const Handle(V3d_View)& theView,
817                                                    const int theMouseX,
818                                                    const int theMouseY )
819 {
820   Standard_Real P[3], D[3];
821   theView->ConvertWithProj( theMouseX, theMouseY, P[0], P[1], P[2], D[0], D[1], D[2] );
822   return gp_Lin( gp_Pnt( P[0], P[1], P[2] ), gp_Dir( D[0], D[1], D[2] ) );
823 }
824
825 //=================================================================================
826 // function : ProjectPlane
827 // purpose  : 
828 //=================================================================================
829 gp_Pnt MeasureGUI_DimensionInteractor::ProjectPlane( const Handle(V3d_View)& theView,
830                                                      const int theMouseX,
831                                                      const int theMouseY,
832                                                      const gp_Pln& thePlane,
833                                                      bool& theIsDone )
834 {
835   gp_Lin aProjection = Projection( theView, theMouseX, theMouseY );
836
837   Handle(Geom_Line)  aCrossLine  = new Geom_Line( aProjection );
838   Handle(Geom_Plane) aCrossPlane = new Geom_Plane( thePlane );
839
840   GeomAPI_IntCS aFindCross( aCrossLine, aCrossPlane );
841   if ( !aFindCross.IsDone() || aFindCross.NbPoints() == 0 )
842   {
843     theIsDone = false;
844     return gp::Origin();
845   }
846
847   theIsDone = true;
848   return aFindCross.Point( 1 );
849 }
850
851 //=================================================================================
852 // function : SensitivityTolerance
853 // purpose  : 
854 //=================================================================================
855 Standard_Real MeasureGUI_DimensionInteractor::SensitivityTolerance( const Handle(V3d_View)& theView )
856 {
857   // snapping tolerance
858   Quantity_Length aSize[2];
859   theView->Size( aSize[0], aSize[1] );
860   return 1e-2 * Max( aSize[0], aSize[1] );
861 }
862
863 //=================================================================================
864 // function : OnViewCreated
865 // purpose  : 
866 //=================================================================================
867 void MeasureGUI_DimensionInteractor::OnViewCreated( SUIT_ViewWindow* theView )
868 {
869   ConnectView( theView );
870 }
871
872 //=================================================================================
873 // function : OnViewRemoved
874 // purpose  : 
875 //=================================================================================
876 void MeasureGUI_DimensionInteractor::OnViewRemoved( SUIT_ViewWindow* theView )
877 {
878   DisconnectView( theView );
879 }