1 // Created on: 1992-10-19
2 // Created by: Laurent PAINNOT
3 // Copyright (c) 1992-1999 Matra Datavision
4 // Copyright (c) 1999-2014 OPEN CASCADE SAS
6 // This file is part of Open CASCADE Technology software library.
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.
14 // Alternatively, this file may be used under the terms of Open CASCADE
15 // commercial license or contractual agreement.
17 // Modified by cma, Fri Dec 10 18:11:56 1993
19 #include Extrema_EPC_hxx
21 #include TheSequenceOfPOnC_hxx
23 #include TheVector_hxx
24 #include TheExtPElC_hxx
25 #include <StdFail_NotDone.hxx>
26 #include <Standard_Failure.hxx>
27 #include <GeomAbs_CurveType.hxx>
28 #include <Precision.hxx>
30 #include <TColStd_Array1OfReal.hxx>
31 #include <NCollection_Array1.hxx>
34 //=======================================================================
37 //=======================================================================
38 void Extrema_GExtPC::Perform(const ThePoint& P)
43 Standard_Integer i, NbExt, n;
46 Standard_Real t3d = Precision::Confusion();
47 if (Precision::IsInfinite(myuinf)) mydist1 = RealLast();
49 Pf = TheCurveTool::Value(*((TheCurve*)myC), myuinf);
50 mydist1 = P.SquareDistance(Pf);
53 if (Precision::IsInfinite(myusup)) mydist2 = RealLast();
55 Pl = TheCurveTool::Value(*((TheCurve*)myC), myusup);
56 mydist2 = P.SquareDistance(Pl);
59 TheCurve & aCurve = *((TheCurve*)myC);
64 myExtPElC.Perform(P, TheCurveTool::Circle(aCurve), t3d, myuinf, myusup);
69 myExtPElC.Perform(P, TheCurveTool::Ellipse(aCurve), t3d, myuinf, myusup);
72 case GeomAbs_Parabola:
74 myExtPElC.Perform(P, TheCurveTool::Parabola(aCurve), t3d,myuinf,myusup);
77 case GeomAbs_Hyperbola:
79 myExtPElC.Perform(P,TheCurveTool::Hyperbola(aCurve),t3d, myuinf, myusup);
84 myExtPElC.Perform(P, TheCurveTool::Line(aCurve), t3d, myuinf, myusup);
87 case GeomAbs_BezierCurve:
91 mysample = (TheCurveTool::Bezier(aCurve))->NbPoles() * 2;
92 myExtPC.Initialize(aCurve);
96 case GeomAbs_BSplineCurve:
98 const Standard_Integer
99 aFirstIdx = TheCurveTool::BSpline(aCurve)->FirstUKnotIndex(),
100 aLastIdx = TheCurveTool::BSpline(aCurve)->LastUKnotIndex();
101 // const reference can not be used due to implementation of BRep_Adaptor.
102 TColStd_Array1OfReal aKnots(aFirstIdx, aLastIdx);
103 TheCurveTool::BSpline(aCurve)->Knots(aKnots);
105 // Workaround to work with:
106 // blend, where knots may be moved from param space.
107 Standard_Real aPeriodJump = 0.0;
108 if (TheCurveTool::IsPeriodic(aCurve))
110 Standard_Integer aPeriodShift =
111 Standard_Integer ((myuinf - aKnots(aFirstIdx)) / TheCurveTool::Period(aCurve));
113 if (myuinf < aKnots(aFirstIdx))
116 aPeriodJump = TheCurveTool::Period(aCurve) * aPeriodShift;
119 Standard_Integer anIdx;
121 // Find first and last used knot
122 Standard_Integer aFirstUsedKnot = aFirstIdx,
123 aLastUsedKnot = aLastIdx;
124 for(anIdx = aFirstIdx; anIdx <= aLastIdx; anIdx++)
126 Standard_Real aKnot = aKnots(anIdx) + aPeriodJump;
128 aFirstUsedKnot = anIdx;
133 for(anIdx = aLastIdx; anIdx >= aFirstIdx; anIdx--)
135 Standard_Real aKnot = aKnots(anIdx) + aPeriodJump;
137 aLastUsedKnot = anIdx;
142 mysample = (TheCurveTool::BSpline(aCurve))->Degree() + 1;
144 // Fill sample points.
145 Standard_Integer aValIdx = 1;
146 NCollection_Array1<Standard_Real> aVal(1, (mysample) * (aLastUsedKnot - aFirstUsedKnot) + 1);
147 NCollection_Array1<Standard_Real> aParam(1, (mysample) * (aLastUsedKnot - aFirstUsedKnot) + 1);
148 for(anIdx = aFirstUsedKnot; anIdx < aLastUsedKnot; anIdx++)
150 Standard_Real aF = aKnots(anIdx) + aPeriodJump,
151 aL = aKnots(anIdx + 1) + aPeriodJump;
153 if (anIdx == aFirstUsedKnot)
155 if (anIdx == aLastUsedKnot - 1)
158 Standard_Real aStep = (aL - aF) / mysample;
159 for(Standard_Integer aPntIdx = 0; aPntIdx < mysample; aPntIdx++)
161 Standard_Real aCurrentParam = aF + aStep * aPntIdx;
162 aVal(aValIdx) = TheCurveTool::Value(aCurve, aCurrentParam).SquareDistance(P);
163 aParam(aValIdx) = aCurrentParam;
168 aVal(aValIdx) = TheCurveTool::Value(aCurve, myusup).SquareDistance(P);
169 aParam(aValIdx) = myusup;
171 myExtPC.Initialize(aCurve);
174 for(anIdx = aVal.Lower() + 1; anIdx < aVal.Upper(); anIdx++)
176 if (aVal(anIdx) <= Precision::SquareConfusion())
178 mySqDist.Append(aVal(anIdx));
179 myismin.Append(Standard_True);
180 mypoint.Append(ThePOnC(aParam(anIdx), TheCurveTool::Value(aCurve, aParam(anIdx))));
182 if ((aVal(anIdx) >= aVal(anIdx + 1) &&
183 aVal(anIdx) >= aVal(anIdx - 1)) ||
184 (aVal(anIdx) <= aVal(anIdx + 1) &&
185 aVal(anIdx) <= aVal(anIdx - 1)) )
187 myintuinf = aParam(anIdx - 1);
188 myintusup = aParam(anIdx + 1);
194 // Solve on first and last interval.
195 if (mydist1 > Precision::SquareConfusion())
199 TheCurveTool::D1(aCurve, aParam.Value(aParam.Lower()), aP1, aV1);
200 TheCurveTool::D1(aCurve, aParam.Value(aParam.Lower() + 1), aP2, aV2);
201 TheVector aBase1(P, aP1), aBase2(P, aP2);
202 Standard_Real aVal1 = aV1.Dot(aBase1); // Derivative of (C(u) - P)^2
203 Standard_Real aVal2 = aV2.Dot(aBase2); // Derivative of (C(u) - P)^2
205 // Derivatives have opposite signs - min or max inside of interval (sufficient condition).
206 // Necessary condition - when point lies on curve.
207 if(aVal1 * aVal2 <= 0.0 ||
208 aBase1.Dot(aBase2) <= 0.0)
210 myintuinf = aParam(aVal.Lower());
211 myintusup = aParam(aVal.Lower() + 1);
216 if (mydist2 > Precision::SquareConfusion())
220 TheCurveTool::D1(aCurve, aParam.Value(aParam.Upper() - 1), aP1, aV1);
221 TheCurveTool::D1(aCurve, aParam.Value(aParam.Upper()), aP2, aV2);
222 TheVector aBase1(P, aP1), aBase2(P, aP2);
223 Standard_Real aVal1 = aV1.Dot(aBase1); // Derivative of (C(u) - P)^2
224 Standard_Real aVal2 = aV2.Dot(aBase2); // Derivative of (C(u) - P)^2
226 // Derivatives have opposite signs - min or max inside of interval (sufficient condition).
227 // Necessary condition - when point lies on curve.
228 if(aVal1 * aVal2 <= 0.0 ||
229 aBase1.Dot(aBase2) <= 0.0)
231 myintuinf = aParam(aVal.Upper() - 1);
232 myintusup = aParam(aVal.Upper());
237 mydone = Standard_True;
240 case GeomAbs_OtherCurve:
242 Standard_Boolean IntExtIsDone = Standard_False;
243 Standard_Boolean IntIsNotValid;
244 n = TheCurveTool::NbIntervals(aCurve, GeomAbs_C2);
245 TColStd_Array1OfReal theInter(1, n+1);
246 Standard_Boolean isPeriodic = TheCurveTool::IsPeriodic(aCurve);
247 TheCurveTool::Intervals(aCurve, theInter, GeomAbs_C2);
248 mysample = Max(mysample/n, 17);
251 Standard_Real s1 = 0.0 ;
252 Standard_Real s2 = 0.0;
253 myExtPC.Initialize(aCurve);
254 for (i = 1; i <= n; i++)
256 myintuinf = theInter(i);
257 myintusup = theInter(i+1);
259 Standard_Real anInfToCheck = myintuinf;
260 Standard_Real aSupToCheck = myintusup;
263 Standard_Real aPeriod = TheCurveTool::Period(aCurve);
264 anInfToCheck = ElCLib::InPeriod(myintuinf, myuinf, myuinf+aPeriod);
265 aSupToCheck = myintusup+(anInfToCheck-myintuinf);
267 IntIsNotValid = (myuinf > aSupToCheck) || (myusup < anInfToCheck);
269 if(IntIsNotValid) continue;
271 if (myuinf >= anInfToCheck) anInfToCheck = myuinf;
272 if (myusup <= aSupToCheck) aSupToCheck = myusup;
273 if((aSupToCheck - anInfToCheck) <= mytolu) continue;
277 TheCurveTool::D1(aCurve, myintuinf, PP, V1);
278 s1 = (TheVector(P, PP))*V1;
280 mySqDist.Append(PP.SquareDistance(P));
281 myismin.Append((s1 < 0.0));
282 mypoint.Append(ThePOnC(myintuinf, PP));
286 TheCurveTool::D1(aCurve, myintusup, PP, V1);
287 s2 = (TheVector(P, PP))*V1;
291 IntExtIsDone = IntExtIsDone || mydone;
294 mydone = IntExtIsDone;
300 if (type == GeomAbs_BSplineCurve ||
301 type == GeomAbs_OtherCurve)
303 // Additional checking if the point is on the first or last point of the curve
304 // and does not added yet.
305 if (mydist1 < Precision::SquareConfusion() ||
306 mydist2 < Precision::SquareConfusion())
308 Standard_Boolean isFirstAdded = Standard_False;
309 Standard_Boolean isLastAdded = Standard_False;
310 Standard_Integer aNbPoints = mypoint.Length();
311 for (i = 1; i <= aNbPoints; i++)
313 U = mypoint.Value(i).Parameter();
314 if (Abs(U - myuinf) < mytolu)
315 isFirstAdded = Standard_True;
316 else if (Abs(myusup - U) < mytolu)
317 isLastAdded = Standard_True;
319 if (!isFirstAdded && mydist1 < Precision::SquareConfusion())
321 mySqDist.Prepend(mydist1);
322 myismin.Prepend(Standard_True);
323 mypoint.Prepend(ThePOnC(myuinf, Pf));
325 if (!isLastAdded && mydist2 < Precision::SquareConfusion())
327 mySqDist.Append(mydist2);
328 myismin.Append(Standard_True);
329 mypoint.Append(ThePOnC(myusup, Pl));
331 mydone = Standard_True;
336 // In analytical case
337 mydone = myExtPElC.IsDone();
340 NbExt = myExtPElC.NbExt();
341 for (i = 1; i <= NbExt; i++)
343 // Verification de la validite des parametres:
344 ThePOnC PC = myExtPElC.Point(i);
346 if (TheCurveTool::IsPeriodic(aCurve))
348 U = ElCLib::InPeriod(U, myuinf, myuinf+TheCurveTool::Period(aCurve));
350 if ((U >= myuinf-mytolu) && (U <= myusup+mytolu))
352 PC.SetValues(U, myExtPElC.Point(i).Value());
353 mySqDist.Append(myExtPElC.SquareDistance(i));
354 myismin.Append(myExtPElC.IsMin(i));
363 //=======================================================================
364 //function : Initialize
366 //=======================================================================
368 void Extrema_GExtPC::Initialize(const TheCurve& C,
369 const Standard_Real Uinf,
370 const Standard_Real Usup,
371 const Standard_Real TolF)
373 myC = (Standard_Address)&C;
374 myintuinf = myuinf = Uinf;
375 myintusup = myusup = Usup;
377 mytolu = TheCurveTool::Resolution(*((TheCurve*)myC), Precision::Confusion());
378 type = TheCurveTool::GetType(C);
379 mydone = Standard_False;
380 mydist1 = RealLast();
381 mydist2 = RealLast();
386 //=======================================================================
387 //function : IntervalPerform
389 //=======================================================================
391 void Extrema_GExtPC::IntervalPerform(const ThePoint& P)
395 myExtPC.Initialize(mysample, myintuinf, myintusup, mytolu, mytolf);
397 mydone = myExtPC.IsDone();
400 Standard_Integer NbExt = myExtPC.NbExt();
401 for (i = 1; i <= NbExt; i++)
403 // Verification de la validite des parametres pour le cas trimme:
404 ThePOnC PC = myExtPC.Point(i);
406 if (TheCurveTool::IsPeriodic(*((TheCurve*)myC)))
408 U = ElCLib::InPeriod(U, myuinf, myuinf+TheCurveTool::Period(*((TheCurve*)myC)));
410 if ((U >= myuinf - mytolu) && (U <= myusup + mytolu))
412 PC.SetValues(U, PC.Value());
413 mySqDist.Append(myExtPC.SquareDistance(i));
414 myismin.Append(myExtPC.IsMin(i));
424 //=======================================================================
425 //function : Extrema_GExtPC
427 //=======================================================================
429 Extrema_GExtPC::Extrema_GExtPC()
432 mydone = Standard_False;
433 mydist1 = RealLast();
434 mydist2 = RealLast();
438 myintuinf = myintusup = myuinf = myusup = Precision::Infinite();
439 type = GeomAbs_OtherCurve;
442 //=======================================================================
443 //function : Extrema_GExtPC
445 //=======================================================================
447 Extrema_GExtPC::Extrema_GExtPC(const ThePoint& P,
449 const Standard_Real Uinf,
450 const Standard_Real Usup,
451 const Standard_Real TolF)
453 Initialize(C, Uinf, Usup, TolF);
457 //=======================================================================
458 //function : Extrema_GExtPC
460 //=======================================================================
462 Extrema_GExtPC::Extrema_GExtPC(const ThePoint& P,
464 const Standard_Real TolF)
466 Initialize(C, TheCurveTool::FirstParameter(C),
467 TheCurveTool::LastParameter(C), TolF);
472 //=======================================================================
475 //=======================================================================
477 Standard_Boolean Extrema_GExtPC::IsDone() const
483 //=======================================================================
486 //=======================================================================
488 Standard_Real Extrema_GExtPC::SquareDistance(const Standard_Integer N) const
490 if(!mydone) StdFail_NotDone::Raise();
491 if ((N < 1) || (N > mySqDist.Length())) Standard_OutOfRange::Raise();
492 return mySqDist.Value(N);
496 //=======================================================================
499 //=======================================================================
501 Standard_Integer Extrema_GExtPC::NbExt() const
503 if(!mydone) StdFail_NotDone::Raise();
504 return mySqDist.Length();
508 //=======================================================================
511 //=======================================================================
513 Standard_Boolean Extrema_GExtPC::IsMin(const Standard_Integer N) const
515 if(!mydone) StdFail_NotDone::Raise();
516 if ((N < 1) || (N > mySqDist.Length())) Standard_OutOfRange::Raise();
517 return myismin.Value(N);
522 //=======================================================================
525 //=======================================================================
527 const ThePOnC & Extrema_GExtPC::Point(const Standard_Integer N) const
529 if(!mydone) StdFail_NotDone::Raise();
530 if ((N < 1) || (N > mySqDist.Length())) Standard_OutOfRange::Raise();
531 return mypoint.Value(N);
535 //=======================================================================
536 //function : TrimmedDistances
538 //=======================================================================
540 void Extrema_GExtPC::TrimmedSquareDistances(Standard_Real& dist1,
541 Standard_Real& dist2,