Salome HOME
Merge changes for HYDRO project : hydro/imps_2017_salome_83 branch.
[modules/geom.git] / src / CurveCreator / CurveCreator_Utils.cxx
1 // Copyright (C) 2013-2016  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include "CurveCreator_Utils.hxx"
21 #include "CurveCreator.hxx"
22 #include "CurveCreator_Curve.hxx"
23 #include "CurveCreator_Section.hxx"
24 #include "CurveCreator_UtilsICurve.hxx"
25
26 #include <GEOMUtils.hxx>
27
28 #include <gp_Pln.hxx>
29
30 #include <TopoDS.hxx>
31 #include <TopoDS_Vertex.hxx>
32 #include <TopoDS_Wire.hxx>
33 #include <TopoDS_Edge.hxx>
34 #include <TopoDS_Compound.hxx>
35
36 #include <AIS_ListOfInteractive.hxx>
37 #include <AIS_ListIteratorOfListOfInteractive.hxx>
38 #include <AIS_Shape.hxx>
39 #include <AIS_Line.hxx>
40 #include <AIS_Trihedron.hxx>
41 #include <AIS_LocalContext.hxx>
42
43 #include <Geom_Point.hxx>
44 #include <Geom_BSplineCurve.hxx>
45 #include <Geom_Line.hxx>
46 #include <Geom_Curve.hxx>
47 #include <Geom_TrimmedCurve.hxx>
48
49 #include <TopExp.hxx>
50 #include <TopExp_Explorer.hxx>
51 #include <TopTools_ListIteratorOfListOfShape.hxx>
52 #include <GeomAPI_ProjectPointOnCurve.hxx>
53 #include <SelectMgr_EntityOwner.hxx>
54 #include <SelectMgr_Selection.hxx>
55 #include <Select3D_SensitivePoint.hxx>
56
57 #include <BRep_Tool.hxx>
58 #include <BRep_Builder.hxx>
59 #include <BRepBuilderAPI_MakeVertex.hxx>
60 #include <BRepBuilderAPI_MakeEdge.hxx>
61 #include <BRepBuilderAPI_MakeWire.hxx>
62 #include <BRepTools_WireExplorer.hxx>
63
64 #include <TColgp_HArray1OfPnt.hxx>
65 #include <TColStd_HArray1OfBoolean.hxx>
66 #include <TColStd_Array1OfReal.hxx>
67 #include <TColgp_Array1OfVec.hxx>
68 #include <GeomAPI_Interpolate.hxx>
69
70 #include <ProjLib.hxx>
71 #include <ElSLib.hxx>
72
73 #include <math.h>
74
75 #include "CurveCreator_ICurve.hxx"
76
77 const double LOCAL_SELECTION_TOLERANCE = 0.0001;
78 const int    SCENE_PIXEL_PROJECTION_TOLERANCE = 10;
79 const int    SCENE_PIXEL_POINT_TOLERANCE = 5;
80
81 #define PLN_FREE   0
82 #define PLN_ORIGIN 1
83 #define PLN_OX     2
84 #define PLN_FIXED  3
85
86 /**
87  * This static function returns the curve of original type from the edge.
88  *
89  * \param theEdge the edge
90  * \return the curve of original type. Can be null handle.
91  */
92 static Handle(Geom_Curve) GetCurve(const TopoDS_Edge &theEdge)
93 {
94   Handle(Geom_Curve) aResult;
95
96   if (theEdge.IsNull()) {
97     return aResult;
98   }
99
100   Standard_Real aF;
101   Standard_Real aL;
102
103   aResult = BRep_Tool::Curve(theEdge, aF, aL);
104
105   if (aResult.IsNull()) {
106     return aResult;
107   }
108
109   // Get the curve of original type
110   Handle(Standard_Type) aType = aResult->DynamicType();
111
112   while (aType == STANDARD_TYPE(Geom_TrimmedCurve)) {
113     Handle(Geom_TrimmedCurve) aTrCurve =
114       Handle(Geom_TrimmedCurve)::DownCast(aResult);
115
116     aResult = aTrCurve->BasisCurve();
117     aType  = aResult->DynamicType();
118   }
119
120   return aResult;
121 }
122
123 //=======================================================================
124 // function : ConvertClickToPoint()
125 // purpose  : Returns the point clicked in 3D view
126 //=======================================================================
127 void CurveCreator_Utils::ConvertPointToClick( const gp_Pnt& thePoint,
128                                               Handle(V3d_View) theView,
129                                               int& x, int& y )
130 {
131   theView->Convert(thePoint.X(), thePoint.Y(), thePoint.Z(), x, y );
132 }
133
134
135 //=======================================================================
136 // function : ConvertClickToPoint()
137 // purpose  : Returns the point clicked in 3D view
138 //=======================================================================
139 gp_Pnt CurveCreator_Utils::ConvertClickToPoint( int x, int y, Handle(V3d_View) aView )
140 {
141   // the 3D point, that is a projection of the pixels to the XYZ view plane
142   //return GEOMUtils::ConvertClickToPoint( x, y, aView );
143
144   // we need the projection to the XOY plane
145   // 1. find a point in the plane of the eye and the normal to the plane
146   Standard_Real X, Y, Z;
147   Standard_Real Vx, Vy, Vz;
148   aView->ConvertWithProj( x, y, X, Y, Z, Vx, Vy, Vz );
149
150   // 2. build a ray from the point by the normal to the XOY plane and intersect it
151   // The ray equation is the following : p(x,y,z) = p0(x,y,z) + t*V(x,y,z)
152   // X,Y,Z - defines p0(x,y,z), Vx,Vy,Vz - defines V(x,y,z)
153   // p(x,y,z) - is a searched point, t - should to be calculated by the condition of XOY plane
154   // The system of equations is the following:
155   // p(x) = p0(x)+t*V(x)
156   // p(y) = p0(y)+t*V(y)
157   // p(z) = p0(z)+t*V(z)
158   // p(z) = 0
159
160   Standard_Real aXp, aYp, aZp;
161   //It is not possible to use Precision::Confusion(), because it is e-0.8, but V is sometimes e-6
162   Standard_Real aPrec = LOCAL_SELECTION_TOLERANCE;
163   if ( fabs( Vz ) > aPrec ) {
164     Standard_Real aT = -Z/Vz;
165     aXp = X + aT*Vx;
166     aYp = Y + aT*Vy;
167     aZp = Z + aT*Vz;
168   }
169   else { // Vz = 0 - the eyed plane is orthogonal to Z plane - XOZ, or YOZ
170     aXp = aYp = aZp = 0;
171     if ( fabs( Vy ) < aPrec ) // Vy = 0 - the YOZ plane
172       aYp = Y;
173     else if ( fabs( Vx ) < aPrec ) // Vx = 0 - the XOZ plane
174       aXp = X;
175   }
176   /*std::cout << "ConvertClickToPoint: " << std::endl
177             << "XYZ1 = (" << X << ", " << Y << ", " << Z << "); " << std::endl
178             << "Vxyz = (" << Vx << ", " << Vy << ", " << Vz << "); " << std::endl
179             << "Resp = (" << aXp << ", " << aYp << ", " << aZp << "); " << std::endl;*/
180
181   gp_Pnt ResultPoint( aXp, aYp, aZp );
182   return ResultPoint;
183 }
184
185 //=======================================================================
186 // function : constructBSpline
187 // purpose  :
188 //=======================================================================
189 bool CurveCreator_Utils::constructBSpline(
190   const Handle(TColgp_HArray1OfPnt)& thePoints,
191   const Standard_Boolean theIsClosed,
192   Handle(Geom_BSplineCurve)& theBSpline)
193 {
194   const int aPointCount = thePoints->Length();
195   if (aPointCount <= 1)
196   {
197     return false;
198   }
199
200   // Calculate the tangents.
201   TColgp_Array1OfVec aTangents(1, aPointCount);
202   Handle(TColStd_HArray1OfBoolean) aTangentFlags =
203     new TColStd_HArray1OfBoolean(1, aPointCount);
204   GeomAPI_Interpolate aInterpolator(thePoints, theIsClosed, 0);
205   if (aPointCount == 2)
206   {
207     aTangentFlags->SetValue(1, Standard_False);
208     aTangentFlags->SetValue(2, Standard_False);
209   }
210   else
211   {
212     for (Standard_Integer aPN = 1; aPN <= aPointCount; ++aPN)
213     {
214       gp_Vec aTangent;
215       if (aPN != 1 || theIsClosed)
216       {
217         const Standard_Integer aPN1 = (aPN != 1) ? (aPN - 1) : aPointCount;
218         aTangent = gp_Vec(thePoints->Value(aPN1),
219           thePoints->Value(aPN)).Normalized();
220       }
221       if (aPN < aPointCount || theIsClosed)
222       {
223         const Standard_Integer aPN2 = (aPN != aPointCount) ? (aPN + 1) : 1;
224         const gp_Vec aTangent2 = aTangent +
225           gp_Vec(thePoints->Value(aPN), thePoints->Value(aPN2)).Normalized();
226         if (aTangent2.SquareMagnitude() >= Precision::SquareConfusion())
227         {
228           aTangent = aTangent2.Normalized();
229         }
230         else
231         {
232           aTangent = -aTangent;
233         }
234       }
235       aTangents.SetValue(aPN, aTangent);
236       aTangentFlags->SetValue(aPN, Standard_True);
237     }
238   }
239
240   // Interpolate.
241   aInterpolator.Load(aTangents, aTangentFlags, Standard_False);
242   aInterpolator.Perform();
243   const bool aResult = (aInterpolator.IsDone() == Standard_True);
244   if (aResult)
245   {
246     theBSpline = aInterpolator.Curve();
247   }
248   return aResult;
249 }
250
251 //=======================================================================
252 // function : constructWire
253 // purpose  :
254 //=======================================================================
255 TopoDS_Wire CurveCreator_Utils::ConstructWire(
256   Handle(TColgp_HArray1OfPnt) thePoints,
257   const bool theIsPolyline,
258   const bool theIsClosed)
259 {
260   TopoDS_Wire aWire;
261   BRep_Builder aBuilder;
262   aBuilder.MakeWire(aWire);
263   const int aPointCount = thePoints->Length();
264   if (theIsPolyline)
265   {
266     const TopoDS_Vertex aFirstVertex =
267       BRepBuilderAPI_MakeVertex(thePoints->Value(1));
268     TopoDS_Vertex aVertex = aFirstVertex;
269     for (Standard_Integer aPN = 1; aPN < aPointCount; ++aPN)
270     {
271       const TopoDS_Vertex aVertex2 =
272         BRepBuilderAPI_MakeVertex(thePoints->Value(aPN + 1));
273       aBuilder.Add(aWire, BRepBuilderAPI_MakeEdge(aVertex, aVertex2));
274       aVertex = aVertex2;
275     }
276     if (theIsClosed && aPointCount > 1)
277     {
278       aBuilder.Add(aWire, BRepBuilderAPI_MakeEdge(aVertex, aFirstVertex));
279     }
280   }
281   else
282   {
283     Handle(Geom_BSplineCurve) aBSpline;
284     if (constructBSpline(thePoints, theIsClosed, aBSpline))
285     {
286       aBuilder.Add(aWire, BRepBuilderAPI_MakeEdge(aBSpline));
287     }
288   }
289   return aWire;
290 }
291
292 //=======================================================================
293 // function : constructShape
294 // purpose  :
295 //=======================================================================
296 void CurveCreator_Utils::constructShape(
297   const CurveCreator_ICurve* theCurve, TopoDS_Shape& theShape)
298 {
299   BRep_Builder aBuilder;
300   TopoDS_Compound aShape;
301   aBuilder.MakeCompound(aShape);
302   const int aSectionCount = theCurve->getNbSections();
303   for (int aSectionI = 0; aSectionI < aSectionCount; ++aSectionI)
304   {
305     const int aTmpPointCount = theCurve->getNbPoints(aSectionI);
306     if (aTmpPointCount == 0)
307     {
308       continue;
309     }
310
311     // Get the different points.
312     Handle(TColgp_HArray1OfPnt) aPoints = theCurve->GetDifferentPoints( aSectionI );
313     const int aPointCount = aPoints->Length();
314     const bool isClosed = theCurve->isClosed(aSectionI);
315
316     // Add the vertices to the shape.
317     for (Standard_Integer aPN = 1; aPN <= aPointCount; ++aPN)
318     {
319       aBuilder.Add(aShape, BRepBuilderAPI_MakeVertex(aPoints->Value(aPN)));
320     }
321
322     // Add the wire to the shape.
323     const bool isPolyline =
324       (theCurve->getSectionType(aSectionI) == CurveCreator::Polyline);
325     const TopoDS_Wire aWire = ConstructWire(aPoints, isPolyline, isClosed);
326     if (!aWire.IsNull())
327     {
328       aBuilder.Add(aShape, aWire);
329     }
330   }
331   theShape = aShape;
332 }
333
334 /**
335  * This is an intermediate structure for curve construction.
336  */
337 struct Section3D
338 {
339   Section3D() : myIsClosed(false), myIsBSpline(false)
340   { }
341
342   bool                        myIsClosed;
343   bool                        myIsBSpline;
344   Handle(TColgp_HArray1OfPnt) myPoints;
345 };
346
347 //=======================================================================
348 // function : constructCurve
349 // purpose  : 
350 //=======================================================================
351 bool CurveCreator_Utils::constructCurve
352                       (const TopoDS_Shape        theShape,
353                              CurveCreator_Curve *theCurve,
354                              gp_Ax3             &theLocalCS)
355 {
356   if (theShape.IsNull()) {
357     return false;
358   }
359
360   // Collect wires or vertices from shape.
361   TopTools_ListOfShape aWOrV;
362   TopAbs_ShapeEnum     aType = theShape.ShapeType();
363
364   if (aType == TopAbs_WIRE || aType == TopAbs_VERTEX) {
365     aWOrV.Append(theShape);
366   } else if (aType == TopAbs_COMPOUND) {
367     TopoDS_Iterator aShIter(theShape);
368
369     for (; aShIter.More(); aShIter.Next()) {
370       const TopoDS_Shape &aSubShape = aShIter.Value();
371
372       aType = aSubShape.ShapeType();
373
374       if (aType == TopAbs_WIRE || aType == TopAbs_VERTEX) {
375         aWOrV.Append(aSubShape);
376       } else {
377         // Only subshapes of types wire or vertex are supported.
378         return false;
379       }
380     }
381   } else {
382     // Only wire (vertex) or compound of wires (vertices) are supported.
383     return false;
384   }
385
386   // Treat each wire or vertex. Get points, compute the working plane.
387   gp_Pln                             aPlane;
388   Standard_Integer                   aPlaneStatus = PLN_FREE;
389   TopTools_ListIteratorOfListOfShape anIter(aWOrV);
390   std::list<Section3D>               aListSec;
391
392   for (; anIter.More(); anIter.Next()) {
393     Section3D aSec3D;
394
395     aSec3D.myPoints = CurveCreator_Utils::getPoints
396       (anIter.Value(), aSec3D.myIsClosed, aSec3D.myIsBSpline);
397
398     if (aSec3D.myPoints.IsNull()) {
399       return false;
400     }
401
402     aListSec.push_back(aSec3D);
403
404     if (aPlaneStatus != PLN_FIXED) {
405       // Compute plane
406       CurveCreator_Utils::FindPlane(aSec3D.myPoints, aPlane, aPlaneStatus);
407     }
408   }
409
410   // Check if it is possible to change a computed coordinate system by
411   // XOY, XOZ or YOZ or parallel to them.
412   gp_Pnt        aO(0., 0., 0.);
413   gp_Dir        aNDir(0., 0., 1.);
414   gp_Dir        aXDir(1., 0., 0.);
415   gp_Ax3        anAxis;
416   Standard_Real aTolAng = Precision::Confusion(); // Angular() is too small.
417
418   switch (aPlaneStatus) {
419     case PLN_ORIGIN:
420       {
421         // Change the location.
422         aO.SetZ(aPlane.Location().Z());
423         anAxis.SetLocation(aO);
424         aPlane.SetPosition(anAxis);
425       }
426       break;
427     case PLN_OX:
428       {
429         // Fixed origin + OX axis
430         const gp_Dir &aPlnX = aPlane.Position().XDirection();
431
432         if (Abs(aPlnX.Z()) <= aTolAng) {
433           // Make a coordinate system parallel to XOY.
434           aO.SetZ(aPlane.Location().Z());
435           anAxis.SetLocation(aO);
436           aPlane.SetPosition(anAxis);
437         } else if (Abs(aPlnX.Y()) <= aTolAng) {
438           // Make a coordinate system parallel to XOZ.
439           aO.SetY(aPlane.Location().Y());
440           aNDir.SetCoord(0., 1., 0.);
441           aXDir.SetCoord(0., 0., 1.);
442           anAxis = gp_Ax3(aO, aNDir, aXDir);
443           aPlane.SetPosition(anAxis);
444         } else if (Abs(aPlnX.X()) <= aTolAng) {
445           // Make a coordinate system parallel to YOZ.
446           aO.SetX(aPlane.Location().X());
447           aNDir.SetCoord(1., 0., 0.);
448           aXDir.SetCoord(0., 1., 0.);
449           anAxis = gp_Ax3(aO, aNDir, aXDir);
450           aPlane.SetPosition(anAxis);
451         }
452       }
453       break;
454     case PLN_FIXED:
455       {
456         const gp_Dir &aPlnN = aPlane.Position().Direction();
457         gp_Dir        aYDir(0., 1., 0.);
458
459         if (aPlnN.IsParallel(aNDir, aTolAng)) {
460           // Make a coordinate system parallel to XOY.
461           aO.SetZ(aPlane.Location().Z());
462           anAxis.SetLocation(aO);
463           aPlane.SetPosition(anAxis);
464         } else if (aPlnN.IsParallel(aYDir, aTolAng)) {
465           // Make a coordinate system parallel to XOZ.
466           aO.SetY(aPlane.Location().Y());
467           aNDir.SetCoord(0., 1., 0.);
468           aXDir.SetCoord(0., 0., 1.);
469           anAxis = gp_Ax3(aO, aNDir, aXDir);
470           aPlane.SetPosition(anAxis);
471         } else if (aPlnN.IsParallel(aXDir, aTolAng)) {
472           // Make a coordinate system parallel to YOZ.
473           aO.SetX(aPlane.Location().X());
474           aNDir.SetCoord(1., 0., 0.);
475           aXDir.SetCoord(0., 1., 0.);
476           anAxis = gp_Ax3(aO, aNDir, aXDir);
477           aPlane.SetPosition(anAxis);
478         }
479       }
480       break;
481     case PLN_FREE:
482     default:
483       // Use XOY plane.
484       aPlane.SetPosition(anAxis);
485       break;
486   }
487
488   // Compute 2d points.
489   std::list<Section3D>::const_iterator aSecIt = aListSec.begin();
490   Standard_Real                        aTolConf2 =
491     Precision::Confusion()*Precision::Confusion();
492   Standard_Real                        aX;
493   Standard_Real                        aY;
494
495   for (; aSecIt != aListSec.end(); ++aSecIt) {
496     Standard_Integer          i;
497     CurveCreator::Coordinates aCoords;
498
499     for (i = aSecIt->myPoints->Lower(); i <= aSecIt->myPoints->Upper(); ++i) {
500       const gp_Pnt &aPnt = aSecIt->myPoints->Value(i);
501
502       if (aPlane.SquareDistance(aPnt) > aTolConf2) {
503         // The point doesn't lie on the plane.
504         return false;
505       }
506
507       ElSLib::Parameters(aPlane, aPnt, aX, aY);
508       aCoords.push_back(aX);
509       aCoords.push_back(aY);
510     }
511
512     // Add a new section to the curve.
513     const std::string               aSecName =
514       CurveCreator_UtilsICurve::getUniqSectionName(theCurve);
515     const CurveCreator::SectionType aSecType = aSecIt->myIsBSpline ?
516       CurveCreator::Spline : CurveCreator::Polyline;
517
518     theCurve->addSectionInternal(aSecName, aSecType,
519                                  aSecIt->myIsClosed, aCoords);
520   }
521
522   // Set the local coordinate system.
523   theLocalCS = aPlane.Position();
524
525   return true;
526 }
527
528 class CompareSectionToPoint
529 {
530 public:
531   CompareSectionToPoint( const int theISection = -1, const int theIPoint = -1 )
532     : mySectionId( theISection ), myPointId( theIPoint ) {};
533   ~CompareSectionToPoint() {}
534
535   bool operator < ( const CompareSectionToPoint& theOther ) const
536   {
537     bool isLess = mySectionId < theOther.mySectionId;
538     if ( !isLess && mySectionId == theOther.mySectionId )
539       isLess = myPointId < theOther.myPointId;
540     return isLess;
541   }
542
543 private:
544   int mySectionId;
545   int myPointId;
546 };
547
548
549 void CurveCreator_Utils::getSelectedPoints( Handle(AIS_InteractiveContext) theContext,
550                                             const CurveCreator_ICurve* theCurve,
551                                             CurveCreator_ICurve::SectionToPointList& thePoints )
552 {
553   thePoints.clear();
554
555   std::list<double> aSelectedPoints;
556   gp_Pnt aPnt;
557   std::map<CompareSectionToPoint, int> aPointsMap;
558
559   CurveCreator_ICurve::SectionToPointList aPoints;
560   for ( theContext->InitSelected(); theContext->MoreSelected(); theContext->NextSelected() ) {
561     TopoDS_Vertex aVertex;
562     TopoDS_Shape aShape = theContext->SelectedShape();
563     if ( !aShape.IsNull() && aShape.ShapeType() == TopAbs_VERTEX )
564       aVertex = TopoDS::Vertex( theContext->SelectedShape() );
565
566     if ( aVertex.IsNull() )
567       continue;
568     aPnt = BRep_Tool::Pnt( aVertex );
569
570     CurveCreator_UtilsICurve::findSectionsToPoints( theCurve, aPnt.X(), aPnt.Y(), aPoints );
571     CurveCreator_ICurve::SectionToPointList::const_iterator anIt = aPoints.begin(),
572                                                             aLast = aPoints.end();
573     CompareSectionToPoint aPoint;
574     for ( ; anIt != aLast; anIt++ ) {
575       aPoint = CompareSectionToPoint( (*anIt).first, (*anIt).second );
576       if ( aPointsMap.find( aPoint ) != aPointsMap.end() )
577         continue;
578       aPointsMap[aPoint] = 0;
579
580       thePoints.push_back( *anIt );
581     }
582   }
583 }
584
585 void CurveCreator_Utils::setSelectedPoints( Handle(AIS_InteractiveContext) theContext,
586                                             const CurveCreator_ICurve* theCurve,
587                                             const CurveCreator_ICurve::SectionToPointList& thePoints )
588 {
589   if ( !theCurve )
590     return;
591
592   Handle(AIS_InteractiveObject) anAIS = theCurve->getAISObject();
593   if ( anAIS.IsNull() )
594     return;
595   Handle(AIS_Shape) anAISShape = Handle(AIS_Shape)::DownCast( anAIS );
596   if ( anAISShape.IsNull() )
597     return;
598
599   //ASL: we convert list of point indices to list of points coordinates
600   int aSize = thePoints.size();
601   std::vector<gp_Pnt> aPntsToSelect( aSize );
602
603   CurveCreator_ICurve::SectionToPointList::const_iterator
604                      aPIt = thePoints.begin(), aPLast = thePoints.end();
605   CurveCreator_ICurve::SectionToPoint aSToPoint;
606   for( int i=0; aPIt != aPLast; aPIt++, i++ )
607   {
608     gp_Pnt aPntToSelect;
609     CurveCreator_UtilsICurve::getPoint( theCurve, aPIt->first, aPIt->second, aPntToSelect );
610     aPntsToSelect[i] = aPntToSelect;
611   }
612
613   theContext->ClearSelected( Standard_False );
614   //ASL: we switch off automatic highlight to improve performance of selection
615   theContext->SetAutomaticHilight( Standard_False );
616
617   Handle(SelectMgr_Selection) aSelection = anAISShape->Selection( AIS_Shape::SelectionMode( TopAbs_VERTEX ) );
618
619   CurveCreator_ICurve::SectionToPointList::const_iterator anIt = thePoints.begin(),
620                                                           aLast = thePoints.end();
621   bool isFound = false;
622   for( int i=0; i<aSize; i++ )
623   {
624     for( aSelection->Init(); aSelection->More(); aSelection->Next() )
625     {
626       const Handle(SelectMgr_SensitiveEntity) aHSenEntity = aSelection->Sensitive();
627       if( aHSenEntity.IsNull() )
628         continue;
629       Handle(SelectBasics_SensitiveEntity) aSenEntity = aHSenEntity->BaseSensitive();
630
631       Handle(Select3D_SensitivePoint) aSenPnt = Handle(Select3D_SensitivePoint)::DownCast( aSenEntity );
632   
633       gp_Pnt anOwnerPnt = aSenPnt->Point();
634       Handle(SelectMgr_EntityOwner) anOwner = Handle(SelectMgr_EntityOwner)::DownCast( aSenPnt->OwnerId() );
635
636       bool isIntersect = fabs( aPntsToSelect[i].X() - anOwnerPnt.X() ) < LOCAL_SELECTION_TOLERANCE &&
637                          fabs( aPntsToSelect[i].Y() - anOwnerPnt.Y() ) < LOCAL_SELECTION_TOLERANCE;
638       if( isIntersect )
639       {
640         theContext->AddOrRemoveSelected( anOwner, Standard_False );
641         break;
642       }
643     }
644   }
645
646   //ASL: we switch on again automatic highlight (otherwise selection will not be shown)
647   //     and call HilightPicked to draw selected owners
648   theContext->SetAutomaticHilight( Standard_True );
649   theContext->LocalContext()->HilightPicked( Standard_True );
650 }
651
652 //=======================================================================
653 // function : setLocalPointContext
654 // purpose  : Open/close the viewer local context
655 //=======================================================================
656 void CurveCreator_Utils::setLocalPointContext( const CurveCreator_ICurve* theCurve,
657                                                Handle(AIS_InteractiveContext) theContext,
658                                                const bool theOpen )
659 {
660   if ( !theContext )
661     return;
662
663   if ( theOpen ) {
664     // Open local context if there is no one
665     if ( !theContext->HasOpenedContext() ) {
666       theContext->ClearCurrents( false );
667       theContext->OpenLocalContext( false/*use displayed objects*/, true/*allow shape decomposition*/ );
668     }
669     // load the curve AIS object to the local context with the point selection
670     Handle(AIS_InteractiveObject) anAIS = theCurve->getAISObject();
671     if ( !anAIS.IsNull() )
672     {
673       if ( anAIS->IsKind( STANDARD_TYPE( AIS_Shape ) ) )
674       {
675         theContext->Load( anAIS, -1/*selection mode*/, true/*allow decomposition*/ );
676         theContext->Activate( anAIS, AIS_Shape::SelectionMode( (TopAbs_ShapeEnum)TopAbs_VERTEX ) );
677       }
678     }
679   }
680   else {
681     if ( theContext->HasOpenedContext() )
682       theContext->CloseAllContexts( Standard_True );
683   }
684 }
685
686 bool CurveCreator_Utils::pointOnObject( Handle(V3d_View) theView,
687                                         Handle(AIS_InteractiveObject) theObject,
688                                         const int theX, const int theY,
689                                         gp_Pnt& thePoint,
690                                         gp_Pnt& thePoint1, gp_Pnt& thePoint2 )
691 {
692   bool isFullFound = false;
693
694   if ( theObject.IsNull() || theView.IsNull() )
695     return isFullFound;
696   Handle(AIS_Shape) aShape = Handle(AIS_Shape)::DownCast( theObject );
697   if ( aShape.IsNull() )
698     return isFullFound;
699   const TopoDS_Compound& aCompound = TopoDS::Compound( aShape->Shape() );
700   if ( aCompound.IsNull() )
701     return isFullFound;
702
703   gp_Pnt aCurPoint, aCurPoint1, aCurPoint2;
704   gp_Pnt aFoundPoint, aFoundPnt1, aFoundPnt2;
705   Standard_Real aParameter;
706   bool isFound = false;
707   int aDelta, aMinDelta = 2*SCENE_PIXEL_PROJECTION_TOLERANCE*SCENE_PIXEL_PROJECTION_TOLERANCE;
708   TopExp_Explorer anExp( aCompound, TopAbs_EDGE );
709   for ( ; anExp.More(); anExp.Next())
710   {
711     const TopoDS_Edge& anEdge = TopoDS::Edge(anExp.Current());
712     if ( anEdge.IsNull() )
713       continue;
714     Standard_Real aFirst, aLast;
715     Handle(Geom_Curve) aCurve = BRep_Tool::Curve( anEdge, aFirst, aLast );
716     if ( aCurve->IsKind( STANDARD_TYPE(Geom_BSplineCurve) ) ) {
717       Handle(Geom_BSplineCurve) aBSplineCurve =
718                           Handle(Geom_BSplineCurve)::DownCast( aCurve );
719       if ( !aBSplineCurve.IsNull() ) {
720         isFound = hasProjectPointOnCurve( theView, theX, theY, aBSplineCurve,
721                                           aParameter, aDelta );
722         if ( isFound ) {
723           aCurPoint = aBSplineCurve->Value( aParameter );
724           Standard_Integer anI1, anI2;
725           aBSplineCurve->LocateU( aParameter, LOCAL_SELECTION_TOLERANCE, anI1, anI2 );
726           aCurPoint1 = aBSplineCurve->Value( aBSplineCurve->Knot( anI1 ) );
727           aCurPoint2 = aBSplineCurve->Value( aBSplineCurve->Knot( anI2 ) );
728         }
729       }
730     }
731     else { // a curve built on a polyline edge
732       Handle(Geom_Line) aGLine = Handle(Geom_Line)::DownCast( aCurve );
733       if ( aGLine.IsNull() )
734         continue;
735       isFound = hasProjectPointOnCurve( theView, theX, theY, aGLine, aParameter,
736                                         aDelta );
737       if ( isFound ) {
738         aCurPoint = aGLine->Value( aParameter );
739         TopoDS_Vertex V1, V2;
740         TopExp::Vertices( anEdge, V1, V2, Standard_True );
741         if ( V1.IsNull() || V2.IsNull() )
742           continue;
743         aCurPoint1 = BRep_Tool::Pnt(V1);
744         aCurPoint2 = BRep_Tool::Pnt(V2);
745
746         // check that the projected point is on the bounded curve
747         gp_Vec aVec1( aCurPoint1, aCurPoint );
748         gp_Vec aVec2( aCurPoint2, aCurPoint );
749         isFound = fabs( aVec1.Angle( aVec2 ) - M_PI ) < LOCAL_SELECTION_TOLERANCE;
750       }
751     }
752     if ( isFound && aMinDelta >= aDelta ) {
753       aMinDelta = aDelta;
754
755       isFullFound = true;
756       aFoundPnt1 = aCurPoint1;
757       aFoundPnt2 = aCurPoint2;
758       aFoundPoint = aCurPoint;
759     }
760   }
761   if ( isFullFound ) {
762     int aX, anY, aX1, anY1, aX2, anY2;
763     int aDelta;
764     CurveCreator_Utils::ConvertPointToClick( aFoundPoint, theView, aX, anY );
765     CurveCreator_Utils::ConvertPointToClick( aFoundPnt1, theView, aX1, anY1 );
766     CurveCreator_Utils::ConvertPointToClick( aFoundPnt2, theView, aX2, anY2 );
767
768     isFullFound = !isEqualPixels( aX, anY, aX1, anY1, SCENE_PIXEL_POINT_TOLERANCE, aDelta ) &&
769                   !isEqualPixels( aX, anY, aX2, anY2, SCENE_PIXEL_POINT_TOLERANCE, aDelta );
770     if ( isFullFound ) {
771       thePoint = aFoundPoint;
772       thePoint1 = aFoundPnt1;
773       thePoint2 = aFoundPnt2;
774     }
775   }
776   return isFullFound;
777 }
778
779 bool CurveCreator_Utils::hasProjectPointOnCurve( Handle(V3d_View) theView,
780                                                  const int theX, const int theY,
781                                                  const Handle(Geom_Curve)& theCurve,
782                                                  Standard_Real& theParameter,
783                                                  int& theDelta )
784 {
785   bool isFound = false;
786   if ( theView.IsNull() )
787     return isFound;
788
789   gp_Pnt aPoint = CurveCreator_Utils::ConvertClickToPoint( theX, theY, theView );
790
791   GeomAPI_ProjectPointOnCurve aProj( aPoint, theCurve );
792   Standard_Integer aNbPoint = aProj.NbPoints();
793   if (aNbPoint > 0) {
794     for (Standard_Integer j = 1; j <= aNbPoint && !isFound; j++) {
795       gp_Pnt aNewPoint = aProj.Point( j );
796       theParameter = aProj.Parameter( j );
797
798       int aX, anY;
799       CurveCreator_Utils::ConvertPointToClick( aNewPoint, theView, aX, anY );
800
801       isFound = isEqualPixels( aX, anY, theX, theY, SCENE_PIXEL_PROJECTION_TOLERANCE, theDelta );
802     }
803   }
804   return isFound;
805 }
806
807 bool CurveCreator_Utils::isEqualPixels( const int theX, const int theY, const int theOtherX,
808                                         const int theOtherY, const double theTolerance, int& theDelta )
809 {
810   int aXDelta = abs( theX - theOtherX );
811   int anYDelta = abs( theY - theOtherY );
812
813   theDelta = aXDelta*aXDelta + anYDelta*anYDelta;
814
815   return aXDelta < theTolerance && anYDelta < theTolerance;
816 }
817
818 bool CurveCreator_Utils::isEqualPoints( const gp_Pnt& thePoint, const gp_Pnt& theOtherPoint )
819 {
820   return theOtherPoint.IsEqual( thePoint, LOCAL_SELECTION_TOLERANCE );
821 }
822
823 //=======================================================================
824 // function : getPoints
825 // purpose  : 
826 //=======================================================================
827 Handle(TColgp_HArray1OfPnt) CurveCreator_Utils::getPoints
828                   (const TopoDS_Shape &theShape,
829                          bool         &IsClosed,
830                          bool         &IsBSpline)
831 {
832   Handle(TColgp_HArray1OfPnt) aResult;
833
834   IsClosed  = false;
835   IsBSpline = false;
836
837   if (theShape.IsNull()) {
838     return aResult;
839   }
840
841   const TopAbs_ShapeEnum aShType = theShape.ShapeType();
842
843   if (aShType == TopAbs_VERTEX) {
844     // There is a single point.
845     gp_Pnt aPnt = BRep_Tool::Pnt(TopoDS::Vertex(theShape));
846
847     aResult = new TColgp_HArray1OfPnt(1, 1, aPnt);
848
849     return aResult;
850   } else if (aShType != TopAbs_WIRE) {
851     // The shape is neither a vertex nor a wire.
852     return aResult;
853   }
854
855   // Treat wire.
856   BRepTools_WireExplorer anExp(TopoDS::Wire(theShape));
857
858   if (!anExp.More()) {
859     // Empty wires are not allowed.
860     return aResult;
861   }
862
863   // Treat the first edge.
864   TopoDS_Edge        anEdge = anExp.Current();
865   Handle(Geom_Curve) aCurve = GetCurve(anEdge);
866
867   if (aCurve.IsNull()) {
868     return aResult;
869   }
870
871   // Check the curve type.
872   Handle(Standard_Type) aType     = aCurve->DynamicType();
873
874   if (aType == STANDARD_TYPE(Geom_BSplineCurve)) {
875     IsBSpline = true;
876   } else if (aType != STANDARD_TYPE(Geom_Line)) {
877     // The curve is neither a line or a BSpline. It is not valid.
878     return aResult;
879   }
880
881   // Go to the next edge.
882   TopoDS_Vertex aFirstVtx = anExp.CurrentVertex();
883
884   anExp.Next();
885
886   if (IsBSpline)
887   {
888     // There should be a single BSpline curve in the wire.
889     if (anExp.More()) {
890       return aResult;
891     }
892
893     // Construct a section from poles of BSpline.
894     Handle(Geom_BSplineCurve) aBSplCurve =
895       Handle(Geom_BSplineCurve)::DownCast(aCurve);
896
897     // Check if the edge is valid. It should not be based on trimmed curve.
898     gp_Pnt aCP[2] = { aBSplCurve->StartPoint(), aBSplCurve->EndPoint() };
899     TopoDS_Vertex aV[2];
900     Standard_Integer i;
901
902     TopExp::Vertices(anEdge, aV[0], aV[1]);
903
904     for (i = 0; i < 2; i++) {
905       gp_Pnt        aPnt = BRep_Tool::Pnt(aV[i]);
906       Standard_Real aTol = BRep_Tool::Tolerance(aV[i]);
907
908       if (!aPnt.IsEqual(aCP[i], aTol)) {
909         return aResult;
910       }
911     }
912
913     IsClosed = aV[0].IsSame(aV[1]) ? true : false;
914     
915     Standard_Integer aNbPoints = aBSplCurve->NbKnots();
916     TColStd_Array1OfReal aKnots(1, aNbPoints);
917     aBSplCurve->Knots(aKnots);
918
919     // Don't consider the last point as it coincides with the first
920     if (IsClosed)
921       --aNbPoints;
922
923     aResult = new TColgp_HArray1OfPnt(1, aNbPoints);
924     for (i = 1; i <= aNbPoints; ++i)
925       aResult->SetValue(i, aBSplCurve->Value( aKnots.Value(i) ));
926   }
927   else
928   {
929     // This is a polyline.
930     TopTools_ListOfShape aVertices;
931     Standard_Integer     aNbVtx = 1;
932
933
934     aVertices.Append(aFirstVtx);
935
936     for (; anExp.More(); anExp.Next(), ++aNbVtx) {
937       anEdge = anExp.Current();
938       aCurve = GetCurve(anEdge);
939
940       if (aCurve.IsNull()) {
941         return aResult;
942       }
943
944       aType = aCurve->DynamicType();
945
946       if (aType != STANDARD_TYPE(Geom_Line)) {
947         // The curve is not a line. It is not valid.
948         return aResult;
949       }
950
951       // Add the current vertex to the list.
952       aVertices.Append(anExp.CurrentVertex());
953     }
954
955     // Check if the section is closed.
956     TopoDS_Vertex aLastVtx = TopExp::LastVertex(anEdge, Standard_True);
957
958     IsClosed = aFirstVtx.IsSame(aLastVtx) ? true : false;
959
960     // Store a last vertex
961     if (!IsClosed)
962     {
963       aVertices.Append(aLastVtx);
964       aNbVtx++;
965     }
966
967     // Fill the array of points.
968     aResult = new TColgp_HArray1OfPnt(1, aNbVtx);
969
970     Standard_Integer i;
971     TopTools_ListIteratorOfListOfShape aVtxIter(aVertices);
972
973     for (i = 1; aVtxIter.More(); aVtxIter.Next(), ++i) {
974       gp_Pnt aPnt = BRep_Tool::Pnt(TopoDS::Vertex(aVtxIter.Value()));
975
976       aResult->SetValue(i, aPnt);
977     }
978   }
979
980   return aResult;
981 }
982 //=======================================================================
983 // function : FindPlane
984 // purpose  : 
985 //=======================================================================
986 void CurveCreator_Utils::FindPlane
987                        (const Handle(TColgp_HArray1OfPnt) &thePoints,
988                               gp_Pln                     &thePlane,
989                               Standard_Integer           &thePlnStatus)
990 {
991   if (thePoints.IsNull() || thePlnStatus == PLN_FIXED) {
992     // The plane can't be defined or is fixed. Nothing to change.
993     return;
994   }
995
996   Standard_Integer    i;
997   const Standard_Real aTolConf = Precision::Confusion();
998
999   for (i = thePoints->Lower(); i <= thePoints->Upper(); ++i) {
1000     const gp_Pnt &aPnt = thePoints->Value(i);
1001
1002     switch (thePlnStatus) {
1003       case PLN_FREE:
1004         // Fix the origin.
1005         thePlane.SetLocation(aPnt);
1006         thePlnStatus = PLN_ORIGIN;
1007         break;
1008       case PLN_ORIGIN:
1009         {
1010           // Fix origin + OX axis
1011           const gp_Pnt &aPlnLoc = thePlane.Location();
1012
1013           if (!aPnt.IsEqual(aPlnLoc, aTolConf)) {
1014             // Set the X axis.
1015             gp_Dir aXDir(aPnt.XYZ().Subtracted(aPlnLoc.XYZ()));
1016             gp_Ax3 aXNorm(aPlnLoc, aXDir);
1017             gp_Ax3 aNewPlnPos(aPlnLoc, aXNorm.XDirection(), aXNorm.Direction());
1018
1019             thePlane.SetPosition(aNewPlnPos);
1020             thePlnStatus = PLN_OX;
1021           }
1022         }
1023         break;
1024       case PLN_OX:
1025         {
1026           // Fix OY axis
1027           gp_Lin aXLin(thePlane.XAxis());
1028           Standard_Real aSqrDist = aXLin.SquareDistance(aPnt);
1029
1030           if (aSqrDist > aTolConf*aTolConf) {
1031             // Compute main axis.
1032             const gp_Pnt &aPlnLoc = thePlane.Location();
1033             gp_Dir        aDir(aPnt.XYZ().Subtracted(aPlnLoc.XYZ()));
1034             gp_Ax3        aXNorm(aPlnLoc, aXLin.Direction(), aDir);
1035             gp_Ax3        aNewPlnPos(aPlnLoc, aXNorm.YDirection(),
1036                                      aXNorm.Direction());
1037
1038             thePlane.SetPosition(aNewPlnPos);
1039             thePlnStatus = PLN_FIXED;
1040             return;
1041           }
1042         }
1043         break;
1044       default:
1045         return;
1046     }
1047   }
1048 }