0026937: Eliminate NO_CXX_EXCEPTION macro support
[occt.git] / src / GeomFill / GeomFill_SweepSectionGenerator.cxx
1 // Created on: 1994-02-28
2 // Created by: Bruno DUMORTIER
3 // Copyright (c) 1994-1999 Matra Datavision
4 // Copyright (c) 1999-2014 OPEN CASCADE SAS
5 //
6 // This file is part of Open CASCADE Technology software library.
7 //
8 // This library is free software; you can redistribute it and/or modify it under
9 // the terms of the GNU Lesser General Public License version 2.1 as published
10 // by the Free Software Foundation, with special exception defined in the file
11 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
12 // distribution for complete text of the license and disclaimer of any warranty.
13 //
14 // Alternatively, this file may be used under the terms of Open CASCADE
15 // commercial license or contractual agreement.
16
17
18 #include <Adaptor3d_HCurve.hxx>
19 #include <ElCLib.hxx>
20 #include <GCPnts_QuasiUniformDeflection.hxx>
21 #include <Geom_BSplineCurve.hxx>
22 #include <Geom_Circle.hxx>
23 #include <Geom_Curve.hxx>
24 #include <Geom_TrimmedCurve.hxx>
25 #include <GeomAdaptor.hxx>
26 #include <GeomAdaptor_Curve.hxx>
27 #include <GeomConvert.hxx>
28 #include <GeomFill_Profiler.hxx>
29 #include <GeomFill_SweepSectionGenerator.hxx>
30 #include <gp_Ax2.hxx>
31 #include <gp_Ax3.hxx>
32 #include <gp_Dir.hxx>
33 #include <gp_Pnt.hxx>
34 #include <gp_Trsf.hxx>
35 #include <gp_Vec.hxx>
36 #include <Precision.hxx>
37 #include <Standard_RangeError.hxx>
38 #include <TColStd_Array1OfReal.hxx>
39
40 #include <stdio.h>
41 #ifdef DRAW
42 #include <DrawTrSurf.hxx>
43 #include <Geom_BSplineCurve.hxx>
44 static Standard_Boolean Affich     = Standard_False;
45 static Standard_Integer NbSECTIONS = 0;
46 #endif
47
48 //=======================================================================
49 //function : GeomFill_SweepSectionGenerator
50 //purpose  : 
51 //=======================================================================
52
53 GeomFill_SweepSectionGenerator::GeomFill_SweepSectionGenerator()
54 {
55   myIsDone = Standard_False;
56 }
57
58
59 //=======================================================================
60 //function : GeomFill_SweepSectionGenerator
61 //purpose  : 
62 //=======================================================================
63
64 GeomFill_SweepSectionGenerator::GeomFill_SweepSectionGenerator
65   (const Handle(Geom_Curve)& Path,
66    const Standard_Real       Radius)
67 {
68   Init(Path,Radius);
69 }
70
71
72 //=======================================================================
73 //function : GeomFill_SweepSectionGenerator
74 //purpose  : 
75 //=======================================================================
76
77 GeomFill_SweepSectionGenerator::GeomFill_SweepSectionGenerator
78   (const Handle(Geom_Curve)& Path,
79    const Handle(Geom_Curve)& FirstSect)
80 {
81   Init(Path,FirstSect);
82 }
83
84
85 //=======================================================================
86 //function : GeomFill_SweepSectionGenerator
87 //purpose  : 
88 //=======================================================================
89
90 GeomFill_SweepSectionGenerator::GeomFill_SweepSectionGenerator
91   (const Handle(Geom_Curve)& Path,
92    const Handle(Geom_Curve)& FirstSect,
93    const Handle(Geom_Curve)& LastSect  )
94 {
95   Init(Path,FirstSect,LastSect);
96 }
97
98
99 //=======================================================================
100 //function : GeomFill_SweepSectionGenerator
101 //purpose  : 
102 //=======================================================================
103
104 GeomFill_SweepSectionGenerator::GeomFill_SweepSectionGenerator
105   (const Handle(Adaptor3d_HCurve)& Path,
106    const Handle(Adaptor3d_HCurve)& Curve1,
107    const Handle(Adaptor3d_HCurve)& Curve2,
108    const Standard_Real       Radius)
109 {
110   Init(Path,Curve1,Curve2,Radius);
111 }
112
113
114 //=======================================================================
115 //function : Init
116 //purpose  : 
117 //=======================================================================
118
119 void GeomFill_SweepSectionGenerator::Init(const Handle(Geom_Curve)& Path,
120                                           const Standard_Real       Radius)
121 {
122   myIsDone = Standard_False;
123   myRadius = Radius;
124   GeomAdaptor_Curve ThePath(Path);
125
126   if (ThePath.GetType() == GeomAbs_Circle) {
127     
128     myCircPathAxis = ThePath.Circle().Axis();
129     myType = 4;
130   }
131   else myType = 1;
132   if ( Path->IsKind(STANDARD_TYPE(Geom_BSplineCurve))) {
133     myPath = Handle(Geom_BSplineCurve)::DownCast(Path->Copy());
134   }
135   else {
136     myPath = GeomConvert::CurveToBSplineCurve(Path);
137   }
138 }
139
140
141 //=======================================================================
142 //function : Init
143 //purpose  : 
144 //=======================================================================
145 void GeomFill_SweepSectionGenerator::Init
146   (const Handle(Geom_Curve)& Path,
147    const Handle(Geom_Curve)& FirstSect)
148 {
149   myIsDone = Standard_False;
150   myRadius = 0; 
151   GeomAdaptor_Curve ThePath(Path);
152
153   if (ThePath.GetType() == GeomAbs_Circle) {    
154     myCircPathAxis = ThePath.Circle().Axis();
155     myType = 5;
156   }
157
158   else  myType   = 2;
159   
160   if ( Path->IsKind(STANDARD_TYPE(Geom_BSplineCurve))) {
161     myPath = Handle(Geom_BSplineCurve)::DownCast(Path->Copy());
162   }
163   else {
164     myPath = GeomConvert::CurveToBSplineCurve(Path);
165   }
166   if ( FirstSect->IsKind(STANDARD_TYPE(Geom_BSplineCurve))) {
167       myFirstSect = Handle(Geom_BSplineCurve)::DownCast(FirstSect->Copy());
168     }
169   else {
170     // JAG
171     myFirstSect = GeomConvert::CurveToBSplineCurve(FirstSect,
172                                                    Convert_QuasiAngular);
173   }
174   if ( myFirstSect->IsPeriodic()) myFirstSect->SetNotPeriodic();
175 }
176
177
178 //=======================================================================
179 //function : Init
180 //purpose  : 
181 //=======================================================================
182
183 void GeomFill_SweepSectionGenerator::Init
184   (const Handle(Geom_Curve)& Path,
185    const Handle(Geom_Curve)& FirstSect,
186    const Handle(Geom_Curve)& LastSect  )
187 {
188   myIsDone = Standard_False;
189   myRadius = 0;
190   GeomAdaptor_Curve ThePath(Path);
191
192   if (ThePath.GetType() == GeomAbs_Circle) {
193     
194     myCircPathAxis = ThePath.Circle().Axis();
195     myType = 6;
196   }
197   else myType   = 3;
198   
199   if ( Path->IsKind(STANDARD_TYPE(Geom_BSplineCurve))) {
200     myPath = Handle(Geom_BSplineCurve)::DownCast(Path->Copy());
201   }
202   else {
203     myPath = GeomConvert::CurveToBSplineCurve(Path);
204   }
205   
206   // JAG
207   if ( FirstSect->IsKind(STANDARD_TYPE(Geom_BSplineCurve))) {
208       myFirstSect = Handle(Geom_BSplineCurve)::DownCast(FirstSect->Copy());
209     }
210   else {
211     myFirstSect = GeomConvert::CurveToBSplineCurve(FirstSect,
212                                                    Convert_QuasiAngular);
213   }
214   if ( LastSect->IsKind(STANDARD_TYPE(Geom_BSplineCurve))) {
215       myLastSect = Handle(Geom_BSplineCurve)::DownCast(LastSect->Copy());
216     }
217   else {
218     myLastSect = GeomConvert::CurveToBSplineCurve(LastSect,
219                                                    Convert_QuasiAngular);
220   }
221
222   if ( myFirstSect->IsPeriodic()) myFirstSect->SetNotPeriodic();
223   if ( myLastSect->IsPeriodic()) myLastSect->SetNotPeriodic();
224
225
226   // JAG
227
228   GeomFill_Profiler Profil;
229   Profil.AddCurve(myFirstSect);
230   Profil.AddCurve(myLastSect);
231   Profil.Perform(Precision::Confusion());
232   
233   myFirstSect = Handle(Geom_BSplineCurve)::DownCast(Profil.Curve(1));
234   myLastSect  = Handle(Geom_BSplineCurve)::DownCast(Profil.Curve(2));
235 }
236
237
238 //=======================================================================
239 //function : Init
240 //purpose  : 
241 //=======================================================================
242
243 void GeomFill_SweepSectionGenerator::Init
244   (const Handle(Adaptor3d_HCurve)& Path,
245    const Handle(Adaptor3d_HCurve)& Curve1,
246    const Handle(Adaptor3d_HCurve)& Curve2,
247    const Standard_Real       Radius)
248 {
249   myIsDone = Standard_False;
250   myRadius = Radius;
251   myType   = 0;
252
253   Handle(Geom_Curve) CC = GeomAdaptor::MakeCurve(Path->Curve());
254   myPath         = GeomConvert::CurveToBSplineCurve(CC);
255   myAdpPath      = Path;
256   myAdpFirstSect = Curve1;
257   myAdpLastSect  = Curve2;
258 }
259
260
261 //=======================================================================
262 //function : Perform
263 //purpose  : 
264 //=======================================================================
265
266 void GeomFill_SweepSectionGenerator::Perform(const Standard_Boolean Polynomial)
267 {
268   myPolynomial = Polynomial;
269
270   // eval myNbSections.
271   Standard_Integer NSpans = myPath->NbKnots()-1;
272
273   myNbSections = 21 * NSpans;
274
275   Standard_Real U;
276
277   Standard_Real U1 = myPath->FirstParameter();
278   Standard_Real U2 = myPath->LastParameter();
279   
280   GCPnts_QuasiUniformDeflection Samp;
281   // Calcul de la longueur approximative de la courbe
282   GeomAdaptor_Curve AdpPath(myPath);
283   gp_Pnt P1 = AdpPath.Value(U1);
284   gp_Pnt P2 = AdpPath.Value((U1+U2)/2.);
285   gp_Pnt P3 = AdpPath.Value(U2);
286   Standard_Real Length = 
287     P1.Distance(P2) + P2.Distance(P3);
288   Standard_Real Fleche = 1.e-5 * Length;
289   Samp.Initialize(AdpPath,Fleche);
290
291   if ( Samp.IsDone() && (Samp.NbPoints() > myNbSections) ) {
292     myNbSections = Samp.NbPoints();
293   }
294   // the transformations are calculate on differents points of <myPath>
295   // corresponding to the path parameter uniformly reparted.
296   Standard_Real DeltaU = (U2-U1)/(Standard_Real)(myNbSections-1);
297   TColStd_Array1OfReal Parameters(1,myNbSections);
298 //  Parameters(1) = U1;
299 //  for (Standard_Integer i = 2; i < myNbSections; i++) {
300 //    Parameters(i) = U1 + (i-1) * DeltaU;
301 //  }
302 //  Parameters(myNbSections) = U2;
303   
304   Parameters(1) = 0.;
305   for (Standard_Integer i = 2; i < myNbSections; i++) {
306     Parameters(i) = (i-1) * DeltaU;
307   }
308   Parameters(myNbSections) = U2 - U1;
309
310   gp_Vec D1Ref, D1;
311   gp_Pnt PRef , P; 
312   gp_Trsf TR, cumulTR, Trans;
313
314   myPath->D1( U1, PRef, D1Ref); 
315
316   if ( ( myType == 1) || (myType == 4) ) {
317     // We create a circle with radius <myRadius>. This axis is create with
318     // main direction <DRef> (first derivate vector of <myPath> on the first
319     // point <PRef> ). This circle is, after transform to BSpline curve, 
320     // put in <myFirstSect>.
321                               
322     gp_Ax2 CircleAxis (PRef,D1Ref);
323 /*
324     Handle(Geom_Circle) Circ = new Geom_Circle( CircleAxis, myRadius); 
325     
326     myFirstSect = GeomConvert::CurveToBSplineCurve(Circ);
327     // le cercle est segmente car AppBlend_AppSurf ne gere
328     // pas les courbes periodiques.
329     myFirstSect->Segment(0., 2.*M_PI);
330 */
331     Handle(Geom_TrimmedCurve) Circ = 
332       new Geom_TrimmedCurve(new Geom_Circle( CircleAxis, myRadius), 
333                             0., 2.*M_PI); 
334     
335     myFirstSect = GeomConvert::CurveToBSplineCurve(Circ,Convert_QuasiAngular);
336   }
337   
338   if (myType <= 3 && myType >=1 ) {
339     
340     for (Standard_Integer i = 2; i <= myNbSections; i++) {
341
342       U = Parameters(i) + U1;
343       if (i == myNbSections) U = U2;
344
345       myPath->D1( U, P, D1);
346     
347       // Eval the translation between the (i-1) section and the i-th.
348       Trans.SetTranslation(PRef, P);
349
350       gp_Trsf Rot;
351       if (! D1Ref.IsParallel(D1, Precision::Angular())) {
352         // Eval the Rotation between (i-1) section and the i-th.
353         Rot.SetRotation(gp_Ax1(P, gp_Dir(D1Ref^D1)), 
354                         D1Ref.AngleWithRef(D1, D1Ref^D1));
355       }
356       else
357         if (D1Ref.IsOpposite(D1, Precision::Angular()))
358 #ifdef OCCT_DEBUG
359           cout <<"Que fais-je ???? " << endl;
360 #endif
361
362       // TR is the transformation between (i-1) section and the i-th.
363       TR = Rot * Trans;
364       // cumulTR is the transformation between <myFirstSec> and 
365       // the i-th section.
366       cumulTR = TR * cumulTR;
367
368       myTrsfs.Append(cumulTR);
369       
370       PRef = P;
371       D1Ref = D1;
372     }
373   }
374   else if ( myType != 0) { 
375     for (Standard_Integer i = 2; i<= myNbSections; i++) {
376       cumulTR.SetRotation(myCircPathAxis, Parameters(i));
377       myTrsfs.Append(cumulTR);      
378     }
379   }
380     
381   myIsDone = Standard_True;
382 }
383
384 //=======================================================================
385 //function : GetShape
386 //purpose  : 
387 //=======================================================================
388
389 void GeomFill_SweepSectionGenerator::GetShape
390   (Standard_Integer& NbPoles,
391    Standard_Integer& NbKnots,
392    Standard_Integer& Degree,
393    Standard_Integer& NbPoles2d) const 
394 {
395 /* 
396  if ( myType == 1) {
397     NbPoles   = 7;
398     NbKnots   = 4;
399     Degree    = 2;
400   }
401   else {
402 */
403   if ( myType != 0) {
404     NbPoles = myFirstSect->NbPoles();
405     NbKnots = myFirstSect->NbKnots();
406     Degree  = myFirstSect->Degree();
407   }
408   else { // myType == 0
409     NbPoles   = 7;
410     NbKnots   = 2;
411     Degree    = 6;
412   }
413   NbPoles2d = 0;
414 }
415
416
417 //=======================================================================
418 //function : Knots
419 //purpose  : 
420 //=======================================================================
421
422 void GeomFill_SweepSectionGenerator::Knots(TColStd_Array1OfReal& TKnots) const 
423 {
424 /*
425   if (myType == 1) {
426     Standard_Real U = 2.*M_PI/3.;
427     for ( Standard_Integer i = 1; i <= 4; i++) 
428       TKnots(i) = ( i-1) * U;
429   }
430   else {
431 */
432   if (myType !=0) {
433     myFirstSect->Knots(TKnots);
434   }
435   else {
436     TKnots(1) = 0.;
437     TKnots(2) = 1.;
438   }
439 //  }
440 }
441
442
443 //=======================================================================
444 //function : Mults
445 //purpose  : 
446 //=======================================================================
447
448 void GeomFill_SweepSectionGenerator::Mults(TColStd_Array1OfInteger& TMults)
449   const 
450 {
451 /*
452   if ( myType == 1) {
453     TMults( 1) = TMults( 4) = 3;
454     TMults( 2) = TMults( 3) = 2;
455   }
456   else {
457 */
458   if ( myType != 0) {
459     myFirstSect->Multiplicities(TMults);
460   }
461   else {
462     TMults( 1) = TMults( 2) = 7;
463   }
464 //  }
465 }
466
467
468 //=======================================================================
469 //function : Section
470 //purpose  : 
471 //=======================================================================
472
473 Standard_Boolean GeomFill_SweepSectionGenerator::Section
474   (const Standard_Integer      P,
475          TColgp_Array1OfPnt&   Poles, 
476          TColgp_Array1OfVec&   DPoles,
477          TColgp_Array1OfPnt2d& Poles2d,
478          TColgp_Array1OfVec2d& , //DPoles2d,
479          TColStd_Array1OfReal& Weigths,
480          TColStd_Array1OfReal& DWeigths
481    ) const 
482 {
483   Section( P, Poles, Poles2d, Weigths);
484
485   // pour les tuyaux sur aretes pour l'instant on ne calcule pas les derivees
486   if ( myType == 0 ) return Standard_False; // a voir pour mieux.
487
488   // calcul des derivees sur la surface
489   // on calcule les derivees en approximant le path au voisinage du point
490   // P(u) par le cercle osculateur au path .
491
492   // calcul du cercle osculateur.
493
494   Standard_Real U;
495   if ( P == 1) {
496     U = myPath->FirstParameter();
497   }
498   else if ( P == myNbSections ) {
499     U = myPath->LastParameter();
500   }
501   else
502     return Standard_False;
503     
504   gp_Vec D1, D2;
505   gp_Pnt Pt;
506
507   myPath->D2(U,Pt,D1,D2);
508   Standard_Real l = D1.Magnitude();
509   
510   if ( l < Epsilon(1.))
511     return Standard_False;
512
513   gp_Dir T = D1;
514   Standard_Real m = D2.Dot(T);
515   gp_Vec D = D2 - m * T;
516   Standard_Real c = D.Magnitude() / (l*l);
517   
518   if ( c < Epsilon(1.)) { 
519     // null curvature : equivalent to a translation of the section 
520     for (Standard_Integer i = 1; i <= myFirstSect->NbPoles(); i++) {
521       DPoles(i) = D1;
522     }    
523   }
524   else {
525     gp_Dir N = D;
526     gp_Pnt Q = Pt.Translated( (1./c) * gp_Vec(N));
527     Standard_Real x, y;
528     gp_Vec V;
529     for ( Standard_Integer i = 1; i <= myFirstSect->NbPoles(); i++) {
530       V = gp_Vec(Q, Poles(i));
531       x = V * gp_Vec(T);
532       y = V * gp_Vec(N);
533       DPoles(i) = x * gp_Vec(N) - y * gp_Vec(T);
534       if ( DPoles(i).Magnitude() > Epsilon(1.)) {
535         DPoles(i).Normalize();
536         DPoles(i) *= Sqrt( x*x + y*y);
537       }
538     }
539   }
540   
541   for ( Standard_Integer i = 1; i <= myFirstSect->NbPoles(); i++) {
542     DWeigths(i) = 0.;
543   }
544   
545   return Standard_True;
546 }
547
548
549 //=======================================================================
550 //function : Section
551 //purpose  : 
552 //=======================================================================
553
554 void GeomFill_SweepSectionGenerator::Section
555   (const Standard_Integer      P,
556          TColgp_Array1OfPnt&   Poles,
557          TColgp_Array1OfPnt2d& , //Poles2d, 
558          TColStd_Array1OfReal& Weigths) const 
559 {
560   if (myType != 0) {
561     myFirstSect->Poles(Poles);
562     myFirstSect->Weights(Weigths);
563     gp_Trsf cumulTR;
564     if (P > 1) {
565       cumulTR = myTrsfs(P - 1); 
566       // <cumulTR> transform <myFirstSect> to the P ieme Section. In fact
567       // each points of the array <poles> will be transformed.
568       
569       if ( (myType == 3 ) || (myType == 6) ){
570         for (Standard_Integer i = 1; i <= myFirstSect->NbPoles(); i++) {
571           Poles(i).SetXYZ( (myNbSections - P) * myFirstSect->Pole(i).XYZ() +
572                           (P - 1) * myLastSect->Pole(i).XYZ() );
573           Poles(i).SetXYZ( Poles(i).XYZ() / (myNbSections - 1));
574           
575           Weigths(i) = (myNbSections - P) * myFirstSect->Weight(i) +
576             (P - 1) * myLastSect->Weight(i);
577           Weigths(i) /= myNbSections - 1;
578         }
579       }
580       
581       for (Standard_Integer i = 1; i<=Poles.Length(); i++)
582         Poles(i).Transform(cumulTR);
583     }
584 #ifdef DRAW
585     if ( Affich) {
586       char name[256];
587       sprintf(name,"SECTION_%d",++NbSECTIONS);
588       DrawTrSurf::Set(name,myFirstSect->Transformed(cumulTR));
589     }
590 #endif
591   }
592   else {
593
594     Standard_Real Coef = (P -1. ) / ( myNbSections - 1.);
595     Standard_Real U = 
596       ( 1- Coef) * myAdpPath->FirstParameter() +
597         Coef     * myAdpPath->LastParameter();
598
599     gp_Pnt PPath = myAdpPath->Value(U);
600     
601     Standard_Real Alpha = U - myAdpPath->FirstParameter();
602     Alpha /= myAdpPath->LastParameter() - myAdpPath->FirstParameter();
603     
604     Standard_Real U1 = 
605       ( 1- Alpha) * myAdpFirstSect->FirstParameter() +
606         Alpha     * myAdpFirstSect->LastParameter();
607     
608     gp_Pnt P1 = myAdpFirstSect->Value(U1);
609     
610     Standard_Real U2 = 
611       ( 1- Alpha) * myAdpLastSect->FirstParameter() +
612         Alpha     * myAdpLastSect->LastParameter();
613     
614     gp_Pnt P2 = myAdpLastSect->Value(U2);
615     
616     gp_Ax2 Axis;
617     Standard_Real Angle;
618     if ( P1.Distance(P2) < Precision::Confusion()) {
619       Angle = 0.;
620     }
621     else {
622       Axis = gp_Ax2(PPath, 
623                     gp_Vec(PPath,P1) ^ gp_Vec(PPath,P2),
624                     gp_Vec(PPath,P1));
625       Angle = ElCLib::CircleParameter(Axis,P2);
626     }
627 #ifdef OCCT_DEBUG
628 /*
629     if (Standard_False) {
630       gp_Vec dummyD1 = myAdpPath->DN(U,1);
631       gp_Vec dummyTg = Axis.Direction();
632       Standard_Real Cos = dummyD1.Dot(dummyTg);
633       if ( Cos > 0.) cout << "+" ;
634       else           cout << "-" ;
635     }
636 */
637 #endif
638     if ( Angle < Precision::Angular()) {
639       for ( Standard_Integer i = 1; i <= Poles.Upper(); i++) {
640         Poles(i) = P1;
641         Weigths(i) = 1;
642       }
643     }
644     else {
645       Handle(Geom_Circle) Circ =
646         new Geom_Circle( Axis, myRadius);
647       Handle(Geom_TrimmedCurve) CT = 
648         new Geom_TrimmedCurve(Circ, 0., Angle);
649       Handle(Geom_BSplineCurve) BS;
650       if ( myPolynomial) 
651         BS = GeomConvert::CurveToBSplineCurve( CT, Convert_Polynomial);
652       else 
653         BS = GeomConvert::CurveToBSplineCurve( CT, Convert_QuasiAngular);
654
655 #ifdef DRAW
656       if ( Affich) {
657         char name[256];
658         sprintf(name,"SECTION_%d",++NbSECTIONS);
659         DrawTrSurf::Set(name,BS);
660       }
661 #endif
662       
663       BS->Poles(Poles);
664       BS->Weights(Weigths);
665     }
666   }
667 }
668
669 //=======================================================================
670 //function : Transformation
671 //purpose  : 
672 //=======================================================================
673 const gp_Trsf& GeomFill_SweepSectionGenerator::Transformation
674   (const Standard_Integer Index) const 
675 {
676   if (Index > myTrsfs.Length())
677     throw Standard_RangeError("GeomFill_SweepSectionGenerator::Transformation");
678   
679   return myTrsfs(Index);
680 }
681
682
683 //=======================================================================
684 //function : Parameter
685 //purpose  : 
686 //=======================================================================
687
688 Standard_Real GeomFill_SweepSectionGenerator::Parameter
689   (const Standard_Integer P) const
690 {
691   if (P == 1) {
692     return myPath->FirstParameter();
693   }
694   else if (P == myNbSections) {
695     return myPath->LastParameter();
696   }
697   else {
698     Standard_Real U1 = myPath->FirstParameter();
699     Standard_Real U2 = myPath->LastParameter();
700     Standard_Real prm = ((myNbSections-P)*U1 + (P-1)*U2)/
701       (Standard_Real)(myNbSections-1);
702     return prm;
703   }
704 }