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