]> SALOME platform Git repositories - modules/geom.git/blob - src/MeasureGUI/MeasureGUI_DimensionInteractor.cxx
Salome HOME
Merge from BR_imps_2013 14/01/2014
[modules/geom.git] / src / MeasureGUI / MeasureGUI_DimensionInteractor.cxx
1 // Copyright (C) 2007-2013  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.
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
524   Standard_Real aHeight = myInteractedIO->DimensionAspect()->TextAspect()->Height() * 0.5;
525
526   // move text of linear dimension
527   if ( myInteractedIO->IsKind( STANDARD_TYPE( AIS_LengthDimension ) )
528     || myInteractedIO->IsKind( STANDARD_TYPE( AIS_DiameterDimension ) ) )
529   {
530     gp_Pnt aFirstPoint;
531     gp_Pnt aSecondPoint;
532
533     // get first and second point from which the flyout line is normally extended
534     Handle(AIS_LengthDimension) aLength = Handle(AIS_LengthDimension)::DownCast( myInteractedIO );
535     if ( !aLength.IsNull() )
536     {
537       aFirstPoint  = aLength->FirstPoint();
538       aSecondPoint = aLength->SecondPoint();
539     }
540
541     Handle(AIS_DiameterDimension) aDiameter = Handle(AIS_DiameterDimension)::DownCast( myInteractedIO );
542     if ( !aDiameter.IsNull() )
543     {
544       const gp_Pnt& aCenter = aDiameter->Circle().Location();
545       aFirstPoint    = aDiameter->AnchorPoint();
546       aSecondPoint   = gp_Pnt( aFirstPoint.XYZ() + gp_Vec( aFirstPoint, aCenter ).XYZ() * 2.0 );
547     }
548
549     // get flyout line
550     Standard_Real aFlyout   = myInteractedIO->GetFlyout();
551     gp_Dir aBaseDir         = gce_MakeDir( aFirstPoint, aSecondPoint );
552     gp_Dir aFlyoutDir       = aFlyout >= 0.0 
553       ?  aPlaneN.Crossed( gce_MakeDir( aFirstPoint, aSecondPoint ) )
554       : -aPlaneN.Crossed( gce_MakeDir( aFirstPoint, aSecondPoint ) );
555     gp_Vec aFlyoutTranslate = gp_Vec( aFlyoutDir ) * Abs( myInteractedIO->GetFlyout() );
556     gp_Lin aFlyoutLine      = gp_Lin( aFirstPoint, aBaseDir ).Translated( aFlyoutTranslate );
557
558     // check if positive or negative offset along flyout line
559     gp_Vec aFlyout2Point = gp_Vec( aFlyoutLine.Location(), aPointOnPlane );
560
561     Standard_Real aPosY = aFlyout2Point.Dot( aFlyoutDir );
562     Standard_Real aPosX = aFlyout2Point.Dot( aFlyoutLine.Direction() );
563
564     if ( aPosX < 0.0 )
565     {
566       aHPos = Prs3d_DTHP_Left;
567     }
568     else if ( aPosX > aFirstPoint.Distance( aSecondPoint ) )
569     {
570       aHPos = Prs3d_DTHP_Right;
571     }
572     else
573     {
574       aHPos = Prs3d_DTHP_Center;
575     }
576
577     if ( aPosY > aHeight )
578     {
579       aVPos = Prs3d_DTVP_Above;
580     }
581     else if ( aPosY < -aHeight )
582     {
583       aVPos = Prs3d_DTVP_Below;
584     }
585     else
586     {
587       aVPos = Prs3d_DTVP_Center;
588     }
589   }
590
591   // move text of angular dimension
592   if ( myInteractedIO->IsKind( STANDARD_TYPE( AIS_AngleDimension ) ) )
593   {
594     Handle(AIS_AngleDimension) anAngle = Handle(AIS_AngleDimension)::DownCast( myInteractedIO );
595
596     const gp_Pnt& aCenterPoint = anAngle->CenterPoint();
597     const gp_Pnt& aFirstPoint  = anAngle->FirstPoint();
598     const gp_Pnt& aSecondPoint = anAngle->SecondPoint();
599
600     gp_Dir aFirstDir  = gce_MakeDir( aCenterPoint, aFirstPoint );
601     gp_Dir aSecondDir = gce_MakeDir( aCenterPoint, aSecondPoint );
602
603     Standard_Real aDirAngle = aFirstDir.Angle( aSecondDir );
604
605     //                 p1 center p2
606     //                   \ dir /
607     // the direction are: \ | /
608     //            first    \|/   second
609     //             dir      c      dir
610     // 
611
612     gp_Dir aCenterDir = aFirstDir.Rotated( gp_Ax1( aCenterPoint, aPlaneN ), -aDirAngle * 0.5 );
613
614     gp_Dir aPointDir  = gce_MakeDir( aCenterPoint, aPointOnPlane );
615
616     Standard_Boolean isPositive = aPointDir.Dot( aCenterDir ) > 0;
617
618     Standard_Real aPointAngle = aPointDir.AngleWithRef(  aCenterDir,  aPlaneN );
619     if ( !isPositive )
620     {
621       aPointAngle = ( M_PI - Abs( aPointAngle ) ) * ( -Sign( 1.0, aPointAngle ) );
622     }
623
624     gp_Vec aPointVec = gp_Vec( aCenterPoint, aPointOnPlane );
625
626     Standard_Real aPosY = 0.0;
627
628     // the point should lie within the dimension flyout area
629     if (  isPositive && aPointVec.Dot( aFirstDir ) < 0.0
630       || !isPositive && aPointVec.Dot( aFirstDir ) > 0.0 )
631     {
632       return;
633     }
634
635     // calculate flyout for each separate case of point location
636     if ( aPointAngle < -aDirAngle * 0.5 ) // outside of second dir
637     {
638       aHPos = Prs3d_DTHP_Left;
639       aPosY = Abs( aPointVec.Dot( aFirstDir ) ) - Abs( myInteractedIO->GetFlyout() );
640     }
641     else if ( aPointAngle > aDirAngle * 0.5 ) // outside of first dir
642     {
643       aHPos = Prs3d_DTHP_Right;
644       aPosY = Abs( aPointVec.Dot( aSecondDir ) ) - Abs( myInteractedIO->GetFlyout() );
645     }
646     else // between first and second direction
647     {
648       aHPos = Prs3d_DTHP_Center;
649       aPosY = Abs( aPointVec.Magnitude() ) - Abs( myInteractedIO->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->SetToUpdate();
669
670   myViewer->getAISContext()->Redisplay( myInteractedIO );
671 }
672
673 //=================================================================================
674 // function : eventFilter
675 // purpose  : 
676 //=================================================================================
677 bool MeasureGUI_DimensionInteractor::eventFilter( QObject* theObject, QEvent* theEvent )
678 {
679   OCCViewer_ViewPort3d* aViewPort = (OCCViewer_ViewPort3d*) theObject;
680
681   Handle(V3d_View) aView3d = aViewPort->getView();
682
683   switch ( theEvent->type() )
684   {
685     // check whether it is the "dimension modify" operation or not.
686     case QEvent::MouseButtonPress :
687     {
688       Handle(AIS_InteractiveContext) anAISContext = myViewer->getAISContext();
689
690       QMouseEvent* aMouseEv = dynamic_cast<QMouseEvent*>( theEvent );
691
692       // init detection
693       anAISContext->MoveTo( aMouseEv->x(), aMouseEv->y(), aView3d );
694       if ( !anAISContext->HasDetected() )
695       {
696         return false;
697       }
698
699       // check that there is only one detected entity
700       anAISContext->InitDetected();
701       if ( anAISContext->MoreDetected() )
702       {
703         return false;
704       }
705
706       Handle(SelectMgr_EntityOwner) aDetectedOwner = anAISContext->DetectedOwner();
707
708       myInteractedIO = Handle(AIS_Dimension)::DownCast( aDetectedOwner->Selectable() );
709
710       // try to start operation for the detected entity
711       Operation aStartOp = GetOperation( aDetectedOwner, aMouseEv->buttons(), aMouseEv->modifiers() );
712       if ( aStartOp == Operation_None )
713       {
714         return false;
715       }
716
717       if ( !StartOperation( aStartOp, aView3d, aMouseEv->x(), aMouseEv->y() ) )
718       {
719         return false;
720       }
721
722       myOperation = aStartOp;
723
724       mySelection.Clear();
725
726       for ( anAISContext->InitSelected(); anAISContext->MoreSelected(); anAISContext->NextSelected() )
727       {
728         mySelection.Append( anAISContext->SelectedOwner() );
729       }
730
731       anAISContext->ClearSelected( Standard_False );
732       anAISContext->AddOrRemoveSelected( aDetectedOwner );
733
734       emit InteractionStarted( myInteractedIO );
735
736       return true;
737     }
738
739     // stop event processing on mouse release
740     case QEvent::MouseButtonRelease :
741     {
742       if ( myOperation != Operation_None )
743       {
744         Handle(AIS_InteractiveContext) anAISContext = myViewer->getAISContext();
745
746         anAISContext->ClearSelected( Standard_False );
747         SeqOfOwners::Iterator anIt( mySelection );
748         for( ; anIt.More(); anIt.Next() )
749         {
750           anAISContext->AddOrRemoveSelected( anIt.Value(), Standard_False );
751         }
752
753         anAISContext->UpdateCurrentViewer();
754
755         mySelection.Clear();
756
757         myOperation = Operation_None;
758
759         emit InteractionFinished( myInteractedIO );
760
761         return true;
762       }
763
764       return false;
765     }
766
767     // perform operation
768     case QEvent::MouseMove :
769     {
770       QMouseEvent* aMouseEv = (QMouseEvent*) theEvent;
771       switch( myOperation )
772       {
773         case Operation_MoveFlyoutFree    : MoveFlyoutFree   ( aView3d, aMouseEv->x(), aMouseEv->y() ); return true;
774         case Operation_MoveFlyoutInPlane : MoveFlyoutInPlane( aView3d, aMouseEv->x(), aMouseEv->y() ); return true;
775         case Operation_MoveText          : MoveText         ( aView3d, aMouseEv->x(), aMouseEv->y() ); return true;
776         default : return false;
777       }
778     }
779
780     default: return false;
781   }
782 }
783
784 //=================================================================================
785 // function : ConnectView
786 // purpose  : 
787 //=================================================================================
788 void MeasureGUI_DimensionInteractor::ConnectView( SUIT_ViewWindow* theView )
789 {
790   ( (OCCViewer_ViewWindow*) theView )->getViewPort()->installEventFilter( this );
791 }
792
793 //=================================================================================
794 // function : DisconnectView
795 // purpose  : 
796 //=================================================================================
797 void MeasureGUI_DimensionInteractor::DisconnectView( SUIT_ViewWindow* theView )
798 {
799   ( (OCCViewer_ViewWindow*) theView )->getViewPort()->removeEventFilter( this );
800 }
801
802 //=================================================================================
803 // function : Projection
804 // purpose  : 
805 //=================================================================================
806 gp_Lin MeasureGUI_DimensionInteractor::Projection( const Handle(V3d_View)& theView,
807                                                    const int theMouseX,
808                                                    const int theMouseY )
809 {
810   Standard_Real P[3], D[3];
811   theView->ConvertWithProj( theMouseX, theMouseY, P[0], P[1], P[2], D[0], D[1], D[2] );
812   return gp_Lin( gp_Pnt( P[0], P[1], P[2] ), gp_Dir( D[0], D[1], D[2] ) );
813 }
814
815 //=================================================================================
816 // function : ProjectPlane
817 // purpose  : 
818 //=================================================================================
819 gp_Pnt MeasureGUI_DimensionInteractor::ProjectPlane( const Handle(V3d_View)& theView,
820                                                      const int theMouseX,
821                                                      const int theMouseY,
822                                                      const gp_Pln& thePlane,
823                                                      bool& theIsDone )
824 {
825   gp_Lin aProjection = Projection( theView, theMouseX, theMouseY );
826
827   Handle(Geom_Line)  aCrossLine  = new Geom_Line( aProjection );
828   Handle(Geom_Plane) aCrossPlane = new Geom_Plane( thePlane );
829
830   GeomAPI_IntCS aFindCross( aCrossLine, aCrossPlane );
831   if ( !aFindCross.IsDone() || aFindCross.NbPoints() == 0 )
832   {
833     theIsDone = false;
834     return gp::Origin();
835   }
836
837   theIsDone = true;
838   return aFindCross.Point( 1 );
839 }
840
841 //=================================================================================
842 // function : SensitivityTolerance
843 // purpose  : 
844 //=================================================================================
845 Standard_Real MeasureGUI_DimensionInteractor::SensitivityTolerance( const Handle(V3d_View)& theView )
846 {
847   // snapping tolerance
848   Quantity_Length aSize[2];
849   theView->Size( aSize[0], aSize[1] );
850   return 1e-2 * Max( aSize[0], aSize[1] );
851 }
852
853 //=================================================================================
854 // function : OnViewCreated
855 // purpose  : 
856 //=================================================================================
857 void MeasureGUI_DimensionInteractor::OnViewCreated( SUIT_ViewWindow* theView )
858 {
859   ConnectView( theView );
860 }
861
862 //=================================================================================
863 // function : OnViewRemoved
864 // purpose  : 
865 //=================================================================================
866 void MeasureGUI_DimensionInteractor::OnViewRemoved( SUIT_ViewWindow* theView )
867 {
868   DisconnectView( theView );
869 }