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