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