Salome HOME
c32d0c9ca38c7e1dfa9e5378b438f0e5df47175d
[modules/geom.git] / src / MeasureGUI / MeasureGUI_DimensionCreateTool.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_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 <BRepAdaptor_Curve.hxx>
45 #include <BRepBndLib.hxx>
46 #include <gce_MakeDir.hxx>
47 #include <gce_MakePln.hxx>
48 #include <GC_MakePlane.hxx>
49 #include <Geom_Plane.hxx>
50 #include <Geom_ElementarySurface.hxx>
51 #include <Geom_Surface.hxx>
52 #include <GeomLib.hxx>
53 #include <GeomLib_Tool.hxx>
54 #include <TopoDS_Shape.hxx>
55 #include <TopoDS_Vertex.hxx>
56 #include <TopoDS_Edge.hxx>
57 #include <TopExp.hxx>
58 #include <TopExp_Explorer.hxx>
59 #include <TopTools_IndexedDataMapOfShapeListOfShape.hxx>
60 #include <TopTools_ListOfShape.hxx>
61 #include <TColgp_SequenceOfDir.hxx>
62 #include <gp_Pnt.hxx>
63 #include <V3d_View.hxx>
64
65 //=================================================================================
66 // function : Constructor
67 // purpose  :
68 //=================================================================================
69 MeasureGUI_DimensionCreateTool::MeasureGUI_DimensionCreateTool( GeometryGUI* theGeomGUI )
70 : myGeomGUI( theGeomGUI )
71 {
72 }
73
74 //=================================================================================
75 // function : LengthOnEdge
76 // purpose  :
77 //=================================================================================
78 Handle(AIS_LengthDimension) MeasureGUI_DimensionCreateTool::LengthOnEdge( const GEOM::GeomObjPtr& theEdge )
79 {
80   /* ---------------------------------------------------------------- *
81    *                  get the edge and parent shape                   *
82    * ---------------------------------------------------------------- */
83
84   TopoDS_Shape aShapeEdge;
85   TopoDS_Shape aShapeMain;
86   if ( !GEOMBase::GetShape( theEdge.get(), aShapeEdge ) )
87   {
88     return NULL;
89   }
90   if ( !GEOMBase::GetShape( GetMainShape( theEdge ).get(), aShapeMain ) )
91   {
92     return NULL;
93   }
94
95   /* ------------------------------------------------- */
96   /*            check the input geometry               */
97   /* ------------------------------------------------- */
98
99   TopoDS_Edge anEdge = TopoDS::Edge( aShapeEdge );
100
101   TopoDS_Vertex aVertex1;
102   TopoDS_Vertex aVertex2;
103   TopExp::Vertices( anEdge, aVertex1, aVertex2 );
104
105   gp_Pnt aPnt1 = BRep_Tool::Pnt( aVertex1 );
106   gp_Pnt aPnt2 = BRep_Tool::Pnt( aVertex2 );
107   if ( aPnt1.Distance( aPnt2 ) <= Precision::Confusion() )
108   {
109     return NULL;
110   }
111
112   /* ------------------------------------------------- *
113    *   compose list of possible flyout directions      *
114    * ------------------------------------------------- */
115
116   Bnd_Box aBnd;
117   BRepBndLib::AddClose( aShapeMain, aBnd );
118
119   TColgp_SequenceOfDir aSeqOfFlyout;
120   ChooseLengthFlyoutsFromShape( aSeqOfFlyout, anEdge, aShapeMain );
121   ChooseLengthFlyoutsFromBnd( aSeqOfFlyout, aPnt1, aPnt2, aBnd );
122   if ( aSeqOfFlyout.IsEmpty() )
123   {
124     return NULL;
125   }
126
127   gp_Dir aPointDir = gce_MakeDir( aPnt1, aPnt2 );
128
129   // make planes for dimension presentation according to flyout directions
130   NCollection_Sequence<gp_Pln> aSeqOfPlanes;
131   for ( Standard_Integer aFlyoutIt = 1; aFlyoutIt <= aSeqOfFlyout.Length(); ++aFlyoutIt )
132   {
133     gp_Pln aPlane( aPnt1, aPointDir ^ aSeqOfFlyout.Value( aFlyoutIt ) );
134     aSeqOfPlanes.Append( aPlane );
135   }
136
137   /* --------------------------------------------------------------------- *
138    *     select best matching dimension plane for view projection          *
139    * --------------------------------------------------------------------- */
140
141   OCCViewer_ViewWindow* anActiveView = NULL;
142
143   if ( myGeomGUI != NULL )
144   {
145     SalomeApp_Application* anApp = myGeomGUI->getApp();
146     if ( !anApp )
147     {
148       OCCViewer_ViewManager* aViewMgr = (OCCViewer_ViewManager*) anApp->getViewManager( OCCViewer_Viewer::Type(), false );
149       if ( aViewMgr )
150       {
151         anActiveView = (OCCViewer_ViewWindow*)  aViewMgr->getActiveView();
152       }
153     }
154   }
155
156   gp_Pln aChoosenPlane = anActiveView
157     ? SelectPlaneForProjection( aSeqOfPlanes, anActiveView->getViewPort()->getView() )
158     : aSeqOfPlanes.First();
159
160   /* ------------------------------------------------------------------------------------ *
161    *                        construct interactive presentation                            *
162    * ------------------------------------------------------------------------------------ */
163
164   Handle(AIS_LengthDimension) aDimension = new AIS_LengthDimension( anEdge, aChoosenPlane );
165   if ( !aDimension->IsValid() )
166   {
167     return NULL;
168   }
169
170   return aDimension;
171 }
172
173 //=================================================================================
174 // function : LengthByPoints
175 // purpose  :
176 //=================================================================================
177 Handle(AIS_LengthDimension) MeasureGUI_DimensionCreateTool::LengthByPoints( const GEOM::GeomObjPtr& thePoint1,
178                                                                             const GEOM::GeomObjPtr& thePoint2 )
179 {
180   TopoDS_Shape aFirstSh;
181   if ( !GEOMBase::GetShape( thePoint1.operator ->(), aFirstSh ) )
182   {
183     return NULL;
184   }
185
186   TopoDS_Shape aSecondSh;
187   if ( !GEOMBase::GetShape( thePoint2.operator ->(), aSecondSh ) )
188   {
189     return NULL;
190   }
191
192   TopoDS_Vertex aFirstVertex  = TopoDS::Vertex( aFirstSh );
193   TopoDS_Vertex aSecondVertex = TopoDS::Vertex( aSecondSh );
194
195   gp_Pnt aPnt1 = BRep_Tool::Pnt( aFirstVertex );
196   gp_Pnt aPnt2 = BRep_Tool::Pnt( aSecondVertex );
197
198   gp_Vec aDir( aPnt1, aPnt2 );
199   gp_Dir aUnitVecs[] = { gp::DZ(), gp::DY(), gp::DX() };
200   int aUnitVecIt = 0;
201   for ( ; aUnitVecIt < 3; ++aUnitVecIt )
202   {
203     if ( aDir.Dot( aUnitVecs[aUnitVecIt] ) <= 0.5 )
204     {
205       break;
206     }
207   }
208
209   gp_Pnt aPnt3 = aPnt2.Translated( aUnitVecs[aUnitVecIt] );
210
211   GC_MakePlane aMkPlane( aPnt1, aPnt2, aPnt3 );
212   Handle(Geom_Plane) aPlane = aMkPlane.Value();
213
214   // check whether it is possible to compute valid dimension
215   Handle(AIS_LengthDimension) aDimension = new AIS_LengthDimension ( aFirstVertex, aSecondVertex, aPlane->Pln() );
216
217   if ( !aDimension->IsValid() )
218   {
219     return NULL;
220   }
221
222   return aDimension;
223 }
224
225 //=================================================================================
226 // function : LengthByParallelEdges
227 // purpose  :
228 //=================================================================================
229 Handle(AIS_LengthDimension) MeasureGUI_DimensionCreateTool::LengthByParallelEdges( const GEOM::GeomObjPtr& theEdge1,
230                                                                                    const GEOM::GeomObjPtr& theEdge2 )
231 {
232   TopoDS_Shape aFirstSh;
233   if ( !GEOMBase::GetShape( theEdge1.operator ->(), aFirstSh ) )
234   {
235     return NULL;
236   }
237
238   TopoDS_Shape aSecondSh;
239   if ( !GEOMBase::GetShape( theEdge2.operator ->(), aSecondSh ) )
240   {
241     return NULL;
242   }
243
244   TopoDS_Edge aFirstEdge  = TopoDS::Edge( aFirstSh );
245   TopoDS_Edge aSecondEdge = TopoDS::Edge( aSecondSh );
246
247   // Build plane through three points
248   BRepAdaptor_Curve aCurve1( aFirstEdge );
249   BRepAdaptor_Curve aCurve2( aSecondEdge );
250
251   gp_Pnt aPnt1 = aCurve1.Value( 0.1 );
252   gp_Pnt aPnt2 = aCurve1.Value( 0.9 );
253   gp_Pnt aPnt3 = aCurve2.Value( 0.5 );
254
255   GC_MakePlane aMkPlane( aPnt1, aPnt2, aPnt3 );
256   Handle(Geom_Plane) aPlane = aMkPlane.Value();
257
258   // check whether it is possible to compute valid dimension
259   Handle(AIS_LengthDimension) aDimension = new AIS_LengthDimension ( aFirstEdge, aSecondEdge, aPlane->Pln() );
260
261   if ( !aDimension->IsValid() )
262   {
263     return NULL;
264   }
265
266   return aDimension;
267 }
268
269 //=================================================================================
270 // function : Diameter
271 // purpose  :
272 //=================================================================================
273 Handle(AIS_DiameterDimension) MeasureGUI_DimensionCreateTool::Diameter( const GEOM::GeomObjPtr& theShape )
274 {
275   TopoDS_Shape aShape;
276   if ( !GEOMBase::GetShape( theShape.operator ->(), aShape ) )
277   {
278     return NULL;
279   }
280
281   if ( aShape.ShapeType() != TopAbs_EDGE &&
282        aShape.ShapeType() != TopAbs_FACE &&
283        aShape.ShapeType() != TopAbs_WIRE )
284   {
285     return NULL;
286   }
287
288   // check whether it is possible to compute dimension on the passed geometry
289   Handle(AIS_DiameterDimension) aDimension = new AIS_DiameterDimension( aShape );
290
291   if ( !aDimension->IsValid() )
292   {
293     return NULL;
294   }
295
296   return aDimension;
297 }
298
299 //=================================================================================
300 // function : AngleByTwoEdges
301 // purpose  :
302 //=================================================================================
303 Handle(AIS_AngleDimension) MeasureGUI_DimensionCreateTool::AngleByTwoEdges( const GEOM::GeomObjPtr& theEdge1,
304                                                                             const GEOM::GeomObjPtr& theEdge2 )
305 {
306   /* --------------------------------------------------- */
307   /*         get construction and parent shapes          */
308   /* --------------------------------------------------- */
309
310   TopoDS_Shape aShapeEdge1;
311   TopoDS_Shape aShapeMain1;
312   if ( !GEOMBase::GetShape( theEdge1.get(), aShapeEdge1 ) )
313   {
314     return NULL;
315   }
316   if ( !GEOMBase::GetShape( GetMainShape( theEdge1 ).get(), aShapeMain1 ) )
317   {
318     return NULL;
319   }
320
321   TopoDS_Shape aShapeEdge2;
322   TopoDS_Shape aShapeMain2;
323   if ( !GEOMBase::GetShape( theEdge2.get(), aShapeEdge2 ) )
324   {
325     return NULL;
326   }
327   if ( !GEOMBase::GetShape( GetMainShape( theEdge2 ).get(), aShapeMain2 ) )
328   {
329     return NULL;
330   }
331
332   /* ---------------------------------------------------- */
333   /*             check construction edges                 */
334   /* ---------------------------------------------------- */
335
336   TopoDS_Edge aFirstEdge  = TopoDS::Edge( aShapeEdge1 );
337   TopoDS_Edge aSecondEdge = TopoDS::Edge( aShapeEdge2 );
338
339   // check whether it is possible to compute dimension on the passed edges
340   Handle(AIS_AngleDimension) aDimension = new AIS_AngleDimension( aFirstEdge, aSecondEdge );
341
342   if ( !aDimension->IsValid() )
343   {
344     return NULL;
345   }
346
347   const gp_Pnt& aFirstPoint  = aDimension->FirstPoint();
348   const gp_Pnt& aSecondPoint = aDimension->SecondPoint();
349   const gp_Pnt& aCenterPoint = aDimension->CenterPoint();
350
351   gp_Vec aVec1( aCenterPoint, aFirstPoint );
352   gp_Vec aVec2( aCenterPoint, aSecondPoint );
353
354   Standard_Real anAngle = aVec2.AngleWithRef( aVec1, aDimension->GetPlane().Axis().Direction() );
355
356   if ( anAngle < 0.0 )
357   {
358     aDimension = new AIS_AngleDimension( aSecondPoint, aCenterPoint, aFirstPoint );
359   }
360
361   return aDimension;
362 }
363
364 //=================================================================================
365 // function : AngleByThreePoints
366 // purpose  :
367 //=================================================================================
368 Handle(AIS_AngleDimension) MeasureGUI_DimensionCreateTool::AngleByThreePoints( const GEOM::GeomObjPtr& thePoint1,
369                                                                                const GEOM::GeomObjPtr& thePoint2,
370                                                                                const GEOM::GeomObjPtr& thePoint3 )
371 {
372   TopoDS_Shape aFirstSh;
373   if ( !GEOMBase::GetShape( thePoint1.operator ->(), aFirstSh ) )
374   {
375     return NULL;
376   }
377
378   TopoDS_Shape aSecondSh;
379   if ( !GEOMBase::GetShape( thePoint2.operator ->(), aSecondSh ) )
380   {
381     return NULL;
382   }
383
384   TopoDS_Shape aThirdSh;
385   if ( !GEOMBase::GetShape( thePoint3.operator ->(), aThirdSh ) )
386   {
387     return NULL;
388   }
389
390   TopoDS_Vertex aFirstVertex  = TopoDS::Vertex( aFirstSh );
391   TopoDS_Vertex aSecondVertex = TopoDS::Vertex( aSecondSh );
392   TopoDS_Vertex aThirdVertex  = TopoDS::Vertex( aThirdSh );
393
394   gp_Pnt aPnt1 = BRep_Tool::Pnt( aFirstVertex );
395   gp_Pnt aPnt2 = BRep_Tool::Pnt( aSecondVertex );
396   gp_Pnt aPnt3 = BRep_Tool::Pnt( aThirdVertex );
397
398   // check whether it is possible to compute dimension on the passed points
399   Handle(AIS_AngleDimension) aDimension = new AIS_AngleDimension( aPnt1, aPnt2, aPnt3 );
400
401   if ( !aDimension->IsValid() )
402   {
403     return NULL;
404   }
405
406   return aDimension;
407 }
408
409 //=================================================================================
410 // function : ChooseLengthFlyoutsFromShape
411 // purpose  :
412 //=================================================================================
413 void MeasureGUI_DimensionCreateTool::ChooseLengthFlyoutsFromShape( TColgp_SequenceOfDir& theDirs,
414                                                                    const TopoDS_Vertex& theVertex1,
415                                                                    const TopoDS_Vertex& theVertex2,
416                                                                    const TopoDS_Shape& theShape )
417 {
418 }
419
420 //=================================================================================
421 // function : ChooseLengthFlyoutsFromShape
422 // purpose  :
423 //=================================================================================
424 void MeasureGUI_DimensionCreateTool::ChooseLengthFlyoutsFromShape( TColgp_SequenceOfDir& theDirs,
425                                                                    const TopoDS_Edge& theEdge,
426                                                                    const TopoDS_Shape& theShape )
427 {
428   TopTools_IndexedDataMapOfShapeListOfShape aRelationMap;
429   TopExp::MapShapesAndAncestors( theShape, TopAbs_EDGE, TopAbs_FACE, aRelationMap );
430   const TopTools_ListOfShape& aRelatedFaces = aRelationMap.FindFromKey( theEdge );
431
432   // get face side directions
433   gp_Dir aSideDir;
434   if ( aRelatedFaces.Extent() > 0 && GetFaceSide( TopoDS::Face( aRelatedFaces.First() ), theEdge, aSideDir ) )
435   {
436     theDirs.Append( aSideDir );
437   }
438   if ( aRelatedFaces.Extent() > 1 && GetFaceSide( TopoDS::Face( aRelatedFaces.Last() ), theEdge, aSideDir ) )
439   {
440     theDirs.Append( aSideDir );
441   }
442
443   // get average direction in case of two non-sharp angled faces
444   if ( theDirs.Length() == 2 )
445   {
446     const gp_Dir& aDir1 = theDirs.First();
447     const gp_Dir& aDir2 = theDirs.Last();
448     Standard_Boolean isSame = aDir1.IsParallel( aDir2, Precision::Angular() );
449     if ( !isSame )
450     {
451       gp_Dir aReferenceDir = aDir1 ^ aDir2;
452       // compute angle between face sides [0 - 2PI]
453       Standard_Real aDirAngle = aDir1.AngleWithRef( aDir2, aReferenceDir );
454       if ( aDirAngle < 0 )
455       {
456         aDirAngle = ( M_PI * 2.0 ) - aDirAngle;
457       }
458
459       // non-sharp angle, use averaged direction
460       if ( aDirAngle > M_PI )
461       {
462         theDirs.Clear();
463         theDirs.Append( aDir1.Rotated( gp_Ax1( gp::Origin(), aReferenceDir ), aDirAngle * 0.5 ) );
464       }
465     }
466   }
467 }
468
469 //=================================================================================
470 // function : ChooseLengthFlyoutsFromBnd
471 // purpose  :
472 //=================================================================================
473 void MeasureGUI_DimensionCreateTool::ChooseLengthFlyoutsFromBnd( TColgp_SequenceOfDir& theDirs,
474                                                                  const gp_Pnt& thePnt1,
475                                                                  const gp_Pnt& thePnt2,
476                                                                  const Bnd_Box& theBnd )
477 {
478   // compose a list of axis-aligned planes for lying-in flyouts
479   NCollection_Sequence<gp_Pln> anAAPlanes;
480
481   // the axis-aligned planes for flyouts are built from
482   // three points (P1, P2, and P1 translated in orthogonal
483   // direction dx, dy, dz)
484   gp_Dir anAxes[3] = { gp::DX(), gp::DY(), gp::DZ() };
485
486   for ( int anIt = 0; anIt < 3; ++anIt )
487   {
488     const gp_Dir& anAxisDir = anAxes[anIt];
489     gp_Pnt aPnt3 = thePnt1.Translated( gp_Vec( anAxisDir ) );
490     gce_MakePln aMakePlane( thePnt1, thePnt2, aPnt3 );
491     if ( !aMakePlane.IsDone() )
492     {
493       continue;
494     }
495
496     anAAPlanes.Append( aMakePlane.Value() );
497   }
498
499   // find out what is the closest direction outside of the bounding box
500   NCollection_Sequence<gp_Pln>::Iterator aPlaneIt( anAAPlanes );
501
502   gp_Dir aPointDir = gce_MakeDir( thePnt1, thePnt2 );
503
504   for ( ; aPlaneIt.More(); aPlaneIt.Next() )
505   {
506     const gp_Pln& aPlane = aPlaneIt.Value();
507
508     // transform bounding box to orthogonal coordiantes relative to
509     // dimension points P1, P2 (x-axis) and plane direction (z-axis),
510     // where y coordinates will correspond to flyout direction against
511     // the dimension point line
512     gp_Ax3 aFlyoutSpace( thePnt1, aPlane.Axis().Direction(), aPointDir );
513
514     gp_Trsf aRelativeTransform;
515     aRelativeTransform.SetTransformation( gp_Ax3(), aFlyoutSpace );
516     Bnd_Box aRelativeBounds = theBnd.Transformed( aRelativeTransform );
517
518     Standard_Real aXmin, aXmax, aYmin, aYmax, aZmin, aZmax;
519     aRelativeBounds.Get( aXmin, aYmin, aZmin, aXmax, aYmax, aZmax );
520
521     gp_Dir aPosFlyout = aPlane.Axis().Direction() ^ aPointDir;
522     gp_Dir aNegFlyout = aPosFlyout.Reversed();
523
524     // select positive or negative flyout
525     theDirs.Append( Abs( aYmax ) < Abs( aYmin ) ? aPosFlyout : aNegFlyout );
526   }
527 }
528
529 //=================================================================================
530 // function : SelectPlaneForProjection
531 // purpose  : Select best matching plane in current view projection
532 //=================================================================================
533 gp_Pln MeasureGUI_DimensionCreateTool::SelectPlaneForProjection( const NCollection_Sequence<gp_Pln>& thePlanes,
534                                                                  const Handle(V3d_View)& theView )
535 {
536   Quantity_Parameter U[3];
537   Quantity_Parameter N[3];
538   theView->Up( U[0], U[1], U[2] );
539   theView->Proj( N[0], N[1], N[2] );
540
541   gp_Dir aViewN( (Standard_Real)N[0], (Standard_Real)N[1], (Standard_Real)N[2] );
542   gp_Dir aViewU( (Standard_Real)U[0], (Standard_Real)U[1], (Standard_Real)U[2] );
543
544   gp_Pln aBestPlane = thePlanes.First();
545
546   Standard_Real aBestDotProduct = RealFirst();
547
548   for ( Standard_Integer aPlnIt = 1; aPlnIt <= thePlanes.Length(); ++aPlnIt )
549   {
550     const gp_Pln& aPlane = thePlanes.Value( aPlnIt );
551
552     Standard_Real aDotProduct = Abs( aPlane.Axis().Direction() * aViewN );
553
554     // preferred plane is "view parallel"
555     if ( aDotProduct <= aBestDotProduct )
556     {
557       continue;
558     }
559
560     aBestPlane = aPlane;
561
562     aBestDotProduct = aDotProduct;
563   }
564
565   return aBestPlane;
566 }
567
568 //=================================================================================
569 // function : GetMainShape
570 // purpose  :
571 //=================================================================================
572 GEOM::GeomObjPtr MeasureGUI_DimensionCreateTool::GetMainShape( const GEOM::GeomObjPtr& theShape )
573 {
574   // iterate over top-level objects to search for main shape
575   GEOM::GeomObjPtr aMainShapeIt = theShape;
576   while ( !aMainShapeIt->IsMainShape() )
577   {
578     aMainShapeIt = aMainShapeIt->GetMainShape();
579   }
580   return aMainShapeIt;
581 }
582
583 //=================================================================================
584 // function : GetFaceSide
585 // purpose  :
586 //=================================================================================
587 bool MeasureGUI_DimensionCreateTool::GetFaceSide( const TopoDS_Face& theFace, const TopoDS_Edge& theEdge, gp_Dir& theDir )
588 {
589   // get correctly oriented edge from main shape
590   TopoDS_Edge anEdgeFromFace;
591   TopExp_Explorer anExplorer( theFace.Oriented( TopAbs_FORWARD ), TopAbs_EDGE );
592   for ( ; anExplorer.More(); anExplorer.Next() )
593   {
594     TopoDS_Edge aCurrentEdge = TopoDS::Edge( anExplorer.Current() );
595     if ( theEdge.IsSame( aCurrentEdge ) )
596     {
597       anEdgeFromFace = aCurrentEdge;
598       break;
599     }
600   }
601
602   if ( anEdgeFromFace.IsNull() )
603   {
604     return false;
605   }
606
607   // check out the direction of face extensions from its boundaries at the edge location
608   // made assumption here that for any linear bounding edge the
609   // normals are same on the whole length of that edge
610   Handle(Geom_Surface) aSurface = BRep_Tool::Surface( theFace );
611   if ( aSurface.IsNull() || !aSurface->IsKind( STANDARD_TYPE(Geom_ElementarySurface) ) )
612   {
613     return false;
614   }
615
616   BRepAdaptor_Curve aSurfCurve( anEdgeFromFace, theFace );
617   if ( !aSurfCurve.IsCurveOnSurface() )
618   {
619     return false;
620   }
621
622   Standard_Real aHalfRange = ( aSurfCurve.FirstParameter() + aSurfCurve.LastParameter() ) / 2.0;
623
624   gp_Pnt aPoint = aSurfCurve.Value( aHalfRange );
625
626   Standard_Real aPointU = 0.0;
627   Standard_Real aPointV = 0.0;
628   GeomLib_Tool::Parameters( aSurface, aPoint, Precision::Confusion(), aPointU, aPointV );
629
630   gp_Dir aNorm;
631   if ( GeomLib::NormEstim( aSurface, gp_Pnt2d( aPointU, aPointV ), Precision::Confusion(), aNorm ) > 1 )
632   {
633     return false;
634   }
635
636   gp_Vec aTangent = aSurfCurve.DN( aHalfRange, 1 );
637   if ( aTangent.Magnitude() < Precision::Confusion() )
638   {
639     return false;
640   }
641
642   TopAbs_Orientation anEdgeOrientation = anEdgeFromFace.Orientation();
643   if ( anEdgeOrientation == TopAbs_REVERSED )
644   {
645     aTangent.Reverse();
646   }
647
648   theDir = gp_Dir( aTangent ) ^ aNorm;
649   return true;
650 }