1 // Copyright (c) 1999-2014 OPEN CASCADE SAS
3 // This file is part of Open CASCADE Technology software library.
5 // This library is free software; you can redistribute it and/or modify it under
6 // the terms of the GNU Lesser General Public License version 2.1 as published
7 // by the Free Software Foundation, with special exception defined in the file
8 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
9 // distribution for complete text of the license and disclaimer of any warranty.
11 // Alternatively, this file may be used under the terms of Open CASCADE
12 // commercial license or contractual agreement.
14 #include <ApproxInt_KnotTools.hxx>
15 #include <TColgp_Array1OfPnt2d.hxx>
16 #include <TColStd_Array1OfReal.hxx>
17 #include <TColStd_HArray1OfReal.hxx>
18 #include <TColStd_HArray1OfInteger.hxx>
19 #include <math_Vector.hxx>
20 #include <Geom_BSplineCurve.hxx>
21 #include <Geom2d_BSplineCurve.hxx>
22 #include <GeomInt_TheMultiLineOfWLApprox.hxx>
23 #include <NCollection_Sequence.hxx>
24 #include <NCollection_List.hxx>
26 #include <Precision.hxx>
27 #include <NCollection_Vector.hxx>
28 #include <TColgp_Array1OfPnt.hxx>
30 // (Sqrt(5.0) - 1.0) / 4.0
31 static const Standard_Real aSinCoeff = 0.30901699437494742410229341718282;
32 static const Standard_Integer aMaxPntCoeff = 15;
35 //=======================================================================
37 //purpose : Evaluate curvature in dim-dimension point.
38 //=======================================================================
39 static Standard_Real EvalCurv(const Standard_Real dim,
40 const Standard_Real* V1,
41 const Standard_Real* V2)
43 // Really V1 and V2 are arrays of size dim;
44 // V1 is first derivative, V2 is second derivative
45 // of n-dimension curve
46 // Curvature is curv = |V1^V2|/|V1|^3
47 // V1^V2 is outer product of two vectors:
48 // P(i,j) = V1(i)*V2(j) - V1(j)*V2(i);
49 Standard_Real mp = 0.;
50 Standard_Integer i, j;
52 for(i = 1; i < dim; ++i)
54 for(j = 0; j < i; ++j)
56 p = V1[i]*V2[j] - V1[j]*V2[i];
60 //mp *= 2.; //P(j,i) = -P(i,j);
64 for(i = 0; i < dim; ++i)
70 Standard_Real curv = mp / (q*q*q);
75 //=======================================================================
76 //function : ComputeKnotInds
78 //=======================================================================
79 void ApproxInt_KnotTools::ComputeKnotInds(const NCollection_LocalArray<Standard_Real>& theCoords,
80 const Standard_Integer theDim,
81 const math_Vector& thePars,
82 NCollection_Sequence<Standard_Integer>& theInds)
84 //I: Create discrete curvature.
85 NCollection_Sequence<Standard_Integer> aFeatureInds;
86 TColStd_Array1OfReal aCurv(thePars.Lower(), thePars.Upper());
87 // Arrays are allocated for max theDim = 7: 1 3d curve + 2 2d curves.
88 Standard_Real Val[21], Par[3], Res[21];
89 Standard_Integer i, j, k, l, m, ic;
90 Standard_Real aMaxCurv = 0.;
91 Standard_Integer dim = theDim;
94 for(j = 0; j < 3; ++j)
97 ic = (k - aCurv.Lower()) * dim;
99 for(m = 0; m < dim; ++m)
101 Val[l + m] = theCoords[ic + m];
105 PLib::EvalLagrange(Par[0], 2, 2, dim, *Val, *Par, *Res);
107 aCurv(i) = EvalCurv(dim, &Res[dim], &Res[2*dim]);
109 if(aCurv(i) > aMaxCurv)
114 for(i = aCurv.Lower()+1; i < aCurv.Upper(); ++i)
116 for(j = 0; j < 3; ++j)
119 ic = (k - aCurv.Lower()) * dim;
121 for(m = 0; m < dim; ++m)
123 Val[l + m] = theCoords[ic + m];
127 PLib::EvalLagrange(Par[1], 2, 2, dim, *Val, *Par, *Res);
129 aCurv(i) = EvalCurv(dim, &Res[dim], &Res[2*dim]);
130 if(aCurv(i) > aMaxCurv)
137 for(j = 0; j < 3; ++j)
140 ic = (k - aCurv.Lower()) * dim;
142 for(m = 0; m < dim; ++m)
144 Val[l + m] = theCoords[ic + m];
148 PLib::EvalLagrange(Par[2], 2, 2, dim, *Val, *Par, *Res);
150 aCurv(i) = EvalCurv(dim, &Res[dim], &Res[2*dim]);
151 if(aCurv(i) > aMaxCurv)
156 #if defined(APPROXINT_KNOTTOOLS_DEBUG) || defined(OCCT_DEBUG)
157 cout << "Discrete curvature array is" << endl;
158 for(i = aCurv.Lower(); i <= aCurv.Upper(); ++i)
160 cout << i << " " << aCurv(i) << endl;
164 theInds.Append(aCurv.Lower());
165 if(aMaxCurv <= Precision::Confusion())
168 theInds.Append(aCurv.Upper());
172 // II: Find extremas of curvature.
173 // Not used Precision::PConfusion, by different from "param space" eps nature.
174 Standard_Real eps = 1.0e-9,
176 for(i = aCurv.Lower() + 1; i < aCurv.Upper(); ++i)
178 Standard_Real d1 = aCurv(i) - aCurv(i - 1),
179 d2 = aCurv(i) - aCurv(i + 1),
180 ad1 = Abs(d1), ad2 = Abs(d2);
182 if(d1*d2 > 0. && ad1 > eps && ad2 > eps)
184 if(i != theInds.Last())
187 aFeatureInds.Append(i);
190 else if((ad1 < eps && ad2 > eps1) || (ad1 > eps1 && ad2 < eps))
192 if(i != theInds.Last())
195 aFeatureInds.Append(i);
198 else if(aCurv(i)*aCurv(i + 1) < 0.0)
200 if(Abs(aCurv(i)) < Abs(aCurv(i + 1)))
202 if(i != theInds.Last())
205 aFeatureInds.Append(i);
210 if(i+1 != theInds.Last())
212 theInds.Append(i + 1);
213 aFeatureInds.Append(i + 1);
218 if(aCurv.Upper() != theInds.Last())
220 theInds.Append(aCurv.Upper());
223 #if defined(APPROXINT_KNOTTOOLS_DEBUG) || defined(OCCT_DEBUG)
225 cout << "Feature indices new: " << endl;
227 for(i = theInds.Lower(); i <= theInds.Upper(); ++i)
229 cout << i << " : " << theInds(i) << endl;
234 //III: Put knots in monotone intervals of curvature.
241 Ok = InsKnotBefI(i, aCurv, theCoords, dim, theInds, Standard_True);
247 while(i < theInds.Length());
249 //IV: Cheking feature points.
251 for(i = 1; i <= aFeatureInds.Length(); ++i)
253 Standard_Integer anInd = aFeatureInds(i);
254 for(; j <= theInds.Length() - 1;)
256 if(theInds(j) == anInd)
258 Standard_Integer anIndPrev = theInds(j-1);
259 Standard_Integer anIndNext = theInds(j+1);
261 Standard_Integer ici = (anIndPrev - aCurv.Lower()) * theDim,
262 ici1 = (anIndNext - aCurv.Lower()) * theDim,
263 icm = (anInd - aCurv.Lower()) * theDim;
264 NCollection_LocalArray<Standard_Real> V1(theDim), V2(theDim);
265 Standard_Integer k,l;
266 Standard_Real mp = 0., m1 = 0., m2 = 0.;
268 for(k = 0; k < theDim; ++k)
270 V1[k] = theCoords[icm + k] - theCoords[ici + k];
272 V2[k] = theCoords[ici1 + k] - theCoords[icm + k];
275 for(k = 1; k < theDim; ++k)
277 for(l = 0; l < k; ++l)
279 p = V1[k]*V2[l] - V1[l]*V2[k];
283 //mp *= 2.; //P(j,i) = -P(i,j);
291 Standard_Real d1 = Abs(aCurv(anInd) - aCurv(anIndPrev));
292 Standard_Real d2 = Abs(aCurv(anInd) - aCurv(anIndNext));
295 Ok = InsKnotBefI(j, aCurv, theCoords, dim, theInds, Standard_False);
307 Ok = InsKnotBefI(j+1, aCurv, theCoords, dim, theInds, Standard_False);
330 //=======================================================================
331 //function : FilterKnots
333 //=======================================================================
334 void ApproxInt_KnotTools::FilterKnots(NCollection_Sequence<Standard_Integer>& theInds,
335 const Standard_Integer theMinNbPnts,
336 NCollection_Vector<Standard_Integer>& theLKnots)
338 // Maximum number of points per knot interval.
339 Standard_Integer aMaxNbPnts = aMaxPntCoeff*theMinNbPnts;
340 Standard_Integer i = 1;
341 Standard_Integer aMinNbStep = theMinNbPnts / 2;
343 // I: Filter too big number of points per knot interval.
344 while(i < theInds.Length())
346 Standard_Integer nbint = theInds(i + 1) - theInds(i) + 1;
347 if(nbint <= aMaxNbPnts)
354 Standard_Integer ind = theInds(i) + nbint / 2;
355 theInds.InsertAfter(i, ind);
359 // II: Filter poins with too small amount of points per knot interval.
361 theLKnots.Append(theInds(i));
362 Standard_Integer anIndsPrev = theInds(i);
363 for(i = 2; i <= theInds.Length(); ++i)
365 if(theInds(i) - anIndsPrev <= theMinNbPnts)
367 if (i != theInds.Length())
369 Standard_Integer anIdx = i + 1;
370 for( ; anIdx <= theInds.Length(); ++anIdx)
372 if (theInds(anIdx) - anIndsPrev > theMinNbPnts)
377 Standard_Integer aMidIdx = (theInds(anIdx) + anIndsPrev) / 2;
378 if (aMidIdx - anIndsPrev < theMinNbPnts &&
379 aMidIdx - theInds(anIdx) < theMinNbPnts &&
380 theInds(anIdx) - anIndsPrev >= aMinNbStep)
382 // Bad distribution points merge into one knot interval.
383 theLKnots.Append(theInds(anIdx));
384 anIndsPrev = theInds(anIdx);
387 else if (anIdx == theInds.Upper() && // Last point obtained.
388 theLKnots.Length() >= 2) // It is possible to modify last item.
390 // Current bad interval from i to last.
391 // Trying to add knot to divide sequence on two parts:
392 // Last good index -> Last index - theMinNbPnts -> Last index
393 Standard_Integer aLastGoodIdx = theLKnots.Value(theLKnots.Upper() - 1);
394 if ( theInds.Last() - 2 * theMinNbPnts >= aLastGoodIdx)
396 theLKnots(theLKnots.Upper()) = theInds.Last() - theMinNbPnts;
397 theLKnots.Append(theInds.Last());
398 anIndsPrev = theInds(anIdx);
402 } // if (i != theInds.Length())
407 theLKnots.Append(theInds(i));
408 anIndsPrev = theInds(i);
412 // III: Fill Last Knot.
413 if(theLKnots.Length() < 2)
415 theLKnots.Append(theInds.Last());
419 if(theLKnots.Last() < theInds.Last())
421 theLKnots(theLKnots.Upper()) = theInds.Last();
425 //=======================================================================
426 //function : InsKnotBefI
428 //=======================================================================
429 Standard_Boolean ApproxInt_KnotTools::InsKnotBefI(const Standard_Integer theI,
430 const TColStd_Array1OfReal& theCurv,
431 const NCollection_LocalArray<Standard_Real>& theCoords,
432 const Standard_Integer theDim,
433 NCollection_Sequence<Standard_Integer>& theInds,
434 const Standard_Boolean ChkCurv)
436 Standard_Integer anInd1 = theInds(theI);
437 Standard_Integer anInd = theInds(theI - 1);
439 if((anInd1-anInd) == 1)
441 return Standard_False;
444 Standard_Real curv = 0.5*(theCurv(anInd) + theCurv(anInd1));
445 Standard_Integer mid = 0, j, jj;
446 const Standard_Real aLimitCurvatureChange = 3.0;
447 for(j = anInd+1; j < anInd1; ++j)
451 // I: Curvature change criteria:
452 // Non-null curvature.
453 if (theCurv(j) > Precision::Confusion() &&
454 theCurv(anInd) > Precision::Confusion() )
456 if (theCurv(j) / theCurv(anInd) > aLimitCurvatureChange ||
457 theCurv(j) / theCurv(anInd) < 1.0 / aLimitCurvatureChange)
459 // Curvature on current interval changed more than 3 times.
461 theInds.InsertBefore(theI, mid);
462 return Standard_True;
466 // II: Angular criteria:
467 Standard_Real ac = theCurv(j - 1), ac1 = theCurv(j);
468 if((curv >= ac && curv <= ac1) || (curv >= ac1 && curv <= ac))
470 if(Abs(curv - ac) < Abs(curv - ac1))
492 Standard_Integer ici = (anInd - theCurv.Lower()) * theDim,
493 ici1 = (anInd1 - theCurv.Lower()) * theDim,
494 icm = (mid - theCurv.Lower()) * theDim;
495 NCollection_LocalArray<Standard_Real> V1(theDim), V2(theDim);
497 Standard_Real mp = 0., m1 = 0., m2 = 0.;
499 for(i = 0; i < theDim; ++i)
501 V1[i] = theCoords[icm + i] - theCoords[ici + i];
503 V2[i] = theCoords[ici1 + i] - theCoords[icm + i];
506 for(i = 1; i < theDim; ++i)
508 for(jj = 0; jj < i; ++jj)
510 p = V1[i]*V2[jj] - V1[jj]*V2[i];
514 //mp *= 2.; //P(j,i) = -P(i,j);
521 theInds.InsertBefore(theI, mid);
522 return Standard_True;
527 theInds.InsertBefore(theI, mid);
528 return Standard_True;
533 return Standard_False;
536 //=======================================================================
537 //function : BuildKnots
539 //=======================================================================
540 void ApproxInt_KnotTools::BuildKnots(const TColgp_Array1OfPnt& thePntsXYZ,
541 const TColgp_Array1OfPnt2d& thePntsU1V1,
542 const TColgp_Array1OfPnt2d& thePntsU2V2,
543 const math_Vector& thePars,
544 const Standard_Boolean theApproxXYZ,
545 const Standard_Boolean theApproxU1V1,
546 const Standard_Boolean theApproxU2V2,
547 const Standard_Integer theMinNbPnts,
548 NCollection_Vector<Standard_Integer>& theKnots)
550 NCollection_Sequence<Standard_Integer> aKnots;
551 Standard_Integer aDim = 0;
553 // I: Convert input data to the corresponding format.
561 NCollection_LocalArray<Standard_Real> aCoords(thePars.Length()*aDim);
562 Standard_Integer i, j;
563 for(i = thePars.Lower(); i <= thePars.Upper(); ++i)
565 j = (i - thePars.Lower()) * aDim;
568 aCoords[j] = thePntsXYZ.Value(i).X();
570 aCoords[j] = thePntsXYZ.Value(i).Y();
572 aCoords[j] = thePntsXYZ.Value(i).Z();
577 aCoords[j] = thePntsU1V1.Value(i).X();
579 aCoords[j] = thePntsU1V1.Value(i).Y();
584 aCoords[j] = thePntsU2V2.Value(i).X();
586 aCoords[j] = thePntsU2V2.Value(i).Y();
591 // II: Build draft knot sequence.
592 ComputeKnotInds(aCoords, aDim, thePars, aKnots);
594 #if defined(APPROXINT_KNOTTOOLS_DEBUG) || defined(OCCT_DEBUG)
595 cout << "Draft knot sequence: " << endl;
596 for(i = aKnots.Lower(); i <= aKnots.Upper(); ++i)
598 cout << i << " : " << aKnots(i) << endl;
602 // III: Build output knot sequence.
603 FilterKnots(aKnots, theMinNbPnts, theKnots);
605 #if defined(APPROXINT_KNOTTOOLS_DEBUG) || defined(OCCT_DEBUG)
606 cout << "Result knot sequence: " << endl;
607 for(i = theKnots.Lower(); i <= theKnots.Upper(); ++i)
609 cout << i << " : " << theKnots(i) << endl;