0023964: Extrema_ExtXX::Point methods might return constant reference instead of...
[occt.git] / src / Extrema / Extrema_GenExtPS.cxx
1 // Created on: 1995-07-18
2 // Created by: Modelistation
3 // Copyright (c) 1995-1999 Matra Datavision
4 // Copyright (c) 1999-2012 OPEN CASCADE SAS
5 //
6 // The content of this file is subject to the Open CASCADE Technology Public
7 // License Version 6.5 (the "License"). You may not use the content of this file
8 // except in compliance with the License. Please obtain a copy of the License
9 // at http://www.opencascade.org and read it completely before using this file.
10 //
11 // The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
12 // main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
13 //
14 // The Original Code and all software distributed under the License is
15 // distributed on an "AS IS" basis, without warranty of any kind, and the
16 // Initial Developer hereby disclaims all such warranties, including without
17 // limitation, any warranties of merchantability, fitness for a particular
18 // purpose or non-infringement. Please see the License for the specific terms
19 // and conditions governing the rights and limitations under the License.
20
21
22 //  Modified by skv - Thu Sep 30 15:21:07 2004 OCC593
23
24
25 #include <Extrema_GenExtPS.ixx>
26 #include <StdFail_NotDone.hxx>
27 #include <Standard_OutOfRange.hxx>
28 #include <TColStd_Array2OfInteger.hxx>
29 #include <TColStd_Array2OfReal.hxx>
30 #include <TColgp_Array2OfPnt.hxx>
31 #include <math_FunctionSetRoot.hxx>
32 #include <math_Vector.hxx>
33 #include <math_NewtonFunctionSetRoot.hxx>
34 #include <GeomAbs_IsoType.hxx>
35 #include <Bnd_Sphere.hxx>
36 #include <Extrema_HUBTreeOfSphere.hxx>
37 #include <Extrema_ExtFlag.hxx>
38 #include <Bnd_Array1OfSphere.hxx>
39 #include <Bnd_HArray1OfSphere.hxx>
40 #include <Precision.hxx>
41 #include <Geom_OffsetSurface.hxx>
42 #include <Geom_RectangularTrimmedSurface.hxx>
43 #include <Geom_BSplineSurface.hxx>
44 #include <Geom_BezierSurface.hxx>
45 #include <Adaptor3d_HSurface.hxx>
46 #include <Adaptor3d_HCurve.hxx>
47 #include <Adaptor3d_Curve.hxx>
48 #include <Geom_BSplineCurve.hxx>
49 #include <Geom_BezierCurve.hxx>
50 //IMPLEMENT_HARRAY1(Extrema_HArray1OfSphere)
51
52
53 class Bnd_SphereUBTreeSelector : public Extrema_UBTreeOfSphere::Selector
54 {
55  public:
56
57   Bnd_SphereUBTreeSelector (const Handle(Bnd_HArray1OfSphere)& theSphereArray,
58 Bnd_Sphere& theSol)
59     : myXYZ(0,0,0),
60       mySphereArray(theSphereArray),
61       mySol(theSol)
62   {
63   }
64
65   void DefineCheckPoint( const gp_Pnt& theXYZ )
66   { myXYZ = theXYZ; }
67
68   Bnd_Sphere& Sphere() const
69   { return mySol; }
70
71   virtual Standard_Boolean Reject( const Bnd_Sphere &theBnd ) const = 0;
72   
73   virtual Standard_Boolean Accept(const Standard_Integer& theObj) = 0;
74  protected:
75   gp_Pnt                              myXYZ;
76   const Handle(Bnd_HArray1OfSphere)&  mySphereArray;
77   Bnd_Sphere&                         mySol;
78  private:
79   void operator= (const Bnd_SphereUBTreeSelector&);
80
81 };
82
83 class Bnd_SphereUBTreeSelectorMin : public Bnd_SphereUBTreeSelector
84 {
85 public:
86   Bnd_SphereUBTreeSelectorMin (const Handle(Bnd_HArray1OfSphere)& theSphereArray,
87                 Bnd_Sphere& theSol)
88                 : Bnd_SphereUBTreeSelector(theSphereArray, theSol),
89                   myMinDist(RealLast())
90   {}
91   
92   void SetMinDist( const Standard_Real theMinDist )
93   { myMinDist = theMinDist; }
94
95   Standard_Real MinDist() const
96   { return myMinDist; }
97
98   Standard_Boolean Reject( const Bnd_Sphere &theBnd ) const
99   { 
100     Bnd_SphereUBTreeSelectorMin* me =
101       const_cast<Bnd_SphereUBTreeSelectorMin*>(this);
102     // myMinDist is decreased each time a nearer object is found
103     return theBnd.IsOut( myXYZ.XYZ(), me->myMinDist );
104   }
105
106   Standard_Boolean Accept(const Standard_Integer&);
107
108 private:
109         Standard_Real   myMinDist;
110 };
111
112 Standard_Boolean Bnd_SphereUBTreeSelectorMin::Accept(const Standard_Integer& theInd)
113 {
114   const Bnd_Sphere& aSph = mySphereArray->Value(theInd);
115   Standard_Real aCurDist;
116
117     if ( (aCurDist = aSph.SquareDistance(myXYZ.XYZ())) < mySol.SquareDistance(myXYZ.XYZ()) )
118     {
119       mySol = aSph;
120       if ( aCurDist < myMinDist ) 
121         myMinDist = aCurDist;
122
123       return Standard_True;
124     }
125
126   return Standard_False;
127 }
128
129 class Bnd_SphereUBTreeSelectorMax : public Bnd_SphereUBTreeSelector
130 {
131 public:
132   Bnd_SphereUBTreeSelectorMax (const Handle(Bnd_HArray1OfSphere)& theSphereArray,
133                 Bnd_Sphere& theSol)
134                 : Bnd_SphereUBTreeSelector(theSphereArray, theSol),
135                   myMaxDist(0)
136   {}
137
138   void SetMaxDist( const Standard_Real theMaxDist )
139   { myMaxDist = theMaxDist; }
140
141   Standard_Real MaxDist() const
142   { return myMaxDist; }
143
144   Standard_Boolean Reject( const Bnd_Sphere &theBnd ) const
145   { 
146     Bnd_SphereUBTreeSelectorMax* me =
147       const_cast<Bnd_SphereUBTreeSelectorMax*>(this);
148     // myMaxDist is decreased each time a nearer object is found
149     return theBnd.IsOut( myXYZ.XYZ(), me->myMaxDist );
150   }
151
152   Standard_Boolean Accept(const Standard_Integer&);
153
154 private:
155         Standard_Real   myMaxDist;
156 };
157
158 Standard_Boolean Bnd_SphereUBTreeSelectorMax::Accept(const Standard_Integer& theInd)
159 {
160   const Bnd_Sphere& aSph = mySphereArray->Value(theInd);
161   Standard_Real aCurDist;
162
163     if ( (aCurDist = aSph.SquareDistance(myXYZ.XYZ())) > mySol.SquareDistance(myXYZ.XYZ()) )
164     {
165       mySol = aSph;
166       if ( aCurDist > myMaxDist ) 
167         myMaxDist = aCurDist;
168
169       return Standard_True;
170     }
171
172   return Standard_False;
173 }
174
175 /*
176  * This function computes the point on surface parameters on edge.
177  * if it coincides with theParam0 or theParam1, it is returned.
178  */
179 static Extrema_POnSurfParams ComputeEdgeParameters
180       (const Standard_Boolean       IsUEdge,
181        const Extrema_POnSurfParams &theParam0,
182        const Extrema_POnSurfParams &theParam1,
183        const Adaptor3d_SurfacePtr  &theSurf,
184        const gp_Pnt                &thePoint,
185        const Standard_Real          theDiffTol)
186 {
187   const Standard_Real aSqrDist01 =
188     theParam0.Value().SquareDistance(theParam1.Value());
189
190   if (aSqrDist01 <= theDiffTol) {
191     // The points are confused. Get the first point and change its type.
192     return theParam0;
193   } else {
194     const Standard_Real aDiffDist =
195       Abs(theParam0.GetSqrDistance() - theParam1.GetSqrDistance());
196
197     if (aDiffDist >= aSqrDist01 - theDiffTol) {
198       // The shortest distance is one of the nodes.
199       if (theParam0.GetSqrDistance() > theParam1.GetSqrDistance()) {
200         // The shortest distance is the point 1.
201         return theParam1;
202       } else {
203         // The shortest distance is the point 0.
204         return theParam0;
205       }
206     } else {
207       // The shortest distance is inside the edge.
208       gp_XYZ aPoP(thePoint.XYZ().Subtracted(theParam0.Value().XYZ()));
209       gp_XYZ aPoP1(theParam1.Value().XYZ().Subtracted(theParam0.Value().XYZ()));
210       Standard_Real aRatio = aPoP.Dot(aPoP1)/aSqrDist01;
211       Standard_Real aU[2];
212       Standard_Real aV[2];
213
214       theParam0.Parameter(aU[0], aV[0]);
215       theParam1.Parameter(aU[1], aV[1]);
216   
217       Standard_Real aUPar = aU[0];
218       Standard_Real aVPar = aV[0];
219
220       if (IsUEdge) {
221         aUPar += aRatio*(aU[1] - aU[0]);
222       } else {
223         aVPar += aRatio*(aV[1] - aV[0]);
224       }
225
226       Extrema_POnSurfParams aParam(aUPar, aVPar, theSurf->Value(aUPar, aVPar));
227       Standard_Integer anIndices[2];
228
229       theParam0.GetIndices(anIndices[0], anIndices[1]);
230       aParam.SetElementType(IsUEdge ? Extrema_UIsoEdge : Extrema_VIsoEdge);
231       aParam.SetSqrDistance(thePoint.SquareDistance(aParam.Value()));
232       aParam.SetIndices(anIndices[0], anIndices[1]);
233
234       return aParam;
235     }
236   }
237 }
238
239 //=============================================================================
240
241 /*-----------------------------------------------------------------------------
242 Function:
243    Find all extremum distances between point P and surface
244   S using sampling (NbU,NbV).
245
246 Method:
247    The algorithm bases on the hypothesis that sampling is precise enough, 
248   if there exist N extreme distances between the point and the surface,
249   so there also exist N extrema between the point and the grid.
250   So, the algorithm consists in starting from extrema of the grid to find the 
251   extrema of the surface.
252   The extrema are calculated by the algorithm math_FunctionSetRoot with the
253   following arguments:
254   - F: Extrema_FuncExtPS created from P and S,
255   - UV: math_Vector the components which of are parameters of the extremum on the 
256     grid,
257   - Tol: Min(TolU,TolV), (Prov.:math_FunctionSetRoot does not autorize a vector)
258   - UVinf: math_Vector the components which of are lower limits of u and v,
259   - UVsup: math_Vector the components which of are upper limits of u and v.
260
261 Processing:
262   a- Creation of the table of distances (TbDist(0,NbU+1,0,NbV+1)):
263      The table is expanded at will; lines 0 and NbU+1 and
264      columns 0 and NbV+1 are initialized at RealFirst() or RealLast()
265      to simplify the tests carried out at stage b
266      (there is no need to test if the point is on border of the grid).
267   b- Calculation of extrema:
268      First the minimums and then the maximums are found. These 2 procedured 
269      pass in a similar way:
270   b.a- Initialization:
271       - 'borders' of table  TbDist (RealLast() in case of minimums
272         and  RealLast() in case of maximums),
273       - table TbSel(0,NbU+1,0,NbV+1) of selection of points for 
274         calculation of local extremum (0). When a point will selected,
275         it will not be selectable, as well as the ajacent points 
276         (8 at least). The corresponding addresses will be set to 1.
277   b.b- Calculation of minimums (or maximums):
278        All distances from table TbDist are parsed in a loop:
279       - search minimum (or maximum) in the grid,
280       - calculate extremum on the surface,
281       - update table TbSel.
282 -----------------------------------------------------------------------------*/
283
284 Extrema_GenExtPS::Extrema_GenExtPS() 
285 {
286   myDone = Standard_False;
287   myInit = Standard_False;
288   myFlag = Extrema_ExtFlag_MINMAX;
289   myAlgo = Extrema_ExtAlgo_Grad;
290 }
291
292
293 Extrema_GenExtPS::Extrema_GenExtPS (const gp_Pnt&          P,
294                               const Adaptor3d_Surface& S,
295                               const Standard_Integer NbU, 
296                               const Standard_Integer NbV,
297                               const Standard_Real    TolU, 
298                               const Standard_Real    TolV,
299                               const Extrema_ExtFlag F,
300                               const Extrema_ExtAlgo A) 
301   : myF (P,S), myFlag(F), myAlgo(A)
302 {
303   Initialize(S, NbU, NbV, TolU, TolV);
304   Perform(P);
305 }
306
307 Extrema_GenExtPS::Extrema_GenExtPS (const gp_Pnt&          P,
308                               const Adaptor3d_Surface& S,
309                               const Standard_Integer NbU, 
310                               const Standard_Integer NbV,
311                               const Standard_Real    Umin,
312                               const Standard_Real    Usup,
313                               const Standard_Real    Vmin,
314                               const Standard_Real    Vsup,
315                               const Standard_Real    TolU, 
316                               const Standard_Real    TolV,
317                               const Extrema_ExtFlag F,
318                               const Extrema_ExtAlgo A) 
319   : myF (P,S), myFlag(F), myAlgo(A)
320 {
321   Initialize(S, NbU, NbV, Umin, Usup, Vmin, Vsup, TolU, TolV);
322   Perform(P);
323 }
324
325
326 void Extrema_GenExtPS::Initialize(const Adaptor3d_Surface& S,
327                                   const Standard_Integer NbU, 
328                                   const Standard_Integer NbV,
329                                   const Standard_Real    TolU, 
330                                   const Standard_Real    TolV)
331 {
332   myumin = S.FirstUParameter();
333   myusup = S.LastUParameter();
334   myvmin = S.FirstVParameter();
335   myvsup = S.LastVParameter();
336   Initialize(S,NbU,NbV,myumin,myusup,myvmin,myvsup,TolU,TolV);
337 }
338
339
340 void Extrema_GenExtPS::Initialize(const Adaptor3d_Surface& S,
341                                   const Standard_Integer NbU, 
342                                   const Standard_Integer NbV,
343                                   const Standard_Real    Umin,
344                                   const Standard_Real    Usup,
345                                   const Standard_Real    Vmin,
346                                   const Standard_Real    Vsup,
347                                   const Standard_Real    TolU, 
348                                   const Standard_Real    TolV)
349 {
350   myS = (Adaptor3d_SurfacePtr)&S;
351   myusample = NbU;
352   myvsample = NbV;
353   mytolu = TolU;
354   mytolv = TolV;
355   myumin = Umin;
356   myusup = Usup;
357   myvmin = Vmin;
358   myvsup = Vsup;
359
360   if ((myusample < 2) ||
361       (myvsample < 2)) { Standard_OutOfRange::Raise(); }
362
363   myF.Initialize(S);
364
365   mySphereUBTree.Nullify();
366   myUParams.Nullify();
367   myVParams.Nullify();
368   myInit = Standard_False;
369 }
370
371 inline static void fillParams(const TColStd_Array1OfReal& theKnots,
372                               Standard_Integer theDegree,
373                               Standard_Real theParMin,
374                               Standard_Real theParMax,
375                               Handle_TColStd_HArray1OfReal& theParams,
376                               Standard_Integer theSample)
377 {
378   NCollection_Vector<Standard_Real> aParams;
379   Standard_Integer i = 1;
380   Standard_Real aPrevPar = theParMin;
381   aParams.Append(aPrevPar);
382   //calculation the array of parametric points depending on the knots array variation and degree of given surface
383   for ( ; i <  theKnots.Length() && theKnots(i) < (theParMax - Precision::PConfusion()); i++ )
384   {
385     if (  theKnots(i+1) < theParMin + Precision::PConfusion())
386       continue;
387
388     Standard_Real aStep = (theKnots(i+1) - theKnots(i))/Max(theDegree,2);
389     Standard_Integer k =1;
390     for( ; k <= theDegree ; k++)
391     {
392       Standard_Real aPar = theKnots(i) + k * aStep;
393       if(aPar > theParMax - Precision::PConfusion())
394         break;
395       if(aPar > aPrevPar + Precision::PConfusion() )
396       {
397         aParams.Append(aPar);
398         aPrevPar = aPar;
399       }
400     }
401
402   }
403   aParams.Append(theParMax);
404   Standard_Integer nbPar = aParams.Length();
405   //in case of an insufficient number of points the grid will be built later 
406   if (nbPar < theSample)
407     return;
408   theParams = new TColStd_HArray1OfReal(1, nbPar );
409   for( i = 0; i < nbPar; i++)
410     theParams->SetValue(i+1,aParams(i));
411 }
412
413 void Extrema_GenExtPS::GetGridPoints( const Adaptor3d_Surface& theSurf)
414 {
415   //creation parametric points for BSpline and Bezier surfaces
416   //with taking into account of Degree and NbKnots of BSpline or Bezier geometry
417   if(theSurf.GetType() == GeomAbs_OffsetSurface)
418     GetGridPoints(theSurf.BasisSurface()->Surface());
419   //parametric points for BSpline surfaces
420   else if( theSurf.GetType() == GeomAbs_BSplineSurface) 
421   {
422     Handle(Geom_BSplineSurface) aBspl = theSurf.BSpline();
423     if(!aBspl.IsNull())
424     {
425       TColStd_Array1OfReal aUKnots(1, aBspl->NbUKnots());
426       aBspl->UKnots( aUKnots);
427       TColStd_Array1OfReal aVKnots(1, aBspl->NbVKnots());
428       aBspl->VKnots( aVKnots);
429       fillParams(aUKnots,aBspl->UDegree(),myumin, myusup, myUParams, myusample);
430       fillParams(aVKnots,aBspl->VDegree(),myvmin, myvsup, myVParams, myvsample);
431     }
432   }
433   //calculation parametric points for Bezier surfaces
434   else if(theSurf.GetType() == GeomAbs_BezierSurface)
435   {
436     Handle(Geom_BezierSurface) aBezier = theSurf.Bezier();
437     if(aBezier.IsNull())
438       return;
439
440     TColStd_Array1OfReal aUKnots(1,2);
441     TColStd_Array1OfReal aVKnots(1,2);
442     aBezier->Bounds(aUKnots(1), aUKnots(2), aVKnots(1), aVKnots(2));
443     fillParams(aUKnots, aBezier->UDegree(), myumin, myusup, myUParams, myusample);
444     fillParams(aVKnots, aBezier->VDegree(), myvmin, myvsup, myVParams, myvsample);
445
446   }
447   //creation points for surfaces based on BSpline or Bezier curves
448   else if(theSurf.GetType() == GeomAbs_SurfaceOfRevolution || 
449     theSurf.GetType() == GeomAbs_SurfaceOfExtrusion)
450   {
451     Handle(TColStd_HArray1OfReal) anArrKnots;
452     Standard_Integer aDegree = 0;
453     if(theSurf.BasisCurve()->Curve().GetType() == GeomAbs_BSplineCurve)
454     {
455       Handle(Geom_BSplineCurve) aBspl = theSurf.BasisCurve()->Curve().BSpline();
456       if(!aBspl.IsNull())
457       {
458         anArrKnots = new TColStd_HArray1OfReal(1,aBspl->NbKnots());
459         aBspl->Knots( anArrKnots->ChangeArray1() );
460         aDegree = aBspl->Degree();
461         
462       }
463
464     }
465     if(theSurf.BasisCurve()->Curve().GetType() == GeomAbs_BezierCurve)
466     {
467       Handle(Geom_BezierCurve) aBez = theSurf.BasisCurve()->Curve().Bezier();
468       if(!aBez.IsNull())
469       {
470         anArrKnots = new TColStd_HArray1OfReal(1,2);
471         anArrKnots->SetValue(1, aBez->FirstParameter());
472         anArrKnots->SetValue(2, aBez->LastParameter());
473         aDegree = aBez->Degree();
474         
475       }
476     }
477     if(anArrKnots.IsNull())
478       return;
479     if( theSurf.GetType() == GeomAbs_SurfaceOfRevolution )
480       fillParams( anArrKnots->Array1(), aDegree, myvmin, myvsup, myVParams, myvsample);
481     else
482       fillParams( anArrKnots->Array1(), aDegree, myumin, myusup, myUParams, myusample);
483   }
484   //update the number of points in sample
485   if(!myUParams.IsNull())
486     myusample = myUParams->Length();
487   if( !myVParams.IsNull())
488     myvsample = myVParams->Length();
489   
490 }
491
492 void Extrema_GenExtPS::BuildGrid(const gp_Pnt &thePoint)
493 {
494   Standard_Integer NoU, NoV;
495
496   //if grid was already built skip its creation
497   if (!myInit) {
498     //build parametric grid in case of a complex surface geometry (BSpline and Bezier surfaces)
499     GetGridPoints(*myS);
500     
501     //build grid in other cases 
502     if( myUParams.IsNull() )
503     {
504       Standard_Real PasU = myusup - myumin;
505       Standard_Real U0 = PasU / myusample / 100.;
506       PasU = (PasU - U0) / (myusample - 1);
507       U0 = U0/2. + myumin;
508       myUParams = new TColStd_HArray1OfReal(1,myusample );
509       Standard_Integer NoU;
510       Standard_Real U = U0;
511       for ( NoU = 1 ; NoU <= myusample; NoU++, U += PasU) 
512         myUParams->SetValue(NoU, U);
513     }
514   
515     if( myVParams.IsNull())
516     {
517       Standard_Real PasV = myvsup - myvmin;
518       Standard_Real V0 = PasV / myvsample / 100.;
519       PasV = (PasV - V0) / (myvsample - 1);
520       V0 = V0/2. + myvmin;
521       
522       myVParams = new TColStd_HArray1OfReal(1,myvsample );
523       Standard_Integer  NoV;
524       Standard_Real V = V0;
525      
526       for ( NoV = 1, V = V0; NoV <= myvsample; NoV++, V += PasV)
527         myVParams->SetValue(NoV, V);
528     }
529
530     //If flag was changed and extrema not reinitialized Extrema would fail
531     myPoints = new Extrema_HArray2OfPOnSurfParams
532       (0, myusample + 1, 0, myvsample + 1);
533     // Calculation of distances
534   
535     for ( NoU = 1 ; NoU <= myusample; NoU++ ) {
536       for ( NoV = 1 ; NoV <= myvsample; NoV++) {
537         gp_Pnt aP1 = myS->Value(myUParams->Value(NoU), myVParams->Value(NoV));
538         Extrema_POnSurfParams aParam
539           (myUParams->Value(NoU), myVParams->Value(NoV), aP1);
540
541         aParam.SetElementType(Extrema_Node);
542         aParam.SetIndices(NoU, NoV);
543         myPoints->SetValue(NoU, NoV, aParam);
544       }
545     }
546
547     // Fill boundary with negative square distance.
548     // It is used for computation of Maximum.
549     for (NoV = 0; NoV <= myvsample + 1; NoV++) {
550       myPoints->ChangeValue(0, NoV).SetSqrDistance(-1.);
551       myPoints->ChangeValue(myusample + 1, NoV).SetSqrDistance(-1.);
552     }
553
554     for (NoU = 1; NoU <= myusample; NoU++) {
555       myPoints->ChangeValue(NoU, 0).SetSqrDistance(-1.);
556       myPoints->ChangeValue(NoU, myvsample + 1).SetSqrDistance(-1.);
557     }
558     
559     myInit = Standard_True;
560   }
561
562   // Compute distances to mesh.
563   // Step 1. Compute distances to nodes.
564   for ( NoU = 1 ; NoU <= myusample; NoU++ ) {
565     for ( NoV = 1 ; NoV <= myvsample; NoV++) {
566       Extrema_POnSurfParams &aParam = myPoints->ChangeValue(NoU, NoV);
567
568       aParam.SetSqrDistance(thePoint.SquareDistance(aParam.Value()));
569     }
570   }
571
572   // For search of minimum compute distances to mesh.
573   if(myFlag == Extrema_ExtFlag_MIN || myFlag == Extrema_ExtFlag_MINMAX) 
574   {
575     // This is the tolerance of difference of squared values.
576     // No need to set it too small.
577     const Standard_Real aDiffTol = mytolu + mytolv;
578
579     // Step 2. Compute distances to edges.
580     // Assume UEdge(i, j) = { Point(i, j); Point(i + 1, j    ) }
581     // Assume VEdge(i, j) = { Point(i, j); Point(i,     j + 1) }
582     Handle(Extrema_HArray2OfPOnSurfParams) aUEdgePntParams =
583           new Extrema_HArray2OfPOnSurfParams(1, myusample - 1, 1, myvsample);
584     Handle(Extrema_HArray2OfPOnSurfParams) aVEdgePntParams =
585           new Extrema_HArray2OfPOnSurfParams(1, myusample, 1, myvsample - 1);
586
587     for ( NoU = 1 ; NoU <= myusample; NoU++ ) {
588       for ( NoV = 1 ; NoV <= myvsample; NoV++) {
589         const Extrema_POnSurfParams &aParam0 = myPoints->Value(NoU, NoV);
590
591         if (NoU < myusample) {
592           // Compute parameters to UEdge.
593           const Extrema_POnSurfParams &aParam1 = myPoints->Value(NoU + 1, NoV);
594           Extrema_POnSurfParams aUEdgeParam = ComputeEdgeParameters
595               (Standard_True, aParam0, aParam1, myS, thePoint, aDiffTol);
596
597           aUEdgePntParams->SetValue(NoU, NoV, aUEdgeParam);
598         }
599
600         if (NoV < myvsample) {
601           // Compute parameters to VEdge.
602           const Extrema_POnSurfParams &aParam1 = myPoints->Value(NoU, NoV + 1);
603           Extrema_POnSurfParams aVEdgeParam = ComputeEdgeParameters
604               (Standard_False, aParam0, aParam1, myS, thePoint, aDiffTol);
605
606           aVEdgePntParams->SetValue(NoU, NoV, aVEdgeParam);
607         }
608       }
609     }
610
611     // Step 3. Compute distances to faces.
612     // Assume myFacePntParams(i, j) =
613     // { Point(i, j); Point(i + 1, j); Point(i + 1, j + 1); Point(i, j + 1) }
614     //   Or
615     // { UEdge(i, j); VEdge(i + 1, j); UEdge(i, j + 1); VEdge(i, j) }
616     myFacePntParams = 
617           new Extrema_HArray2OfPOnSurfParams(0, myusample, 0, myvsample);
618     Standard_Real aSqrDist01;
619     Standard_Real aDiffDist;
620     Standard_Boolean isOut;
621
622     for ( NoU = 1 ; NoU < myusample; NoU++ ) {
623       for ( NoV = 1 ; NoV < myvsample; NoV++) {
624         const Extrema_POnSurfParams &aUE0 = aUEdgePntParams->Value(NoU, NoV);
625         const Extrema_POnSurfParams &aUE1 = aUEdgePntParams->Value(NoU, NoV+1);
626         const Extrema_POnSurfParams &aVE0 = aVEdgePntParams->Value(NoU, NoV);
627         const Extrema_POnSurfParams &aVE1 = aVEdgePntParams->Value(NoU+1, NoV);
628
629         aSqrDist01 = aUE0.Value().SquareDistance(aUE1.Value());
630         aDiffDist = Abs(aUE0.GetSqrDistance() - aUE1.GetSqrDistance());
631         isOut = Standard_False;
632
633         if (aDiffDist >= aSqrDist01 - aDiffTol) {
634           // The projection is outside the face.
635           isOut = Standard_True;
636         } else {
637           aSqrDist01 = aVE0.Value().SquareDistance(aVE1.Value());
638           aDiffDist = Abs(aVE0.GetSqrDistance() - aVE1.GetSqrDistance());
639
640           if (aDiffDist >= aSqrDist01 - aDiffTol) {
641             // The projection is outside the face.
642             isOut = Standard_True;
643           }
644         }
645
646         if (isOut) {
647           // Get the closest point on an edge.
648           const Extrema_POnSurfParams &aUEMin =
649             aUE0.GetSqrDistance() < aUE1.GetSqrDistance() ? aUE0 : aUE1;
650           const Extrema_POnSurfParams &aVEMin =
651             aVE0.GetSqrDistance() < aVE1.GetSqrDistance() ? aVE0 : aVE1;
652           const Extrema_POnSurfParams &aEMin =
653             aUEMin.GetSqrDistance() < aVEMin.GetSqrDistance() ? aUEMin : aVEMin;
654
655           myFacePntParams->SetValue(NoU, NoV, aEMin);
656         } else {
657           // Find closest point inside the face.
658           Standard_Real aU[2];
659           Standard_Real aV[2];
660           Standard_Real aUPar;
661           Standard_Real aVPar;
662
663           // Compute U parameter.
664           aUE0.Parameter(aU[0], aV[0]);
665           aUE1.Parameter(aU[1], aV[1]);
666           aUPar = 0.5*(aU[0] + aU[1]);
667           // Compute V parameter.
668           aVE0.Parameter(aU[0], aV[0]);
669           aVE1.Parameter(aU[1], aV[1]);
670           aVPar = 0.5*(aV[0] + aV[1]);
671
672           Extrema_POnSurfParams aParam(aUPar, aVPar, myS->Value(aUPar, aVPar));
673
674           aParam.SetElementType(Extrema_Face);
675           aParam.SetSqrDistance(thePoint.SquareDistance(aParam.Value()));
676           aParam.SetIndices(NoU, NoV);
677           myFacePntParams->SetValue(NoU, NoV, aParam);
678         }
679       }
680     }
681
682     // Fill boundary with RealLast square distance.
683     for (NoV = 0; NoV <= myvsample; NoV++) {
684       myFacePntParams->ChangeValue(0, NoV).SetSqrDistance(RealLast());
685       myFacePntParams->ChangeValue(myusample, NoV).SetSqrDistance(RealLast());
686     }
687
688     for (NoU = 1; NoU < myusample; NoU++) {
689       myFacePntParams->ChangeValue(NoU, 0).SetSqrDistance(RealLast());
690       myFacePntParams->ChangeValue(NoU, myvsample).SetSqrDistance(RealLast());
691     }
692   }
693 }
694
695 /*
696 a- Constitution of the table of distances (TbDist(0,myusample+1,0,myvsample+1)):
697    ---------------------------------------------------------------
698 */
699
700 // Parameterisation of the sample
701
702 void Extrema_GenExtPS::BuildTree()
703 {
704   // if tree already exists, assume it is already correctly filled
705   if ( ! mySphereUBTree.IsNull() )
706     return;
707
708   Standard_Real PasU = myusup - myumin;
709   Standard_Real PasV = myvsup - myvmin;
710   Standard_Real U0 = PasU / myusample / 100.;
711   Standard_Real V0 = PasV / myvsample / 100.;
712   gp_Pnt P1;
713   PasU = (PasU - U0) / (myusample - 1);
714   PasV = (PasV - V0) / (myvsample - 1);
715   U0 = U0/2. + myumin;
716   V0 = V0/2. + myvmin;
717
718   //build grid of parametric points 
719   myUParams = new TColStd_HArray1OfReal(1,myusample );
720   myVParams = new TColStd_HArray1OfReal(1,myvsample );
721   Standard_Integer NoU, NoV;
722   Standard_Real U = U0, V = V0;
723   for ( NoU = 1 ; NoU <= myusample; NoU++, U += PasU) 
724     myUParams->SetValue(NoU, U);
725   for ( NoV = 1, V = V0; NoV <= myvsample; NoV++, V += PasV)
726     myVParams->SetValue(NoV, V);
727
728   // Calculation of distances
729   mySphereUBTree = new Extrema_UBTreeOfSphere;
730   Extrema_UBTreeFillerOfSphere aFiller(*mySphereUBTree);
731   Standard_Integer i = 0;
732   
733   mySphereArray = new Bnd_HArray1OfSphere(0, myusample * myvsample);
734  
735   for ( NoU = 1; NoU <= myusample; NoU++ ) {
736     for ( NoV = 1; NoV <= myvsample; NoV++) {
737       P1 = myS->Value(myUParams->Value(NoU), myVParams->Value(NoV));
738       Bnd_Sphere aSph(P1.XYZ(), 0/*mytolu < mytolv ? mytolu : mytolv*/, NoU, NoV);
739       aFiller.Add(i, aSph);
740       mySphereArray->SetValue( i, aSph );
741       i++;
742     }
743   }
744   aFiller.Fill();
745 }
746
747 void Extrema_GenExtPS::FindSolution(const gp_Pnt& /*P*/, 
748                                     const Extrema_POnSurfParams &theParams)
749 {
750   math_Vector Tol(1,2);
751   Tol(1) = mytolu;
752   Tol(2) = mytolv;
753
754   math_Vector UV(1, 2);
755   theParams.Parameter(UV(1), UV(2));
756
757   math_Vector UVinf(1,2), UVsup(1,2);
758   UVinf(1) = myumin;
759   UVinf(2) = myvmin;
760   UVsup(1) = myusup;
761   UVsup(2) = myvsup;
762
763   const Standard_Integer aNbMaxIter = 100;
764   math_FunctionSetRoot S (myF, UV, Tol, UVinf, UVsup, aNbMaxIter);
765
766   myDone = Standard_True;
767 }
768
769 void Extrema_GenExtPS::SetFlag(const Extrema_ExtFlag F)
770 {
771   myFlag = F;
772 }
773
774 void Extrema_GenExtPS::SetAlgo(const Extrema_ExtAlgo A)
775 {
776   if(myAlgo != A)
777      myInit = Standard_False;
778   myAlgo = A;
779  
780 }
781
782 void Extrema_GenExtPS::Perform(const gp_Pnt& P) 
783 {  
784   myDone = Standard_False;
785   myF.SetPoint(P);
786   
787   if(myAlgo == Extrema_ExtAlgo_Grad)
788   {
789     BuildGrid(P);
790     Standard_Integer NoU,NoV;
791
792     if(myFlag == Extrema_ExtFlag_MIN || myFlag == Extrema_ExtFlag_MINMAX) 
793     {
794       Extrema_ElementType anElemType;
795       Standard_Integer iU;
796       Standard_Integer iV;
797       Standard_Integer iU2;
798       Standard_Integer iV2;
799       Standard_Boolean isMin;
800       Standard_Integer i;
801
802       for (NoU = 1; NoU < myusample; NoU++) {
803         for (NoV = 1; NoV < myvsample; NoV++) {
804           const Extrema_POnSurfParams &aParam =
805             myFacePntParams->Value(NoU, NoV);
806
807           isMin = Standard_False;
808           anElemType = aParam.GetElementType();
809
810           if (anElemType == Extrema_Face) {
811             isMin = Standard_True;
812           } else {
813             // Check if it is a boundary edge or corner vertex.
814             aParam.GetIndices(iU, iV);
815
816             if (anElemType == Extrema_UIsoEdge) {
817               isMin = (iV == 1 || iV == myvsample);
818             } else if (anElemType == Extrema_VIsoEdge) {
819               isMin = (iU == 1 || iU == myusample);
820             } else if (anElemType == Extrema_Node) {
821               isMin = (iU == 1 || iU == myusample) &&
822                       (iV == 1 || iV == myvsample);
823             }
824
825             if (!isMin) {
826               // This is a middle element.
827               if (anElemType == Extrema_UIsoEdge ||
828                 (anElemType == Extrema_Node && (iU == 1 || iU == myusample))) {
829                 // Check the down face.
830                 const Extrema_POnSurfParams &aDownParam =
831                     myFacePntParams->Value(NoU, NoV - 1);
832
833                 if (aDownParam.GetElementType() == anElemType) {
834                   aDownParam.GetIndices(iU2, iV2);
835                   isMin = (iU == iU2 && iV == iV2);
836                 }
837               } else if (anElemType == Extrema_VIsoEdge ||
838                 (anElemType == Extrema_Node && (iV == 1 || iV == myvsample))) {
839                 // Check the right face.
840                 const Extrema_POnSurfParams &aRightParam =
841                     myFacePntParams->Value(NoU - 1, NoV);
842
843                 if (aRightParam.GetElementType() == anElemType) {
844                   aRightParam.GetIndices(iU2, iV2);
845                   isMin = (iU == iU2 && iV == iV2);
846                 }
847               } else if (iU == NoU && iV == NoV) {
848                 // Check the lower-left node. For this purpose it is necessary
849                 // to check lower-left, lower and left faces.
850                 isMin = Standard_True;
851
852                 const Extrema_POnSurfParams *anOtherParam[3] =
853                 { &myFacePntParams->Value(NoU, NoV - 1),     // Down
854                   &myFacePntParams->Value(NoU - 1, NoV - 1), // Lower-left
855                   &myFacePntParams->Value(NoU - 1, NoV) };   // Left
856
857                 for (i = 0; i < 3 && isMin; i++) {
858                   if (anOtherParam[i]->GetElementType() == Extrema_Node) {
859                     anOtherParam[i]->GetIndices(iU2, iV2);
860                     isMin = (iU == iU2 && iV == iV2);
861                   } else {
862                     isMin = Standard_False;
863                   }
864                 }
865               }
866             }
867           }
868
869           if (isMin) {
870             FindSolution(P, aParam);
871           }
872         }
873       }
874     }
875     
876     if(myFlag == Extrema_ExtFlag_MAX || myFlag == Extrema_ExtFlag_MINMAX)
877     {
878       Standard_Real Dist;
879
880       for (NoU = 1; NoU <= myusample; NoU++) {
881         for (NoV = 1; NoV <= myvsample; NoV++) {
882           Dist = myPoints->Value(NoU, NoV).GetSqrDistance();
883           if ((myPoints->Value(NoU-1,NoV-1).GetSqrDistance() <= Dist) &&
884               (myPoints->Value(NoU-1,NoV  ).GetSqrDistance() <= Dist) &&
885               (myPoints->Value(NoU-1,NoV+1).GetSqrDistance() <= Dist) &&
886               (myPoints->Value(NoU  ,NoV-1).GetSqrDistance() <= Dist) &&
887               (myPoints->Value(NoU  ,NoV+1).GetSqrDistance() <= Dist) &&
888               (myPoints->Value(NoU+1,NoV-1).GetSqrDistance() <= Dist) &&
889               (myPoints->Value(NoU+1,NoV  ).GetSqrDistance() <= Dist) &&
890               (myPoints->Value(NoU+1,NoV+1).GetSqrDistance() <= Dist)) {
891             // Find maximum.
892             FindSolution(P, myPoints->Value(NoU, NoV));
893           }
894         }
895       }
896     }
897   }
898   else
899   {
900     BuildTree();
901     if(myFlag == Extrema_ExtFlag_MIN || myFlag == Extrema_ExtFlag_MINMAX)
902     {
903       Bnd_Sphere aSol = mySphereArray->Value(0);
904       Bnd_SphereUBTreeSelectorMin aSelector(mySphereArray, aSol);
905       //aSelector.SetMaxDist( RealLast() );
906       aSelector.DefineCheckPoint( P );
907       mySphereUBTree->Select( aSelector );
908       //TODO: check if no solution in binary tree
909       Bnd_Sphere& aSph = aSelector.Sphere();
910       Standard_Real aU = myUParams->Value(aSph.U());
911       Standard_Real aV = myVParams->Value(aSph.V());
912       Extrema_POnSurfParams aParams(aU, aV, myS->Value(aU, aV));
913
914       aParams.SetSqrDistance(P.SquareDistance(aParams.Value()));
915       aParams.SetIndices(aSph.U(), aSph.V());
916       FindSolution(P, aParams);
917     }
918     if(myFlag == Extrema_ExtFlag_MAX || myFlag == Extrema_ExtFlag_MINMAX)
919     {
920       Bnd_Sphere aSol = mySphereArray->Value(0);
921       Bnd_SphereUBTreeSelectorMax aSelector(mySphereArray, aSol);
922       //aSelector.SetMaxDist( RealLast() );
923       aSelector.DefineCheckPoint( P );
924       mySphereUBTree->Select( aSelector );
925       //TODO: check if no solution in binary tree
926       Bnd_Sphere& aSph = aSelector.Sphere();
927       Standard_Real aU = myUParams->Value(aSph.U());
928       Standard_Real aV = myVParams->Value(aSph.V());
929       Extrema_POnSurfParams aParams(aU, aV, myS->Value(aU, aV));
930       aParams.SetSqrDistance(P.SquareDistance(aParams.Value()));
931       aParams.SetIndices(aSph.U(), aSph.V());
932
933       FindSolution(P, aParams);
934     }
935   }
936 }
937 //=============================================================================
938
939 Standard_Boolean Extrema_GenExtPS::IsDone () const { return myDone; }
940 //=============================================================================
941
942 Standard_Integer Extrema_GenExtPS::NbExt () const
943 {
944   if (!IsDone()) { StdFail_NotDone::Raise(); }
945   return myF.NbExt();
946 }
947 //=============================================================================
948
949 Standard_Real Extrema_GenExtPS::SquareDistance (const Standard_Integer N) const
950 {
951   if (!IsDone()) { StdFail_NotDone::Raise(); }
952   return myF.SquareDistance(N);
953 }
954 //=============================================================================
955
956 const Extrema_POnSurf& Extrema_GenExtPS::Point (const Standard_Integer N) const
957 {
958   if (!IsDone()) { StdFail_NotDone::Raise(); }
959   return myF.Point(N);
960 }
961 //=============================================================================