1 // Created on: 2016-04-19
2 // Copyright (c) 2016 OPEN CASCADE SAS
3 // Created by: Oleg AGASHIN
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15 
16 #include <BRepMesh_NURBSRangeSplitter.hxx>
17 
18 #include <algorithm>
19 #include <BRepMesh_GeomTool.hxx>
20 #include <GeomAdaptor_Curve.hxx>
21 #include <GeomLib.hxx>
22 #include <IMeshData_Edge.hxx>
23 #include <IMeshData_Wire.hxx>
24 #include <NCollection_Handle.hxx>
25 
26 namespace
27 {
28   class AnalyticalFilter
29   {
30   public:
31     //! Constructor.
AnalyticalFilter(const IMeshData::IFaceHandle & theDFace,const GeomAbs_IsoType theIsoType,const Handle (IMeshData::SequenceOfReal)& theParams,const Handle (IMeshData::SequenceOfReal)& theControlParams,const Handle (IMeshData::MapOfReal)& theParamsForbiddenToRemove,const Handle (IMeshData::MapOfReal)& theControlParamsForbiddenToRemove)32     AnalyticalFilter(
33       const IMeshData::IFaceHandle&             theDFace,
34       const GeomAbs_IsoType                     theIsoType,
35       const Handle(IMeshData::SequenceOfReal)&  theParams,
36       const Handle(IMeshData::SequenceOfReal)&  theControlParams,
37       const Handle(IMeshData::MapOfReal)&       theParamsForbiddenToRemove,
38       const Handle(IMeshData::MapOfReal)&       theControlParamsForbiddenToRemove)
39       : myDFace(theDFace),
40         mySurface(myDFace->GetSurface()->Surface().Surface()),
41         myIsoU(theIsoType == GeomAbs_IsoU),
42         myParams(theParams),
43         myControlParams(theControlParams),
44         myParamsForbiddenToRemove(theParamsForbiddenToRemove),
45         myControlParamsForbiddenToRemove(theControlParamsForbiddenToRemove),
46         myAllocator(new NCollection_IncAllocator(IMeshData::MEMORY_BLOCK_SIZE_HUGE)),
47         myControlParamsToRemove(new IMeshData::MapOfReal(1, myAllocator)),
48         myCurrParam(0.0),
49         myCurrControlParam(0.0),
50         myPrevControlParam(0.0)
51     {
52     }
53 
54     //! Returns map of parameters supposed to be removed.
Handle(IMeshData::MapOfReal)55     const Handle(IMeshData::MapOfReal)& GetControlParametersToRemove(
56       const IMeshTools_Parameters& theParameters)
57     {
58       myParameters = theParameters;
59 
60       Standard_Integer aStartIndex, aEndIndex;
61       if (myIsoU)
62       {
63         aStartIndex = 1;
64         aEndIndex = myParams->Length();
65       }
66       else
67       {
68         aStartIndex = 2;
69         aEndIndex = myParams->Length() - 1;
70       }
71 
72       for (Standard_Integer i = aStartIndex; i <= aEndIndex; ++i)
73       {
74         myCurrParam = myParams->Value(i);
75         myIso = new GeomAdaptor_Curve(myIsoU ? mySurface->UIso(myCurrParam) : mySurface->VIso(myCurrParam));
76 
77         myPrevControlParam = myControlParams->Value(1);
78         myIso->D1(myPrevControlParam, myPrevControlPnt, myPrevControlVec);
79         for (Standard_Integer j = 2; j <= myControlParams->Length();)
80         {
81           j += checkControlPointAndMoveOn(j);
82         }
83       }
84 
85       return myControlParamsToRemove;
86     }
87 
88   private:
89 
90     //! Checks the given control point for deviation.
91     //! Returns number of steps to be used to move point iterator.
checkControlPointAndMoveOn(const Standard_Integer theIndex)92     Standard_Integer checkControlPointAndMoveOn(const Standard_Integer theIndex)
93     {
94       Standard_Integer aMoveSteps = 0;
95       myCurrControlParam = myControlParams->Value(theIndex);
96       myIso->D1(myCurrControlParam, myCurrControlPnt, myCurrControlVec);
97 
98       const Standard_Real aMidParam = 0.5 * (myPrevControlParam + myCurrControlParam);
99       const gp_Pnt aMidPnt = myIso->Value(aMidParam);
100 
101       const Standard_Real aSqDist = BRepMesh_GeomTool::SquareDeflectionOfSegment(
102         myPrevControlPnt, myCurrControlPnt, aMidPnt);
103 
104       Standard_Real anAngle = 0.0;
105 
106       if ((myPrevControlVec.SquareMagnitude() > Precision::SquareConfusion()) &&
107           (myCurrControlVec.SquareMagnitude() > Precision::SquareConfusion()))
108       {
109         anAngle = myPrevControlVec.Angle(myCurrControlVec);
110       }
111 
112       const Standard_Real aSqMaxDeflection = myDFace->GetDeflection() *
113         myDFace->GetDeflection();
114 
115       if (((aSqDist > aSqMaxDeflection) || (anAngle > myParameters.AngleInterior)) &&
116           aSqDist > myParameters.MinSize * myParameters.MinSize)
117       {
118         // insertion
119         myControlParams->InsertBefore(theIndex, aMidParam);
120       }
121       else
122       {
123         // Here we should leave at least 3 parameters as far as
124         // we must have at least one parameter related to surface
125         // internals in order to prevent movement of triangle body
126         // outside the surface in case of highly curved ones, e.g.
127         // BSpline springs.
128         if (((aSqDist < aSqMaxDeflection) || (anAngle < myParameters.AngleInterior)) &&
129             myControlParams->Length() > 3 && theIndex < myControlParams->Length())
130         {
131           // Remove too dense points
132           const Standard_Real aTmpParam = myControlParams->Value(theIndex + 1);
133           if (checkParameterForDeflectionAndUpdateCache(aTmpParam))
134           {
135             ++aMoveSteps;
136           }
137         }
138 
139         myPrevControlParam = myCurrControlParam;
140         myPrevControlPnt   = myCurrControlPnt;
141         myPrevControlVec   = myCurrControlVec;
142 
143         ++aMoveSteps;
144       }
145 
146       return aMoveSteps;
147     }
148 
149     //! Checks whether the given param suits specified deflection. Updates cache.
checkParameterForDeflectionAndUpdateCache(const Standard_Real theParam)150     Standard_Boolean checkParameterForDeflectionAndUpdateCache(const Standard_Real theParam)
151     {
152       gp_Pnt aTmpPnt;
153       gp_Vec aTmpVec;
154       myIso->D1(theParam, aTmpPnt, aTmpVec);
155 
156       const Standard_Real aTmpMidParam = 0.5 * (myPrevControlParam + theParam);
157       const gp_Pnt        aTmpMidPnt = myIso->Value(aTmpMidParam);
158 
159       // Lets check next parameter.
160       // If it also fits deflection, we can remove previous parameter.
161       const Standard_Real aSqDist = BRepMesh_GeomTool::SquareDeflectionOfSegment(
162         myPrevControlPnt, aTmpPnt, aTmpMidPnt);
163 
164       if (aSqDist < myDFace->GetDeflection() * myDFace->GetDeflection())
165       {
166         // Lets check parameters for angular deflection.
167         if (myPrevControlVec.SquareMagnitude() < gp::Resolution() ||
168             aTmpVec.SquareMagnitude()          < gp::Resolution() ||
169             myPrevControlVec.Angle(aTmpVec)    < myParameters.AngleInterior)
170         {
171           // For current Iso line we can remove this parameter.
172           myControlParamsToRemove->Add(myCurrControlParam);
173           myCurrControlParam = theParam;
174           myCurrControlPnt   = aTmpPnt;
175           myCurrControlVec   = aTmpVec;
176           return Standard_True;
177         }
178         else
179         {
180           // We have found a place on the surface refusing
181           // removement of this parameter.
182           myParamsForbiddenToRemove       ->Add(myCurrParam);
183           myControlParamsForbiddenToRemove->Add(myCurrControlParam);
184         }
185       }
186 
187       return Standard_False;
188     }
189 
190   private:
191 
192     IMeshData::IFaceHandle                myDFace;
193     Handle(Geom_Surface)                  mySurface;
194     Standard_Boolean                      myIsoU;
195     Handle(IMeshData::SequenceOfReal)     myParams;
196     Handle(IMeshData::SequenceOfReal)     myControlParams;
197 
198     Handle(IMeshData::MapOfReal)          myParamsForbiddenToRemove;
199     Handle(IMeshData::MapOfReal)          myControlParamsForbiddenToRemove;
200 
201     Handle(NCollection_IncAllocator)      myAllocator;
202     Handle(IMeshData::MapOfReal)          myControlParamsToRemove;
203 
204 
205     IMeshTools_Parameters                 myParameters;
206     NCollection_Handle<GeomAdaptor_Curve> myIso;
207 
208     Standard_Real                         myCurrParam;
209 
210     Standard_Real                         myCurrControlParam;
211     gp_Pnt                                myCurrControlPnt;
212     gp_Vec                                myCurrControlVec;
213 
214     Standard_Real                         myPrevControlParam;
215     gp_Pnt                                myPrevControlPnt;
216     gp_Vec                                myPrevControlVec;
217   };
218 
219   //! Adds param to map if it fits specified range.
addParam(const Standard_Real & theParam,const std::pair<Standard_Real,Standard_Real> & theRange,IMeshData::IMapOfReal & theParams)220   Standard_Boolean addParam(
221     const Standard_Real&                           theParam,
222     const std::pair<Standard_Real, Standard_Real>& theRange,
223     IMeshData::IMapOfReal&                         theParams)
224   {
225     if (theParam < theRange.first ||
226         theParam > theRange.second)
227     {
228       return Standard_False;
229     }
230 
231     theParams.Add(theParam);
232     return Standard_True;
233   }
234 
235   //! Initializes parameters map using CN intervals.
initParamsFromIntervals(const TColStd_Array1OfReal & theIntervals,const std::pair<Standard_Real,Standard_Real> & theRange,const Standard_Boolean isSplitIntervals,IMeshData::IMapOfReal & theParams)236   Standard_Boolean initParamsFromIntervals(
237     const TColStd_Array1OfReal&                    theIntervals,
238     const std::pair<Standard_Real, Standard_Real>& theRange,
239     const Standard_Boolean                         isSplitIntervals,
240     IMeshData::IMapOfReal&                         theParams)
241   {
242     Standard_Boolean isAdded = Standard_False;
243 
244     for (Standard_Integer i = theIntervals.Lower(); i <= theIntervals.Upper(); ++i)
245     {
246       const Standard_Real aStartParam = theIntervals.Value(i);
247       if (addParam(aStartParam, theRange, theParams))
248       {
249         isAdded = Standard_True;
250       }
251 
252       if (isSplitIntervals && i < theIntervals.Upper())
253       {
254         const Standard_Real aMidParam = (aStartParam + theIntervals.Value(i + 1)) / 2.;
255         if (addParam(aMidParam, theRange, theParams))
256         {
257           isAdded = Standard_True;
258         }
259       }
260     }
261 
262     return isAdded;
263   }
264 
265   //! Checks whether intervals should be split.
266   //! Returns true in case if it is impossible to compute normal
267   //! directly on intervals, false is returned elsewhere.
toSplitIntervals(const Handle (Geom_Surface)& theSurf,const TColStd_Array1OfReal (& theIntervals)[2])268   Standard_Boolean toSplitIntervals (const Handle (Geom_Surface)&  theSurf,
269                                      const TColStd_Array1OfReal  (&theIntervals)[2])
270   {
271     Standard_Integer aIntervalU = theIntervals[0].Lower ();
272     for (; aIntervalU <= theIntervals[0].Upper (); ++aIntervalU)
273     {
274       const Standard_Real aParamU = theIntervals[0].Value(aIntervalU);
275       if (Precision::IsInfinite (aParamU))
276         continue;
277 
278       Standard_Integer aIntervalV = theIntervals[1].Lower ();
279       for (; aIntervalV <= theIntervals[1].Upper (); ++aIntervalV)
280       {
281         gp_Dir aNorm;
282         const Standard_Real aParamV = theIntervals[1].Value(aIntervalV);
283         if (Precision::IsInfinite (aParamV))
284           continue;
285 
286         if (GeomLib::NormEstim (theSurf, gp_Pnt2d (aParamU, aParamV), Precision::Confusion (), aNorm) != 0)
287         {
288           return Standard_True;
289         }
290         // TODO: do not split intervals if there is no normal in the middle of interval.
291       }
292     }
293 
294     return Standard_False;
295   }
296 }
297 
298 //=======================================================================
299 // Function: AdjustRange
300 // Purpose :
301 //=======================================================================
AdjustRange()302 void BRepMesh_NURBSRangeSplitter::AdjustRange()
303 {
304   BRepMesh_DefaultRangeSplitter::AdjustRange();
305   mySurfaceType = GetSurface()->GetType();
306 
307   if (mySurfaceType == GeomAbs_BezierSurface)
308   {
309     const std::pair<Standard_Real, Standard_Real>& aRangeU = GetRangeU();
310     const std::pair<Standard_Real, Standard_Real>& aRangeV = GetRangeV();
311 
312     myIsValid = !(aRangeU.first  < -0.5 ||
313                   aRangeU.second >  1.5 ||
314                   aRangeV.first  < -0.5 ||
315                   aRangeV.second >  1.5);
316   }
317 }
318 
319 //=======================================================================
320 // Function: GenerateSurfaceNodes
321 // Purpose :
322 //=======================================================================
Handle(IMeshData::ListOfPnt2d)323 Handle(IMeshData::ListOfPnt2d) BRepMesh_NURBSRangeSplitter::GenerateSurfaceNodes(
324   const IMeshTools_Parameters& theParameters) const
325 {
326   if (!initParameters())
327   {
328     return Handle(IMeshData::ListOfPnt2d)();
329   }
330 
331   const std::pair<Standard_Real, Standard_Real>& aRangeU = GetRangeU();
332   const std::pair<Standard_Real, Standard_Real>& aRangeV = GetRangeV();
333   const std::pair<Standard_Real, Standard_Real>& aDelta  = GetDelta ();
334 
335   const Standard_Real                 aDefFace = GetDFace()->GetDeflection();
336   const Handle(BRepAdaptor_Surface)& gFace    = GetSurface();
337   Handle(Geom_Surface)                aSurface = gFace->Surface().Surface();
338 
339   const Handle(NCollection_IncAllocator) aTmpAlloc =
340     new NCollection_IncAllocator(IMeshData::MEMORY_BLOCK_SIZE_HUGE);
341 
342   const Handle(IMeshData::SequenceOfReal) aParams[2] = {
343     computeGrainAndFilterParameters(GetParametersU(), gFace->UResolution(aDefFace),
344       (aRangeU.second - aRangeU.first), aDelta.first,  theParameters, aTmpAlloc),
345 
346     computeGrainAndFilterParameters(GetParametersV(), gFace->VResolution(aDefFace),
347       (aRangeV.second - aRangeV.first), aDelta.second, theParameters, aTmpAlloc)
348   };
349 
350   // check intermediate isolines
351   Handle(IMeshData::MapOfReal) aFixedParams[2] = {
352     new IMeshData::MapOfReal(1, aTmpAlloc),
353     new IMeshData::MapOfReal(1, aTmpAlloc)
354   };
355 
356   const Handle(IMeshData::MapOfReal) aParamsToRemove[2] = {
357     AnalyticalFilter(GetDFace(), GeomAbs_IsoV, aParams[1], aParams[0],
358       aFixedParams[1], aFixedParams[0]).GetControlParametersToRemove(theParameters),
359 
360     AnalyticalFilter(GetDFace(), GeomAbs_IsoU, aParams[0], aParams[1],
361       aFixedParams[0], aFixedParams[1]).GetControlParametersToRemove(theParameters),
362   };
363 
364   aParamsToRemove[0]->Subtract(*aFixedParams[0]);
365   aParamsToRemove[1]->Subtract(*aFixedParams[1]);
366 
367   // insert nodes of the regular grid
368   Handle(IMeshData::ListOfPnt2d) aNodes = new IMeshData::ListOfPnt2d(
369     new NCollection_IncAllocator(IMeshData::MEMORY_BLOCK_SIZE_HUGE));
370 
371   // insert nodes of the regular grid
372   for (Standard_Integer i = 1; i <= aParams[0]->Length(); ++i)
373   {
374     const Standard_Real aParam1 = aParams[0]->Value(i);
375     if (aParamsToRemove[0]->Contains(aParam1))
376     {
377       continue;
378     }
379 
380     for (Standard_Integer j = 1; j <= aParams[1]->Length(); ++j)
381     {
382       const Standard_Real aParam2 = aParams[1]->Value(j);
383       if (aParamsToRemove[1]->Contains(aParam2))
384       {
385         continue;
386       }
387 
388       aNodes->Append(gp_Pnt2d(aParam1, aParam2));
389     }
390   }
391 
392   return aNodes;
393 }
394 
395 //=======================================================================
396 // Function: initParameters
397 // Purpose :
398 //=======================================================================
initParameters() const399 Standard_Boolean BRepMesh_NURBSRangeSplitter::initParameters() const
400 {
401   const Handle(BRepAdaptor_Surface)& aSurface = GetSurface();
402 
403   const GeomAbs_Shape aContinuity = GeomAbs_CN;
404   const std::pair<Standard_Integer, Standard_Integer> aIntervalsNb(
405     aSurface->NbUIntervals(aContinuity),
406     aSurface->NbVIntervals(aContinuity)
407   );
408 
409   TColStd_Array1OfReal aIntervals[2] = {
410     TColStd_Array1OfReal(1, aIntervalsNb.first  + 1),
411     TColStd_Array1OfReal(1, aIntervalsNb.second + 1)
412   };
413 
414   aSurface->UIntervals(aIntervals[0], aContinuity);
415   aSurface->VIntervals(aIntervals[1], aContinuity);
416 
417   const Standard_Boolean isSplitIntervals = toSplitIntervals (aSurface->Surface().Surface(), aIntervals);
418 
419   if (!initParamsFromIntervals(aIntervals[0], GetRangeU(), isSplitIntervals,
420                                const_cast<IMeshData::IMapOfReal&>(GetParametersU())))
421   {
422     //if (!grabParamsOfEdges (Edge_Frontier, Param_U))
423     {
424       return Standard_False;
425     }
426   }
427 
428   if (!initParamsFromIntervals(aIntervals[1], GetRangeV(), isSplitIntervals,
429                                const_cast<IMeshData::IMapOfReal&>(GetParametersV())))
430   {
431     //if (!grabParamsOfEdges (Edge_Frontier, Param_V))
432     {
433       return Standard_False;
434     }
435   }
436 
437   return grabParamsOfEdges(Edge_Internal, Param_U | Param_V);
438 }
439 
440 //=======================================================================
441 //function : grabParamsOfInternalEdges
442 //purpose  :
443 //=======================================================================
grabParamsOfEdges(const EdgeType theEdgeType,const Standard_Integer theParamDimensionFlag) const444 Standard_Boolean BRepMesh_NURBSRangeSplitter::grabParamsOfEdges (
445   const EdgeType         theEdgeType,
446   const Standard_Integer theParamDimensionFlag) const
447 {
448   if ((theParamDimensionFlag & (Param_U | Param_V)) == 0)
449   {
450     return Standard_False;
451   }
452 
453   const IMeshData::IFaceHandle& aDFace = GetDFace ();
454   for (Standard_Integer aWireIt = 0; aWireIt < aDFace->WiresNb (); ++aWireIt)
455   {
456     const IMeshData::IWireHandle& aDWire = aDFace->GetWire (aWireIt);
457     for (Standard_Integer aEdgeIt = 0; aEdgeIt < aDWire->EdgesNb (); ++aEdgeIt)
458     {
459       const IMeshData::IEdgePtr& aDEdge = aDWire->GetEdge (aEdgeIt);
460       for (Standard_Integer aPCurveIt = 0; aPCurveIt < aDEdge->PCurvesNb (); ++aPCurveIt)
461       {
462         const IMeshData::IPCurveHandle& aDPCurve = aDEdge->GetPCurve (aPCurveIt);
463         if (aDPCurve->GetFace () == aDFace)
464         {
465           if (theEdgeType == Edge_Internal && !aDPCurve->IsInternal ())
466           {
467             continue;
468           }
469 
470           for (Standard_Integer aPointIt = 0; aPointIt < aDPCurve->ParametersNb (); ++aPointIt)
471           {
472             const gp_Pnt2d& aPnt2d = aDPCurve->GetPoint (aPointIt);
473             if (theParamDimensionFlag & Param_U)
474             {
475               const_cast<IMeshData::IMapOfReal&>(GetParametersU ()).Add (aPnt2d.X ());
476             }
477 
478             if (theParamDimensionFlag & Param_V)
479             {
480               const_cast<IMeshData::IMapOfReal&>(GetParametersV ()).Add (aPnt2d.Y ());
481             }
482           }
483         }
484       }
485     }
486   }
487 
488   return Standard_True;
489 }
490 
491 //=======================================================================
492 //function : computeGrainAndFilterParameters
493 //purpose  :
494 //=======================================================================
Handle(IMeshData::SequenceOfReal)495 Handle(IMeshData::SequenceOfReal) BRepMesh_NURBSRangeSplitter::computeGrainAndFilterParameters(
496   const IMeshData::IMapOfReal&            theSourceParams,
497   const Standard_Real                     theTol2d,
498   const Standard_Real                     theRangeDiff,
499   const Standard_Real                     theDelta,
500   const IMeshTools_Parameters&            theParameters,
501   const Handle(NCollection_IncAllocator)& theAllocator) const
502 {
503   // Sort and filter sequence of parameters
504   Standard_Real aMinDiff = Precision::PConfusion();
505   if (theDelta < 1.)
506   {
507     aMinDiff /= theDelta;
508   }
509 
510   aMinDiff = Max(theParameters.MinSize, aMinDiff);
511 
512   const Standard_Real aDiffMaxLim = 0.1 * theRangeDiff;
513   const Standard_Real aDiffMinLim = Max(0.005 * theRangeDiff,
514                                         2. * theTol2d);
515   const Standard_Real aDiff = Max(theParameters.MinSize,
516                                   Min(aDiffMaxLim, aDiffMinLim));
517   return filterParameters(theSourceParams, aMinDiff, aDiff, theAllocator);
518 }
519 
520 //=======================================================================
521 //function : filterParameters
522 //purpose  :
523 //=======================================================================
Handle(IMeshData::SequenceOfReal)524 Handle(IMeshData::SequenceOfReal) BRepMesh_NURBSRangeSplitter::filterParameters(
525   const IMeshData::IMapOfReal&            theParams,
526   const Standard_Real                     theMinDist,
527   const Standard_Real                     theFilterDist,
528   const Handle(NCollection_IncAllocator)& theAllocator) const
529 {
530   Handle(IMeshData::SequenceOfReal) aResult = new IMeshData::SequenceOfReal(theAllocator);
531 
532   // Sort sequence of parameters
533   const Standard_Integer anInitLen = theParams.Extent();
534 
535   if (anInitLen < 1)
536   {
537     return aResult;
538   }
539 
540   TColStd_Array1OfReal aParamArray(1, anInitLen);
541   Standard_Integer j;
542   for (j = 1; j <= anInitLen; j++)
543     aParamArray(j) = theParams(j);
544 
545   std::sort(aParamArray.begin(), aParamArray.end());
546 
547   // mandatory pre-filtering using the first (minimal) filter value
548   Standard_Integer aParamLength = 1;
549   for (j = 2; j <= anInitLen; j++)
550   {
551     if ((aParamArray(j) - aParamArray(aParamLength)) > theMinDist)
552     {
553       if (++aParamLength < j)
554         aParamArray(aParamLength) = aParamArray(j);
555     }
556   }
557 
558   //perform filtering on series
559   Standard_Real aLastAdded, aLastCandidate;
560   Standard_Boolean isCandidateDefined = Standard_False;
561   aLastAdded = aParamArray(1);
562   aLastCandidate = aLastAdded;
563   aResult->Append(aLastAdded);
564 
565   for (j = 2; j < aParamLength; j++)
566   {
567     Standard_Real aVal = aParamArray(j);
568     if (aVal - aLastAdded > theFilterDist)
569     {
570       //adds the parameter
571       if (isCandidateDefined)
572       {
573         aLastAdded = aLastCandidate;
574         isCandidateDefined = Standard_False;
575         j--;
576       }
577       else
578       {
579         aLastAdded = aVal;
580       }
581       aResult->Append(aLastAdded);
582       continue;
583     }
584 
585     aLastCandidate = aVal;
586     isCandidateDefined = Standard_True;
587   }
588   aResult->Append(aParamArray(aParamLength));
589 
590   return aResult;
591 }
592