Salome HOME
b7d9b88a7464dd6ad73774cc6a6d0405cd6964a6
[modules/geom.git] / src / MeasureGUI / MeasureGUI_DimensionCreateTool.cxx
1 // Copyright (C) 2007-2023  CEA, EDF, 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_DimensionCreateTool.cxx
25 // Author : Anton POLETAEV, Open CASCADE S.A.S.
26
27 #include "MeasureGUI_DimensionCreateTool.h"
28
29 // GEOM includes
30 #include <GEOMBase.h>
31
32 // GUI includes
33 #include <SalomeApp_Application.h>
34
35 // SUIT includes
36 #include <OCCViewer_ViewModel.h>
37 #include <OCCViewer_ViewManager.h>
38 #include <OCCViewer_ViewWindow.h>
39 #include <OCCViewer_ViewPort3d.h>
40
41 // OCCT includes
42 #include <Adaptor3d_CurveOnSurface.hxx>
43 #include <BRep_Tool.hxx>
44 #include <BRepTools.hxx>
45 #include <BRepAdaptor_Curve.hxx>
46 #include <BRepAdaptor_Surface.hxx>
47 #include <BRepBndLib.hxx>
48 #include <ElCLib.hxx>
49 #include <gp_Pnt.hxx>
50 #include <gp_Circ.hxx>
51 #include <gp_Sphere.hxx>
52 #include <gp_Cone.hxx>
53 #include <gp_Torus.hxx>
54 #include <gce_MakeDir.hxx>
55 #include <gce_MakePln.hxx>
56 #include <gce_MakeCirc.hxx>
57 #include <GC_MakePlane.hxx>
58 #include <Geom_Circle.hxx>
59 #include <Geom_Plane.hxx>
60 #include <Geom_ElementarySurface.hxx>
61 #include <Geom_Surface.hxx>
62 #include <Geom_ConicalSurface.hxx>
63 #include <Geom_SphericalSurface.hxx>
64 #include <Geom_ToroidalSurface.hxx>
65 #include <Geom_TrimmedCurve.hxx>
66 #include <GeomLib.hxx>
67 #include <GeomLib_Tool.hxx>
68 #include <TopoDS_Shape.hxx>
69 #include <TopoDS_Vertex.hxx>
70 #include <TopoDS_Edge.hxx>
71 #include <TopExp.hxx>
72 #include <TopExp_Explorer.hxx>
73 #include <TopTools_IndexedDataMapOfShapeListOfShape.hxx>
74 #include <TopTools_ListOfShape.hxx>
75 #include <TopTools_ListIteratorOfListOfShape.hxx>
76 #include <TColgp_SequenceOfDir.hxx>
77 #include <V3d_View.hxx>
78
79 // plane associated with custom data
80 struct PlaneAndSegment
81 {
82   PlaneAndSegment() {}
83   PlaneAndSegment(const gp_Pln& thePlane, const MeasureGUI_DimensionCreateTool::Segment& theSegment) : pln(thePlane), seg(theSegment) {}
84   operator gp_Pln () const { return pln; }
85   operator MeasureGUI_DimensionCreateTool::Segment () const { return seg; }
86   gp_Pln pln;
87   MeasureGUI_DimensionCreateTool::Segment seg;
88 };
89
90 typedef NCollection_Sequence<PlaneAndSegment> SeqOfPlnsAndSegments;
91
92 //=================================================================================
93 // function : Constructor
94 // purpose  :
95 //=================================================================================
96 MeasureGUI_DimensionCreateTool::MeasureGUI_DimensionCreateTool()
97 {
98   Settings.DefaultFlyout = 0.0;
99   Settings.ActiveView = NULL;
100 }
101
102 //=================================================================================
103 // function : LengthOnEdge
104 // purpose  :
105 //=================================================================================
106 Handle(AIS_LengthDimension) MeasureGUI_DimensionCreateTool::LengthOnEdge( const GEOM::GeomObjPtr& theMeasuredObj ) const
107 {
108   /* ---------------------------------------------------------------- *
109    *                  get the edge and parent shape                   *
110    * ---------------------------------------------------------------- */
111
112   TopoDS_Shape aMeasuredShape;
113   TopoDS_Shape aMainShape;
114   if ( !GEOMBase::GetShape( theMeasuredObj.operator ->(), aMeasuredShape ) )
115   {
116     return NULL;
117   }
118
119   if ( !GEOMBase::GetShape( GetMainShape( theMeasuredObj ).get(), aMainShape ) )
120   {
121     return NULL;
122   }
123
124   /* ------------------------------------------------- */
125   /*            check the input geometry               */
126   /* ------------------------------------------------- */
127
128   TopoDS_Edge anEdge = TopoDS::Edge( aMeasuredShape );
129
130   TopoDS_Vertex aVertex1;
131   TopoDS_Vertex aVertex2;
132   TopExp::Vertices( anEdge, aVertex1, aVertex2 );
133
134   gp_Pnt aPnt1 = BRep_Tool::Pnt( aVertex1 );
135   gp_Pnt aPnt2 = BRep_Tool::Pnt( aVertex2 );
136   if ( aPnt1.Distance( aPnt2 ) <= Precision::Confusion() )
137   {
138     return NULL;
139   }
140
141   /* ------------------------- *
142    *   position the dimension 
143    * ------------------------- */
144
145   Bnd_Box aBnd;
146   BRepBndLib::AddClose( aMainShape, aBnd );
147
148   // get face sides
149   TopTools_IndexedDataMapOfShapeListOfShape aRelationMap;
150   TopExp::MapShapesAndAncestors( aMainShape, TopAbs_EDGE, TopAbs_FACE, aRelationMap );
151   const TopTools_ListOfShape& aRelatedFaces = aRelationMap.FindFromKey( anEdge );
152
153   gp_Vec aFaceN1( gp::Origin(), gp::Origin() );
154   gp_Vec aFaceN2( gp::Origin(), gp::Origin() );
155   gp_Vec aFaceS1( gp::Origin(), gp::Origin() );
156   gp_Vec aFaceS2( gp::Origin(), gp::Origin() );
157
158   gp_Pnt aMiddlePnt = gp_Pnt( ( aPnt1.XYZ() + aPnt2.XYZ() ) * 0.5 );
159
160   TopTools_ListIteratorOfListOfShape aFaceIt( aRelatedFaces );
161
162   // get face side directions
163   if ( aFaceIt.More() )
164   {
165     TopoDS_Face aFace = TopoDS::Face( aFaceIt.Value() );
166
167     gp_Dir aSideDir;
168     if ( GetFaceSide( aFace, anEdge, aSideDir ) )
169     {
170       aFaceS1 = aSideDir;
171     }
172
173     Handle(Geom_Surface) aSurface = BRep_Tool::Surface( aFace );
174
175     Standard_Real aU = 0.0, aV = 0.0;
176     GeomLib_Tool::Parameters( aSurface, aMiddlePnt, Precision::Confusion(), aU, aV );
177
178     gp_Dir aNorm;
179     if ( GeomLib::NormEstim( aSurface, gp_Pnt2d( aU, aV ), Precision::Confusion(), aNorm ) <= 1 )
180     {
181       aFaceN1 = aFace.Orientation() == TopAbs_REVERSED ? -aNorm : aNorm;
182     }
183
184     aFaceIt.Next();
185   }
186
187   if ( aFaceIt.More() )
188   {
189     TopoDS_Face aFace = TopoDS::Face( aFaceIt.Value() );
190
191     gp_Dir aSideDir;
192     if ( GetFaceSide( aFace, anEdge, aSideDir ) )
193     {
194       aFaceS2 = aSideDir;
195     }
196
197     Handle(Geom_Surface) aSurface = BRep_Tool::Surface( aFace );
198
199     Standard_Real aU = 0.0, aV = 0.0;
200     GeomLib_Tool::Parameters( aSurface, aMiddlePnt, Precision::Confusion(), aU, aV );
201
202     gp_Dir aNorm;
203     if ( GeomLib::NormEstim( aSurface, gp_Pnt2d( aU, aV ), Precision::Confusion(), aNorm ) <= 1 )
204     {
205       aFaceN2 = aFace.Orientation() == TopAbs_REVERSED ? -aNorm : aNorm;
206     }
207   }
208
209   gp_Pln aPln;
210   PositionLength( aBnd, aFaceN1, aFaceN2, aFaceS1, aFaceS2, aPnt1, aPnt2, aPln );
211
212   /* --------------------------------------------------------- *
213    *   construct the dimension for the best selected position
214    * --------------------------------------------------------- */
215
216   Handle(AIS_LengthDimension) aDimension = new AIS_LengthDimension( anEdge, aPln );
217
218   aDimension->SetFlyout( Settings.DefaultFlyout );
219
220   if ( !aDimension->IsValid() )
221   {
222     return NULL;
223   }
224
225   return aDimension;
226 }
227
228 //=================================================================================
229 // function : LengthByPoints
230 // purpose  :
231 //=================================================================================
232 Handle(AIS_LengthDimension) MeasureGUI_DimensionCreateTool::LengthByPoints( const GEOM::GeomObjPtr& theMeasuredObj1,
233                                                                             const GEOM::GeomObjPtr& theMeasuredObj2 ) const
234 {
235   /* ---------------------------------------------------------------- *
236    *                  get the edge and parent shape                   *
237    * ---------------------------------------------------------------- */
238
239   TopoDS_Shape aMeasuredShape1;
240   TopoDS_Shape aMeasuredShape2;
241   TopoDS_Shape aMainShape;
242
243   if ( !GEOMBase::GetShape( theMeasuredObj1.operator ->(), aMeasuredShape1 ) )
244   {
245     return NULL;
246   }
247
248   if ( !GEOMBase::GetShape( theMeasuredObj2.operator ->(), aMeasuredShape2 ) )
249   {
250     return NULL;
251   }
252
253   if ( !GEOMBase::GetShape( GetMainShape( theMeasuredObj1 ).get(), aMainShape ) )
254   {
255     return NULL;
256   }
257
258   /* ------------------------------------------------- */
259   /*            check the input geometry               */
260   /* ------------------------------------------------- */
261
262   TopoDS_Vertex aVertex1 = TopoDS::Vertex( aMeasuredShape1 );
263   TopoDS_Vertex aVertex2 = TopoDS::Vertex( aMeasuredShape2 );
264
265   gp_Pnt aPnt1 = BRep_Tool::Pnt( aVertex1 );
266   gp_Pnt aPnt2 = BRep_Tool::Pnt( aVertex2 );
267   if ( aPnt1.Distance( aPnt2 ) <= Precision::Confusion() )
268   {
269     return NULL;
270   }
271
272   /* ------------------------- *
273    *   position the dimension 
274    * ------------------------- */
275
276   Bnd_Box aBnd;
277   BRepBndLib::AddClose( aMainShape, aBnd );
278
279   // check whether the points share same edge
280   TopExp_Explorer anEdgeExp( aMainShape, TopAbs_EDGE, TopAbs_EDGE );
281   for ( ; anEdgeExp.More(); anEdgeExp.Next() )
282   {
283     TopoDS_Vertex anEdgeV1;
284     TopoDS_Vertex anEdgeV2;
285     TopExp::Vertices( TopoDS::Edge( anEdgeExp.Current() ), anEdgeV1, anEdgeV2 );
286     gp_Pnt anEdgePnt1 = BRep_Tool::Pnt( anEdgeV1 );
287     gp_Pnt anEdgePnt2 = BRep_Tool::Pnt( anEdgeV2 );
288
289     if ( aPnt1.Distance( anEdgePnt1 ) <= Precision::Confusion() )
290     {
291       if ( aPnt2.Distance( anEdgePnt2 ) <= Precision::Confusion() )
292       {
293         break;
294       }
295     }
296
297     if ( aPnt2.Distance( anEdgePnt1 ) <= Precision::Confusion() )
298     {
299       if ( aPnt1.Distance( anEdgePnt2 ) <= Precision::Confusion() )
300       {
301         break;
302       }
303     }
304   }
305
306   gp_Vec aFaceN1( gp::Origin(), gp::Origin() );
307   gp_Vec aFaceN2( gp::Origin(), gp::Origin() );
308   gp_Vec aFaceS1( gp::Origin(), gp::Origin() );
309   gp_Vec aFaceS2( gp::Origin(), gp::Origin() );
310
311   // have shared edge
312   if ( anEdgeExp.More() )
313   {
314     TopoDS_Edge anEdge = TopoDS::Edge( anEdgeExp.Current() );
315     TopTools_IndexedDataMapOfShapeListOfShape aRelationMap;
316     TopExp::MapShapesAndAncestors( aMainShape, TopAbs_EDGE, TopAbs_FACE, aRelationMap );
317     const TopTools_ListOfShape& aRelatedFaces = aRelationMap.FindFromKey( anEdge );
318
319     gp_Pnt aMiddlePnt = gp_Pnt( ( aPnt1.XYZ() + aPnt2.XYZ() ) * 0.5 );
320
321     TopTools_ListIteratorOfListOfShape aFaceIt( aRelatedFaces );
322
323     // get face side directions
324     if ( aFaceIt.More() )
325     {
326       TopoDS_Face aFace = TopoDS::Face( aFaceIt.Value() );
327
328       gp_Dir aSideDir;
329       if ( GetFaceSide( aFace, anEdge, aSideDir ) )
330       {
331         aFaceS1 = aSideDir;
332       }
333
334       Handle(Geom_Surface) aSurface = BRep_Tool::Surface( aFace );
335
336       Standard_Real aU = 0.0, aV = 0.0;
337       GeomLib_Tool::Parameters( aSurface, aMiddlePnt, Precision::Confusion(), aU, aV );
338
339       gp_Dir aNorm;
340       if ( GeomLib::NormEstim( aSurface, gp_Pnt2d( aU, aV ), Precision::Confusion(), aNorm ) <= 1 )
341       {
342         aFaceN1 = aFace.Orientation() == TopAbs_REVERSED ? -aNorm : aNorm;
343       }
344
345       aFaceIt.Next();
346     }
347
348     if ( aFaceIt.More() )
349     {
350       TopoDS_Face aFace = TopoDS::Face( aFaceIt.Value() );
351
352       gp_Dir aSideDir;
353       if ( GetFaceSide( aFace, anEdge, aSideDir ) )
354       {
355         aFaceS2 = aSideDir;
356       }
357
358       Handle(Geom_Surface) aSurface = BRep_Tool::Surface( aFace );
359
360       Standard_Real aU = 0.0, aV = 0.0;
361       GeomLib_Tool::Parameters( aSurface, aMiddlePnt, Precision::Confusion(), aU, aV );
362
363       gp_Dir aNorm;
364       if ( GeomLib::NormEstim( aSurface, gp_Pnt2d( aU, aV ), Precision::Confusion(), aNorm ) <= 1 )
365       {
366         aFaceN2 = aFace.Orientation() == TopAbs_REVERSED ? -aNorm : aNorm;
367       }
368     }
369   }
370
371   gp_Pln aPln;
372   PositionLength( aBnd, aFaceN1, aFaceN2, aFaceS1, aFaceS2, aPnt1, aPnt2, aPln );
373
374   /* --------------------------------------------------------- *
375    *   construct the dimension for the best selected position
376    * --------------------------------------------------------- */
377
378   Handle(AIS_LengthDimension) aDimension = new AIS_LengthDimension( aPnt1, aPnt2, aPln );
379
380   aDimension->SetFlyout( Settings.DefaultFlyout );
381
382   if ( !aDimension->IsValid() )
383   {
384     return NULL;
385   }
386
387   return aDimension;
388 }
389
390 //=================================================================================
391 // function : LengthByParallelEdges
392 // purpose  :
393 //=================================================================================
394 Handle(AIS_LengthDimension) MeasureGUI_DimensionCreateTool::LengthByParallelEdges( const GEOM::GeomObjPtr& theEdge1,
395                                                                                    const GEOM::GeomObjPtr& theEdge2 ) const
396 {
397   TopoDS_Shape aFirstSh;
398   if ( !GEOMBase::GetShape( theEdge1.operator ->(), aFirstSh ) )
399   {
400     return NULL;
401   }
402
403   TopoDS_Shape aSecondSh;
404   if ( !GEOMBase::GetShape( theEdge2.operator ->(), aSecondSh ) )
405   {
406     return NULL;
407   }
408
409   if( aFirstSh == aSecondSh )
410     return NULL;
411
412   TopoDS_Edge aFirstEdge  = TopoDS::Edge( aFirstSh );
413   TopoDS_Edge aSecondEdge = TopoDS::Edge( aSecondSh );
414
415   // Build plane through three points
416   BRepAdaptor_Curve aCurve1( aFirstEdge );
417   BRepAdaptor_Curve aCurve2( aSecondEdge );
418
419   gp_Pnt aPnt1 = aCurve1.Value( 0.1 );
420   gp_Pnt aPnt2 = aCurve1.Value( 0.9 );
421   gp_Pnt aPnt3 = aCurve2.Value( 0.5 );
422
423   GC_MakePlane aMkPlane( aPnt1, aPnt2, aPnt3 );
424   Handle(Geom_Plane) aPlane = aMkPlane.Value();
425
426   // check whether it is possible to compute valid dimension
427   Handle(AIS_LengthDimension) aDimension = new AIS_LengthDimension ( aFirstEdge, aSecondEdge, aPlane->Pln() );
428
429   aDimension->SetFlyout( Settings.DefaultFlyout );
430
431   if ( !aDimension->IsValid() )
432   {
433     return NULL;
434   }
435
436   return aDimension;
437 }
438
439 //=================================================================================
440 // function : Diameter
441 // purpose  :
442 //=================================================================================
443 Handle(AIS_DiameterDimension) MeasureGUI_DimensionCreateTool::Diameter( const GEOM::GeomObjPtr& theMeasuredObj ) const
444 {
445   /* ------------------------------------------------ *
446    *     get the shape and its parent (if exist)      *
447    * ------------------------------------------------ */
448
449   TopoDS_Shape aMeasuredShape;
450   TopoDS_Shape aMainShape;
451   if ( !GEOMBase::GetShape( theMeasuredObj.operator ->(), aMeasuredShape ) )
452   {
453     return NULL;
454   }
455
456   if ( !GEOMBase::GetShape( GetMainShape( theMeasuredObj ).get(), aMainShape ) )
457   {
458     return NULL;
459   }
460
461   Bnd_Box aBnd;
462   BRepBndLib::AddClose( aMainShape, aBnd );
463
464   /* ------------------------------------------------ *
465    *    get the dimension construction arguments      *
466    * ------------------------------------------------ */
467
468   Handle(Geom_Circle) aCircle;
469
470   Standard_Real aPmin = 0, aPmax = 2 * M_PI;
471
472   gp_Vec aFaceN( gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(0.0, 0.0, 0.0) );
473
474   switch ( aMeasuredShape.ShapeType() )
475   {
476     case TopAbs_FACE:
477     {
478       TopoDS_Face aMeasuredFace = TopoDS::Face(aMeasuredShape);
479
480       BRepAdaptor_Surface aSurf( aMeasuredFace );
481
482       Standard_Real aUmin = aSurf.FirstUParameter();
483       Standard_Real aUmax = aSurf.LastUParameter();
484       Standard_Real aVmin = aSurf.FirstVParameter();
485       Standard_Real aVmax = aSurf.LastVParameter();
486
487       // get arguments of sphere (the sphere should not be cutted at v-center)
488       if ( aSurf.GetType() == GeomAbs_Sphere )
489       {
490         if ( aVmax <= Precision::PConfusion() || aVmin >= Precision::PConfusion() )
491         {
492           return NULL;
493         }
494
495         Handle(Geom_Surface) aBasisSurface = Handle(Geom_Surface)::DownCast(
496           aSurf.Surface().Surface()->Transformed( aSurf.Trsf() ) );
497
498         Handle(Geom_Curve) aCurve = aBasisSurface->VIso( 0.0 );
499
500         if ( aCurve->IsKind( STANDARD_TYPE( Geom_Circle ) ) )
501         {
502           aPmin = aUmin;
503           aPmax = aUmax;
504           aCircle = Handle(Geom_Circle)::DownCast( aCurve );
505         }
506         else if (  aCurve->IsKind( STANDARD_TYPE( Geom_TrimmedCurve ) ) )
507         {
508           Handle(Geom_TrimmedCurve) aTrimmedCurve = Handle(Geom_TrimmedCurve)::DownCast( aCurve );
509           aPmin = aTrimmedCurve->FirstParameter();
510           aPmax = aTrimmedCurve->LastParameter();
511
512           aCircle = Handle(Geom_Circle)::DownCast( aTrimmedCurve ); // todo: useless downcast: aCircle always NULL
513         }
514         break;
515       }
516
517       // get arguments of cone
518       if ( aSurf.GetType() == GeomAbs_Cone )
519       {
520         aPmin = aUmin;
521         aPmax = aUmax;
522
523         gp_Cone aCone = aSurf.Cone();
524         gp_Ax2 anAx2 = aCone.Position().Ax2();
525         aCircle = new Geom_Circle( anAx2, aCone.RefRadius() );
526
527         aFaceN = aCone.SemiAngle() > 0.0 
528           ?  anAx2.Axis().Direction()
529           : -anAx2.Axis().Direction();
530         break;
531       }
532
533       // get arguments of closed torus or cylinder
534       if ( aSurf.GetType() == GeomAbs_Torus || aSurf.GetType() == GeomAbs_Cylinder )
535       {
536         Handle(Geom_Surface) aBasisSurface = Handle(Geom_Surface)::DownCast(
537           aSurf.Surface().Surface()->Transformed( aSurf.Trsf() ) );
538
539         Handle(Geom_Curve) aCurve = aBasisSurface->VIso( (aVmax + aVmin) * 0.5 );
540
541         if ( aCurve->IsKind( STANDARD_TYPE( Geom_Circle ) ) )
542         {
543           aPmin = aUmin;
544           aPmax = aUmax;
545           aCircle = Handle(Geom_Circle)::DownCast( aCurve );
546         }
547         else if (  aCurve->IsKind( STANDARD_TYPE( Geom_TrimmedCurve ) ) )
548         {
549           Handle(Geom_TrimmedCurve) aTrimmedCurve = Handle(Geom_TrimmedCurve)::DownCast( aCurve );
550           aPmin = aTrimmedCurve->FirstParameter();
551           aPmax = aTrimmedCurve->LastParameter();
552
553           aCircle = Handle(Geom_Circle)::DownCast( aTrimmedCurve ); // todo: useless downcast: aCircle always NULL
554         }
555
556         break;
557       }
558
559       // face containing edge?
560       TopExp_Explorer anExp( aMeasuredShape, TopAbs_EDGE );
561       if ( !anExp.More() )
562       {
563         return NULL;
564       }
565
566       TopoDS_Shape anExpEdge = anExp.Current();
567       if ( anExpEdge.IsNull() )
568       {
569         return NULL;
570       }
571
572       // only a single edge is expected
573       anExp.Next();
574       if ( anExp.More() )
575       {
576         return NULL;
577       }
578
579       // do not break, go to edge checking
580       aMeasuredShape = anExpEdge;
581     }
582     // fall through!
583
584     case TopAbs_EDGE:
585     {
586       TopoDS_Edge aMeasureEdge = TopoDS::Edge( aMeasuredShape );
587
588       BRepAdaptor_Curve aCurve(aMeasureEdge);
589
590       if ( aCurve.GetType() != GeomAbs_Circle )
591       {
592         return NULL;
593       }
594
595       aPmin = aCurve.FirstParameter();
596       aPmax = aCurve.LastParameter();
597
598       aCircle = new Geom_Circle( aCurve.Circle() );
599
600       // check if there is an parent face containing the edge
601       TopTools_IndexedDataMapOfShapeListOfShape aShapeMap;
602       TopExp::MapShapesAndAncestors( aMainShape, TopAbs_EDGE, TopAbs_FACE, aShapeMap );
603       const TopTools_ListOfShape& aFaces = aShapeMap.FindFromKey( aMeasureEdge );
604
605       TopTools_ListIteratorOfListOfShape aFaceIt( aFaces );
606       for ( ; aFaceIt.More(); aFaceIt.Next() )
607       {
608         TopoDS_Face aFace = TopoDS::Face( aFaceIt.Value() );
609
610         Handle(Geom_Surface) aSurface = BRep_Tool::Surface( TopoDS::Face( aFace ) );
611
612         gp_Pnt aCircCenter = aCircle->Circ().Location();
613         Standard_Real aCircU = 0.0, aCircV = 0.0;
614         GeomLib_Tool::Parameters( aSurface, aCircCenter, Precision::Confusion(), aCircU, aCircV );
615
616         gp_Dir aNorm;
617         if ( GeomLib::NormEstim( aSurface, gp_Pnt2d( aCircU, aCircV ), Precision::Confusion(), aNorm ) > 1 )
618         {
619           break;
620         }
621
622         if ( aNorm.Angle( aCircle->Circ().Axis().Direction() ) > M_PI * 0.25 )
623         {
624           continue;
625         }
626
627         aFaceN = gp_Vec( aFace.Orientation() == TopAbs_REVERSED ? -aNorm : aNorm );
628       }
629     }
630     break;
631   default:
632     break;
633   }
634
635   if ( aCircle.IsNull() )
636   {
637     return NULL;
638   }
639
640   ElCLib::AdjustPeriodic( 0.0, M_PI * 2, Precision::PConfusion(), aPmin, aPmax );
641
642   /* ------------------------- *
643    *   position the dimension 
644    * ------------------------- */
645
646   gp_Pnt aPnt1;
647   gp_Pnt aPnt2;
648   gp_Pln aPln;
649
650   // diameter for closed circle
651   if ( Abs( ( aPmax - aPmin ) - M_PI * 2 ) <= Precision::PConfusion() )
652   {
653     PositionDiameter( aBnd, aFaceN, aCircle->Circ(), aPnt1, aPnt2, aPln );
654   }
655   // diameter for half-closed circle
656   else if ( Abs( aPmax - aPmin ) > M_PI )
657   {
658     Standard_Real anAnchor = aPmin + ( ( aPmax - aPmin ) - M_PI ) * 0.5;
659
660     PositionDiameter( aBnd, aFaceN, aCircle->Circ(), anAnchor, aPln );
661
662     aPnt1 = ElCLib::Value( anAnchor, aCircle->Circ() );
663     aPnt2 = ElCLib::Value( anAnchor + M_PI, aCircle->Circ() );
664   }
665   // diameter for less than half-closed circle
666   else
667   {
668     Standard_Real anAnchor = aPmin + ( aPmax - aPmin ) * 0.5;
669
670     PositionDiameter( aBnd, aFaceN, aCircle->Circ(), anAnchor, aPln );
671
672     aPnt1 = ElCLib::Value( anAnchor, aCircle->Circ() );
673     aPnt2 = ElCLib::Value( anAnchor + M_PI, aCircle->Circ() );
674   }
675
676   /* --------------------------------------------------------- *
677    *   construct the dimension for the best selected position
678    * --------------------------------------------------------- */
679
680   gp_Pnt aCircP = aCircle->Circ().Location();
681   gp_Dir aCircN = aCircle->Circ().Axis().Direction();
682   gp_Dir aCircX = gce_MakeDir( aPnt1, aPnt2 );
683   Standard_Real aCircR = aCircle->Circ().Radius();
684
685   // construct closed circle as base for the diameter dimension
686   Standard_Boolean isReversed = ( ( aPln.Axis().Direction() ^ aCircX ) * aCircN ) < 0.0;
687
688   gp_Circ aRuledCirc = gce_MakeCirc( gp_Ax2( aCircP, isReversed ? -aCircN : aCircN, aCircX ), aCircR );
689
690   Handle(AIS_DiameterDimension) aDimension = new AIS_DiameterDimension( aRuledCirc, aPln );
691
692   // if flyout is extended in tangent direction to circle, the default flyout value is used
693   // if flyout is extended in plane of circle, the zero flyout value is chosen initially
694   Standard_Real aFlyout = aCircN.IsParallel( aPln.Axis().Direction(), Precision::Angular() ) ? 0.0 : Settings.DefaultFlyout;
695
696   aDimension->SetFlyout(aFlyout);
697
698   if ( !aDimension->IsValid() )
699   {
700     return NULL;
701   }
702
703   return aDimension;
704 }
705
706 //=================================================================================
707 // function : AngleByTwoEdges
708 // purpose  :
709 //=================================================================================
710 Handle(AIS_AngleDimension) MeasureGUI_DimensionCreateTool::AngleByTwoEdges( const GEOM::GeomObjPtr& theEdge1,
711                                                                             const GEOM::GeomObjPtr& theEdge2 ) const
712 {
713   /* --------------------------------------------------- */
714   /*         get construction and parent shapes          */
715   /* --------------------------------------------------- */
716
717   TopoDS_Shape aShapeEdge1;
718   TopoDS_Shape aShapeMain1;
719   if ( !GEOMBase::GetShape( theEdge1.get(), aShapeEdge1 ) )
720   {
721     return NULL;
722   }
723   if ( !GEOMBase::GetShape( GetMainShape( theEdge1 ).get(), aShapeMain1 ) )
724   {
725     return NULL;
726   }
727
728   TopoDS_Shape aShapeEdge2;
729   TopoDS_Shape aShapeMain2;
730   if ( !GEOMBase::GetShape( theEdge2.get(), aShapeEdge2 ) )
731   {
732     return NULL;
733   }
734   if ( !GEOMBase::GetShape( GetMainShape( theEdge2 ).get(), aShapeMain2 ) )
735   {
736     return NULL;
737   }
738
739   /* ---------------------------------------------------- */
740   /*             check construction edges                 */
741   /* ---------------------------------------------------- */
742
743   TopoDS_Edge aFirstEdge  = TopoDS::Edge( aShapeEdge1 );
744   TopoDS_Edge aSecondEdge = TopoDS::Edge( aShapeEdge2 );
745
746   // check whether it is possible to compute dimension on the passed edges
747   Handle(AIS_AngleDimension) aDimension = new AIS_AngleDimension( aFirstEdge, aSecondEdge );
748
749   if ( !aDimension->IsValid() )
750   {
751     return NULL;
752   }
753
754   const gp_Pnt& aFirstPoint  = aDimension->FirstPoint();
755   const gp_Pnt& aSecondPoint = aDimension->SecondPoint();
756   const gp_Pnt& aCenterPoint = aDimension->CenterPoint();
757
758   gp_Vec aVec1( aCenterPoint, aFirstPoint );
759   gp_Vec aVec2( aCenterPoint, aSecondPoint );
760
761   Standard_Real anAngle = aVec2.AngleWithRef( aVec1, aDimension->GetPlane().Axis().Direction() );
762
763   if ( anAngle < 0.0 )
764   {
765     aDimension = new AIS_AngleDimension( aSecondPoint, aCenterPoint, aFirstPoint );
766   }
767
768   aDimension->SetFlyout( Settings.DefaultFlyout );
769
770   return aDimension;
771 }
772
773 //=================================================================================
774 // function : AngleByThreePoints
775 // purpose  :
776 //=================================================================================
777 Handle(AIS_AngleDimension) MeasureGUI_DimensionCreateTool::AngleByThreePoints( const GEOM::GeomObjPtr& thePoint1,
778                                                                                const GEOM::GeomObjPtr& thePoint2,
779                                                                                const GEOM::GeomObjPtr& thePoint3 ) const
780 {
781   TopoDS_Shape aFirstSh;
782   if ( !GEOMBase::GetShape( thePoint1.operator ->(), aFirstSh ) )
783   {
784     return NULL;
785   }
786
787   TopoDS_Shape aSecondSh;
788   if ( !GEOMBase::GetShape( thePoint2.operator ->(), aSecondSh ) )
789   {
790     return NULL;
791   }
792
793   TopoDS_Shape aThirdSh;
794   if ( !GEOMBase::GetShape( thePoint3.operator ->(), aThirdSh ) )
795   {
796     return NULL;
797   }
798
799   TopoDS_Vertex aFirstVertex  = TopoDS::Vertex( aFirstSh );
800   TopoDS_Vertex aSecondVertex = TopoDS::Vertex( aSecondSh );
801   TopoDS_Vertex aThirdVertex  = TopoDS::Vertex( aThirdSh );
802
803   gp_Pnt aPnt1 = BRep_Tool::Pnt( aFirstVertex );
804   gp_Pnt aPnt2 = BRep_Tool::Pnt( aSecondVertex );
805   gp_Pnt aPnt3 = BRep_Tool::Pnt( aThirdVertex );
806
807   // check whether it is possible to compute dimension on the passed points
808   Handle(AIS_AngleDimension) aDimension = new AIS_AngleDimension( aPnt1, aPnt2, aPnt3 );
809
810   if ( !aDimension->IsValid() )
811   {
812     return NULL;
813   }
814
815   aDimension->SetFlyout( Settings.DefaultFlyout );
816
817   return aDimension;
818 }
819
820 //=================================================================================
821 // function : PositionLength
822 // purpose  : The method provides preliminary positioning algorithm for
823 //            for length dimensions measuring the length between two points.
824 //            Parameters:
825 //              theBnd [in] - the bounding box of the main shape
826 //              theFaceN1 [in] - the normal to a first face of edge length (if any)
827 //              theFaceN2 [in] - the normal to a second face of edge length (if any)
828 //              theFaceS1 [in] - the side vector from a first face of edge length (if any)
829 //              theFaceS2 [in] - the side vector from a second face of edge length (if any)
830 //              thePnt1 [in] - the first measured point
831 //              thePnt2 [in] - the last measured point
832 //            The method selects flyout plane to best match the current
833 //            view projection. If edge length is constructed, then the flyout
834 //            can go in line with sides of faces, normal to the faces, or
835 //            aligned to XOY, YOZ, ZOX planes.
836 //=================================================================================
837 void MeasureGUI_DimensionCreateTool::PositionLength( const Bnd_Box& theBnd,
838                                                      const gp_Vec& theFaceN1,
839                                                      const gp_Vec& theFaceN2,
840                                                      const gp_Vec& theFaceS1,
841                                                      const gp_Vec& theFaceS2,
842                                                      const gp_Pnt& thePnt1,
843                                                      const gp_Pnt& thePnt2,
844                                                      gp_Pln& thePln ) const
845 {
846   Standard_Boolean isFace1 = theFaceN1.Magnitude() > Precision::Confusion();
847   Standard_Boolean isFace2 = theFaceN2.Magnitude() > Precision::Confusion();
848   gp_Vec anAverageN( gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(0.0, 0.0, 0.0) );
849
850   // get average direction in case of two non-sharp angled faces
851   if ( isFace1 && isFace2 )
852   {
853     Standard_Boolean isSame = theFaceN1.IsParallel( theFaceN2, Precision::Angular() );
854     if ( !isSame )
855     {
856       gp_Dir aReferenceDir = theFaceN1 ^ theFaceN2;
857       // compute angle between face sides [0 - 2PI]
858       Standard_Real aDirAngle = theFaceN1.AngleWithRef( theFaceN2, aReferenceDir );
859       if ( aDirAngle < 0 )
860       {
861         aDirAngle = ( M_PI * 2.0 ) - aDirAngle;
862       }
863
864       // non-sharp angle, use averaged directio
865       if ( aDirAngle > M_PI * 0.5 )
866       {
867         anAverageN = theFaceN1 + theFaceN2;
868       }
869
870       if ( aDirAngle > M_PI )
871       {
872         isFace1 = Standard_False;
873         isFace2 = Standard_False;
874       }
875     }
876   }
877
878   Standard_Boolean isAverage = anAverageN.Magnitude() > Precision::Confusion();
879
880   SeqOfDirs aFlyoutDirs;
881   if ( isFace1 )
882   {
883     aFlyoutDirs.Append( theFaceN1 );
884     aFlyoutDirs.Append( theFaceS1 );
885   }
886   if ( isFace2 )
887   {
888     aFlyoutDirs.Append( theFaceN2 );
889     aFlyoutDirs.Append( theFaceS2 );
890   }
891   if ( isAverage )
892   {
893     aFlyoutDirs.Append( anAverageN );
894   }
895
896   ChooseLengthFlyoutsFromBnd( aFlyoutDirs, thePnt1, thePnt2, theBnd );
897
898   if ( aFlyoutDirs.IsEmpty() )
899   {
900     return;
901   }
902
903   gp_Dir aPointDir = gce_MakeDir( thePnt1, thePnt2 );
904
905   // make planes for dimension presentation according to flyout directions
906   SeqOfPlanes aSeqOfPlanes;
907   for ( Standard_Integer aFlyoutIt = 1; aFlyoutIt <= aFlyoutDirs.Length(); ++aFlyoutIt )
908   {
909     gp_Pln aPlane( thePnt1, aPointDir ^ aFlyoutDirs.Value( aFlyoutIt ) );
910     aSeqOfPlanes.Append( aPlane );
911   }
912
913   Handle(V3d_View) aView = Settings.ActiveView;
914
915   thePln = !aView.IsNull()
916     ? SelectPlaneForProjection( aSeqOfPlanes, aView )
917     : aSeqOfPlanes.First();
918 }
919
920 //=================================================================================
921 // function : PositionDiameter
922 // purpose  : The method provides preliminary positioning algorithm for
923 //            for diameter dimensions measuring the circle.
924 //            Parameters:
925 //              theBnd [in] - the bounding box of the shape
926 //              theFaceN [in] - the circle face normal (can be void)
927 //              theCirc [in] - the measured circle
928 //              thePnt1 [out] - first dimension point
929 //              thePnt2 [out] - second dimension point
930 //              thePln [out] - dimension flyout plane
931 //            The method selects points on the circle for diameter dimension and
932 //            flyout plane to best match the current view projection (if any)
933 //            The points are aligned to XOY, YOZ, ZOX planes.
934 //            The flyout takes into account bounding box of main shape of face normal
935 //            vector. The flyouts tangential to the circle plane are directed in 
936 //            accordance with the face normal (if not-null), otherwise the flyouts
937 //            are turned to direct to the closest border of bounding box.
938 //=================================================================================
939 void MeasureGUI_DimensionCreateTool::PositionDiameter( const Bnd_Box& theBnd,
940                                                        const gp_Vec& theFaceN,
941                                                        const gp_Circ& theCirc,
942                                                        gp_Pnt& thePnt1,
943                                                        gp_Pnt& thePnt2,
944                                                        gp_Pln& thePln ) const
945 {
946   // select list of measured segments aligned to projection planes
947   SeqOfDirs aProjectionDirs;
948   aProjectionDirs.Append( gp::DX() );
949   aProjectionDirs.Append( gp::DY() );
950   aProjectionDirs.Append( gp::DZ() );
951
952   SeqOfSegments aMeasureSegments = GetInPlaneSegments( theCirc, aProjectionDirs );
953
954   SeqOfPlnsAndSegments aSelectedPlanes;
955
956   // select in-circle-plane direction for flyout closest to border of bounding box
957   for ( Standard_Integer aSegmentIt = 1; aSegmentIt <= aMeasureSegments.Length(); ++aSegmentIt )
958   {
959     const Segment& aSegment = aMeasureSegments.Value(aSegmentIt);
960
961     Standard_Real anAnchor = ElCLib::Parameter( theCirc, aSegment.First );
962
963     gp_Pln aSelectedPlane;
964
965     PositionDiameter( theBnd, theFaceN, theCirc, anAnchor, aSelectedPlane );
966
967     aSelectedPlanes.Append( PlaneAndSegment( aSelectedPlane, aSegment ) );
968   }
969
970   Handle(V3d_View) aView = Settings.ActiveView;
971
972   PlaneAndSegment aChoosenParams = !aView.IsNull()
973     ? SelectPlaneForProjection( aSelectedPlanes, aView )
974     : aSelectedPlanes.First();
975
976   thePnt1 = ((Segment)aChoosenParams).First;
977   thePnt2 = ((Segment)aChoosenParams).Last;
978   thePln  = ((gp_Pln)aChoosenParams);
979 }
980
981 //=================================================================================
982 // function : PositionDiameter
983 // purpose  : The method provides preliminary positioning algorithm for
984 //            for diameter dimensions measuring the circle. The diameter
985 //            dimension is bound at anchor point on the circle.
986 //            Parameters:
987 //              theBnd [in] the bounding box of the shape
988 //              theFaceN [in] - the circle face normal (can be void)
989 //              theCirc [in] - the measured circle
990 //              theAnchorAt [in] - the anchoring parameter
991 //              thePln [out] - dimension flyout plane
992 //            The method selects flyout plane to best match the current
993 //            view projection. The flyout plane can be parallel to circle,
994 //            or tangent to it.
995 //=================================================================================
996 void MeasureGUI_DimensionCreateTool::PositionDiameter( const Bnd_Box& theBnd,
997                                                        const gp_Vec& theFaceN,
998                                                        const gp_Circ& theCirc,
999                                                        const Standard_Real& theAnchorAt,
1000                                                        gp_Pln& thePln ) const
1001 {
1002   gp_Dir aCircN = theCirc.Axis().Direction();
1003   gp_Pnt aCircP = theCirc.Location();
1004
1005   // select tangent direction for flyout closest to border of bounding box
1006   gp_Dir aSelectedTanDir;
1007   if ( theFaceN.Magnitude() < Precision::Confusion() )
1008   {
1009     SeqOfDirs aTangentDirs;
1010     aTangentDirs.Append(  aCircN );
1011     aTangentDirs.Append( -aCircN );
1012     aSelectedTanDir = ChooseDirFromBnd( aTangentDirs, aCircP, theBnd );
1013   }
1014   else
1015   {
1016     aSelectedTanDir = gp_Dir( theFaceN );
1017   }
1018
1019   gp_Pnt aPnt1 = ElCLib::Value( theAnchorAt, theCirc );
1020   gp_Pnt aPnt2 = ElCLib::Value( theAnchorAt + M_PI, theCirc );
1021
1022   gp_Dir aSegmentDir = gce_MakeDir( aPnt1, aPnt2 );
1023
1024   SeqOfDirs aSegmentDirs;
1025   aSegmentDirs.Append(  aCircN ^ aSegmentDir );
1026   aSegmentDirs.Append( -aCircN ^ aSegmentDir );
1027   gp_Dir aSelectedSegDir = ChooseDirFromBnd( aSegmentDirs, aCircP, theBnd );
1028
1029   gp_Pln aTangentFlyout( aCircP, aSegmentDir ^ aSelectedTanDir );
1030   gp_Pln aCoplanarFlyout( aCircP, aSegmentDir ^ aSelectedSegDir );
1031
1032   SeqOfPlanes aSelectedPlanes;
1033   aSelectedPlanes.Append( aTangentFlyout );
1034   aSelectedPlanes.Append( aCoplanarFlyout );
1035
1036   Handle(V3d_View) aView = Settings.ActiveView;
1037
1038   thePln = !aView.IsNull()
1039     ? SelectPlaneForProjection( aSelectedPlanes, aView )
1040     : aSelectedPlanes.First();
1041 }
1042
1043 //=================================================================================
1044 // function : ChooseLengthFlyoutsFromBnd
1045 // purpose  :
1046 //=================================================================================
1047 void MeasureGUI_DimensionCreateTool::ChooseLengthFlyoutsFromBnd( SeqOfDirs& theDirs,
1048                                                                  const gp_Pnt& thePnt1,
1049                                                                  const gp_Pnt& thePnt2,
1050                                                                  const Bnd_Box& theBnd ) const
1051 {
1052   // compose a list of axis-aligned planes for lying-in flyouts
1053   NCollection_Sequence<gp_Pln> anAAPlanes;
1054
1055   // the axis-aligned planes for flyouts are built from
1056   // three points (P1, P2, and P1 translated in orthogonal
1057   // direction dx, dy, dz)
1058   gp_Dir anAxes[3] = { gp::DX(), gp::DY(), gp::DZ() };
1059
1060   for ( int anIt = 0; anIt < 3; ++anIt )
1061   {
1062     const gp_Dir& anAxisDir = anAxes[anIt];
1063     gp_Pnt aPnt3 = thePnt1.Translated( gp_Vec( anAxisDir ) );
1064     gce_MakePln aMakePlane( thePnt1, thePnt2, aPnt3 );
1065     if ( !aMakePlane.IsDone() )
1066     {
1067       continue;
1068     }
1069
1070     anAAPlanes.Append( aMakePlane.Value() );
1071   }
1072
1073   // find out what is the closest direction outside of the bounding box
1074   NCollection_Sequence<gp_Pln>::Iterator aPlaneIt( anAAPlanes );
1075
1076   gp_Dir aPointDir = gce_MakeDir( thePnt1, thePnt2 );
1077
1078   for ( ; aPlaneIt.More(); aPlaneIt.Next() )
1079   {
1080     const gp_Pln& aPlane = aPlaneIt.Value();
1081
1082     // transform bounding box to orthogonal coordinates relative to
1083     // dimension points P1, P2 (x-axis) and plane direction (z-axis),
1084     // where y coordinates will correspond to flyout direction against
1085     // the dimension point line
1086     gp_Ax3 aFlyoutSpace( thePnt1, aPlane.Axis().Direction(), aPointDir );
1087
1088     gp_Trsf aRelativeTransform;
1089     aRelativeTransform.SetTransformation( gp_Ax3(), aFlyoutSpace );
1090     Bnd_Box aRelativeBounds = theBnd.Transformed( aRelativeTransform );
1091
1092     Standard_Real aXmin, aXmax, aYmin, aYmax, aZmin, aZmax;
1093     aRelativeBounds.Get( aXmin, aYmin, aZmin, aXmax, aYmax, aZmax );
1094
1095     gp_Dir aPosFlyout = aPlane.Axis().Direction() ^ aPointDir;
1096     gp_Dir aNegFlyout = aPosFlyout.Reversed();
1097
1098     // select positive or negative flyout
1099     theDirs.Append( Abs( aYmax ) < Abs( aYmin ) ? aPosFlyout : aNegFlyout );
1100   }
1101 }
1102
1103 //=================================================================================
1104 // function : ChooseDirFromBnd
1105 // purpose  : The method chooses the best direction from the passed list of
1106 //            directions, which is closest to the bounding box border.
1107 //            Parameters:
1108 //              theCandidates [in] the list of candidate directions
1109 //              thePos [in] the position from where the directions are traced
1110 //              theBnd [in] the bounding box of main shape
1111 //=================================================================================
1112 gp_Dir MeasureGUI_DimensionCreateTool::ChooseDirFromBnd( const SeqOfDirs& theCandidates,
1113                                                          const gp_Pnt& thePos,
1114                                                          const Bnd_Box& theBnd ) const
1115 {
1116   gp_Dir aBestDir;
1117
1118   Standard_Real aBestDistance = RealLast();
1119
1120   SeqOfDirs::Iterator anIt( theCandidates );
1121   for ( ; anIt.More(); anIt.Next() )
1122   {
1123     const gp_Dir& aDir = anIt.Value();
1124
1125     gp_Ax3 aFlyoutSpace( thePos, aDir );
1126
1127     gp_Trsf aRelativeTransform;
1128     aRelativeTransform.SetTransformation( gp_Ax3(), aFlyoutSpace );
1129     Bnd_Box aRelativeBounds = theBnd.Transformed( aRelativeTransform );
1130
1131     Standard_Real aXmin, aXmax, aYmin, aYmax, aZmin, aZmax;
1132     aRelativeBounds.Get( aXmin, aYmin, aZmin, aXmax, aYmax, aZmax );
1133
1134     if ( aYmax < aBestDistance )
1135     {
1136       aBestDir = aDir;
1137       aBestDistance = aYmax;
1138     }
1139   }
1140
1141   return aBestDir;
1142 }
1143
1144 //=================================================================================
1145 // function : SelectPlaneForProjection
1146 // purpose  : Select best matching plane in current view projection
1147 //=================================================================================
1148 template <typename TPlane>
1149 TPlane MeasureGUI_DimensionCreateTool::SelectPlaneForProjection( const NCollection_Sequence<TPlane>& thePlanes,
1150                                                                  const Handle(V3d_View)& theView ) const
1151 {
1152   Standard_Real U[3];
1153   Standard_Real N[3];
1154   theView->Up( U[0], U[1], U[2] );
1155   theView->Proj( N[0], N[1], N[2] );
1156
1157   gp_Dir aViewN( (Standard_Real)N[0], (Standard_Real)N[1], (Standard_Real)N[2] );
1158   gp_Dir aViewU( (Standard_Real)U[0], (Standard_Real)U[1], (Standard_Real)U[2] );
1159
1160   TPlane aBestPlane = thePlanes.First();
1161
1162   Standard_Real aBestDotProduct = RealFirst();
1163
1164   for ( Standard_Integer aPlnIt = 1; aPlnIt <= thePlanes.Length(); ++aPlnIt )
1165   {
1166     const TPlane& aPlane = thePlanes.Value( aPlnIt );
1167
1168     Standard_Real aDotProduct = Abs( ((gp_Pln)aPlane).Axis().Direction() * aViewN );
1169
1170     // preferred plane is "view parallel"
1171     if ( aDotProduct <= aBestDotProduct )
1172     {
1173       continue;
1174     }
1175
1176     aBestPlane = aPlane;
1177
1178     aBestDotProduct = aDotProduct;
1179   }
1180
1181   return aBestPlane;
1182 }
1183
1184 //=================================================================================
1185 // function : GetMainShape
1186 // purpose  :
1187 //=================================================================================
1188 GEOM::GeomObjPtr MeasureGUI_DimensionCreateTool::GetMainShape( const GEOM::GeomObjPtr& theShape ) const
1189 {
1190   // iterate over top-level objects to search for main shape
1191   GEOM::GeomObjPtr aMainShapeIt = theShape;
1192   while ( !aMainShapeIt->IsMainShape() )
1193   {
1194     aMainShapeIt = aMainShapeIt->GetMainShape();
1195   }
1196   return aMainShapeIt;
1197 }
1198
1199 //=================================================================================
1200 // function : GetFaceSide
1201 // purpose  :
1202 //=================================================================================
1203 bool MeasureGUI_DimensionCreateTool::GetFaceSide( const TopoDS_Face& theFace, const TopoDS_Edge& theEdge, gp_Dir& theDir ) const
1204 {
1205   // get correctly oriented edge from main shape
1206   TopoDS_Edge anEdgeFromFace;
1207   TopExp_Explorer anExplorer( theFace.Oriented( TopAbs_FORWARD ), TopAbs_EDGE );
1208   for ( ; anExplorer.More(); anExplorer.Next() )
1209   {
1210     TopoDS_Edge aCurrentEdge = TopoDS::Edge( anExplorer.Current() );
1211     if ( theEdge.IsSame( aCurrentEdge ) )
1212     {
1213       anEdgeFromFace = aCurrentEdge;
1214       break;
1215     }
1216   }
1217
1218   if ( anEdgeFromFace.IsNull() )
1219   {
1220     return false;
1221   }
1222
1223   // check out the direction of face extensions from its boundaries at the edge location
1224   // made assumption here that for any linear bounding edge the
1225   // normals are same on the whole length of that edge
1226   Handle(Geom_Surface) aSurface = BRep_Tool::Surface( theFace );
1227   if ( aSurface.IsNull() || !aSurface->IsKind( STANDARD_TYPE(Geom_ElementarySurface) ) )
1228   {
1229     return false;
1230   }
1231
1232   BRepAdaptor_Curve aSurfCurve( anEdgeFromFace, theFace );
1233   if ( !aSurfCurve.IsCurveOnSurface() )
1234   {
1235     return false;
1236   }
1237
1238   Standard_Real aHalfRange = ( aSurfCurve.FirstParameter() + aSurfCurve.LastParameter() ) / 2.0;
1239
1240   gp_Pnt aPoint = aSurfCurve.Value( aHalfRange );
1241
1242   Standard_Real aPointU = 0.0;
1243   Standard_Real aPointV = 0.0;
1244   GeomLib_Tool::Parameters( aSurface, aPoint, Precision::Confusion(), aPointU, aPointV );
1245
1246   gp_Dir aNorm;
1247   if ( GeomLib::NormEstim( aSurface, gp_Pnt2d( aPointU, aPointV ), Precision::Confusion(), aNorm ) > 1 )
1248   {
1249     return false;
1250   }
1251
1252   gp_Vec aTangent = aSurfCurve.DN( aHalfRange, 1 );
1253   if ( aTangent.Magnitude() < Precision::Confusion() )
1254   {
1255     return false;
1256   }
1257
1258   TopAbs_Orientation anEdgeOrientation = anEdgeFromFace.Orientation();
1259   if ( anEdgeOrientation == TopAbs_REVERSED )
1260   {
1261     aTangent.Reverse();
1262   }
1263
1264   theDir = gp_Dir( aTangent ) ^ aNorm;
1265   return true;
1266 }
1267
1268 //=================================================================================
1269 // function : GetInPlaneSegments
1270 // purpose  : The method finds segments crossing the passed circle,
1271 //            which lie in the passed planes.
1272 //            Parameters:
1273 //              theCirc [in] the circle to be crossed.
1274 //              thePlanes [in] the projection planes crossing the circle.
1275 //=================================================================================
1276 MeasureGUI_DimensionCreateTool::SeqOfSegments
1277   MeasureGUI_DimensionCreateTool::GetInPlaneSegments( const gp_Circ& theCirc,
1278                                                       const SeqOfDirs& thePlanes ) const
1279 {
1280   SeqOfSegments aResult;
1281
1282   gp_Pnt aCircP = theCirc.Location();
1283   gp_Dir aCircN = theCirc.Axis().Direction();
1284   Standard_Real aCircR = theCirc.Radius();
1285
1286   SeqOfDirs::Iterator anIt( thePlanes );
1287   for ( ; anIt.More(); anIt.Next() )
1288   {
1289     const gp_Dir& aDir = anIt.Value();
1290
1291     if ( aDir.IsParallel( aCircN, Precision::Angular() ) )
1292     {
1293       continue;
1294     }
1295
1296     gp_Dir aIntDir = aDir ^ aCircN;
1297
1298     gp_Pnt aPnt1 = gp_Pnt( aCircP.XYZ() - aIntDir.XYZ() * aCircR );
1299     gp_Pnt aPnt2 = gp_Pnt( aCircP.XYZ() + aIntDir.XYZ() * aCircR );
1300     Segment aSegment;
1301     aSegment.First = aPnt1;
1302     aSegment.Last  = aPnt2;
1303     aResult.Append( aSegment );
1304   }
1305
1306   return aResult;
1307 }