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