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