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