File indexing completed on 2026-09-28 09:19:57
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014 #ifndef _ExtremaPC_HeaderFile
0015 #define _ExtremaPC_HeaderFile
0016
0017 #include <gp_Pnt.hxx>
0018 #include <MathUtils_Domain.hxx>
0019 #include <NCollection_DynamicArray.hxx>
0020 #include <Precision.hxx>
0021
0022 #include <limits>
0023 #include <optional>
0024
0025
0026
0027
0028
0029
0030
0031
0032 namespace ExtremaPC
0033 {
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043 constexpr double THE_DEFAULT_TOLERANCE = Precision::Confusion();
0044
0045
0046
0047 constexpr double THE_PARAM_TOLERANCE = Precision::PConfusion();
0048
0049
0050
0051 constexpr double THE_NEIGHBOR_STEP_RATIO = 0.01;
0052
0053
0054
0055 constexpr double THE_INTERVAL_EXPAND_RATIO = 0.05;
0056
0057
0058
0059 constexpr double THE_REFINEMENT_STEP_RATIO = 0.001;
0060
0061
0062 constexpr double THE_NEWTON_XTOL_FACTOR = 0.01;
0063
0064
0065 constexpr double THE_NEWTON_FTOL_FACTOR = 0.001;
0066
0067
0068 constexpr double THE_HINT_SEARCH_RADIUS = 0.1;
0069
0070
0071 constexpr double THE_RANGE_NARROWING_FACTOR = 0.25;
0072
0073
0074
0075 constexpr double THE_MAX_SKIP_THRESHOLD = 0.9;
0076
0077
0078
0079 constexpr double THE_NEAR_ZERO_F_FACTOR = 10.0;
0080
0081
0082
0083 constexpr double THE_FALLBACK_F_FACTOR = 100.0;
0084
0085
0086 constexpr int THE_MAX_NEWTON_ITERATIONS = 20;
0087
0088
0089 constexpr int THE_REFINEMENT_NB_SAMPLES = 20;
0090
0091
0092 constexpr int THE_REFINEMENT_NB_PASSES = 3;
0093
0094
0095 constexpr int THE_BEZIER_MIN_SAMPLES = 24;
0096
0097
0098 constexpr int THE_BEZIER_DEGREE_MULTIPLIER = 3;
0099
0100
0101 constexpr int THE_OTHER_CURVE_NB_SAMPLES = 64;
0102
0103
0104 constexpr int THE_BSPLINE_FALLBACK_SAMPLES = 32;
0105
0106
0107
0108
0109
0110
0111 constexpr int THE_BSPLINE_SPAN_MULTIPLIER = 2;
0112
0113
0114 using Domain1D = MathUtils::Domain1D;
0115
0116
0117 enum class Status
0118 {
0119 OK,
0120 NotDone,
0121 InfiniteSolutions,
0122 NoSolution,
0123 NumericalError
0124 };
0125
0126
0127
0128 enum class SearchMode
0129 {
0130 MinMax,
0131 Min,
0132 Max
0133 };
0134
0135
0136 struct ExtremumResult
0137 {
0138 double Parameter = 0.0;
0139 gp_Pnt Point;
0140 double SquareDistance = 0.0;
0141 bool IsMinimum = true;
0142 };
0143
0144
0145
0146 struct Result
0147 {
0148 ExtremaPC::Status Status = ExtremaPC::Status::NotDone;
0149 NCollection_DynamicArray<ExtremumResult> Extrema{8};
0150
0151
0152
0153 double InfiniteSquareDistance = 0.0;
0154
0155
0156 Result() = default;
0157
0158
0159 Result(const Result&) = delete;
0160
0161
0162 Result& operator=(const Result&) = delete;
0163
0164
0165 Result(Result&&) = default;
0166
0167
0168 Result& operator=(Result&&) = default;
0169
0170
0171 bool IsDone() const { return Status == Status::OK; }
0172
0173
0174 bool IsInfinite() const { return Status == Status::InfiniteSolutions; }
0175
0176
0177 int NbExt() const { return Extrema.Length(); }
0178
0179
0180 const ExtremumResult& operator[](int theIndex) const { return Extrema.Value(theIndex); }
0181
0182
0183
0184 double MinSquareDistance() const
0185 {
0186 if (Extrema.IsEmpty())
0187 {
0188 return std::numeric_limits<double>::infinity();
0189 }
0190 double aMinSqDist = Extrema.Value(0).SquareDistance;
0191 for (int i = 1; i < Extrema.Length(); ++i)
0192 {
0193 if (Extrema.Value(i).SquareDistance < aMinSqDist)
0194 {
0195 aMinSqDist = Extrema.Value(i).SquareDistance;
0196 }
0197 }
0198 return aMinSqDist;
0199 }
0200
0201
0202
0203 int MinIndex() const
0204 {
0205 if (Extrema.IsEmpty())
0206 {
0207 return -1;
0208 }
0209 int aMinIdx = 0;
0210 double aMinSqDist = Extrema.Value(0).SquareDistance;
0211 for (int i = 1; i < Extrema.Length(); ++i)
0212 {
0213 if (Extrema.Value(i).SquareDistance < aMinSqDist)
0214 {
0215 aMinSqDist = Extrema.Value(i).SquareDistance;
0216 aMinIdx = i;
0217 }
0218 }
0219 return aMinIdx;
0220 }
0221
0222
0223
0224 double MaxSquareDistance() const
0225 {
0226 if (Extrema.IsEmpty())
0227 {
0228 return 0.0;
0229 }
0230 double aMaxSqDist = Extrema.Value(0).SquareDistance;
0231 for (int i = 1; i < Extrema.Length(); ++i)
0232 {
0233 if (Extrema.Value(i).SquareDistance > aMaxSqDist)
0234 {
0235 aMaxSqDist = Extrema.Value(i).SquareDistance;
0236 }
0237 }
0238 return aMaxSqDist;
0239 }
0240
0241
0242
0243 int MaxIndex() const
0244 {
0245 if (Extrema.IsEmpty())
0246 {
0247 return -1;
0248 }
0249 int aMaxIdx = 0;
0250 double aMaxSqDist = Extrema.Value(0).SquareDistance;
0251 for (int i = 1; i < Extrema.Length(); ++i)
0252 {
0253 if (Extrema.Value(i).SquareDistance > aMaxSqDist)
0254 {
0255 aMaxSqDist = Extrema.Value(i).SquareDistance;
0256 aMaxIdx = i;
0257 }
0258 }
0259 return aMaxIdx;
0260 }
0261
0262
0263
0264 void Clear()
0265 {
0266 Status = Status::NotDone;
0267 Extrema.Clear();
0268 InfiniteSquareDistance = 0.0;
0269 }
0270 };
0271
0272
0273 struct Config
0274 {
0275 double Tolerance = THE_DEFAULT_TOLERANCE;
0276 std::optional<Domain1D> Domain;
0277 int NbSamples = 32;
0278 SearchMode Mode = SearchMode::MinMax;
0279 bool IncludeEndpoints = true;
0280 };
0281
0282
0283
0284
0285
0286
0287
0288
0289
0290
0291
0292
0293
0294
0295
0296
0297
0298
0299
0300
0301 template <typename CurveEvaluator>
0302 inline void AddEndpointExtrema(Result& theResult,
0303 const gp_Pnt& theP,
0304 const Domain1D& theDomain,
0305 const CurveEvaluator& theEval,
0306 double theTol,
0307 SearchMode theMode)
0308 {
0309
0310 if (!theDomain.IsFinite())
0311 {
0312 return;
0313 }
0314
0315 const double theUMin = theDomain.Min;
0316 const double theUMax = theDomain.Max;
0317
0318
0319 auto isDuplicate = [&](double theU, const gp_Pnt& thePt) -> bool {
0320 for (int i = 0; i < theResult.Extrema.Length(); ++i)
0321 {
0322
0323 if (std::abs(theResult.Extrema.Value(i).Parameter - theU) < theTol)
0324 {
0325 return true;
0326 }
0327
0328 double aSqDist = theResult.Extrema.Value(i).Point.SquareDistance(thePt);
0329 if (aSqDist < theTol * theTol)
0330 {
0331 return true;
0332 }
0333 }
0334 return false;
0335 };
0336
0337
0338 gp_Pnt aPtMin = theEval.Value(theUMin);
0339 gp_Pnt aPtMax = theEval.Value(theUMax);
0340
0341 double aSqDistMin = theP.SquareDistance(aPtMin);
0342 double aSqDistMax = theP.SquareDistance(aPtMax);
0343
0344
0345 double aStep = (theUMax - theUMin) * THE_NEIGHBOR_STEP_RATIO;
0346 if (aStep < theTol)
0347 {
0348 aStep = theTol;
0349 }
0350
0351
0352 gp_Pnt aNeighborMin = theEval.Value(theUMin + aStep);
0353 double aNeighborDistMin = theP.SquareDistance(aNeighborMin);
0354 bool aIsMinAtUMin = (aSqDistMin <= aNeighborDistMin);
0355 bool aIsMaxAtUMin = (aSqDistMin >= aNeighborDistMin);
0356
0357
0358 gp_Pnt aNeighborMax = theEval.Value(theUMax - aStep);
0359 double aNeighborDistMax = theP.SquareDistance(aNeighborMax);
0360 bool aIsMinAtUMax = (aSqDistMax <= aNeighborDistMax);
0361 bool aIsMaxAtUMax = (aSqDistMax >= aNeighborDistMax);
0362
0363
0364 bool aEndpointsAreSame = aPtMin.SquareDistance(aPtMax) < theTol * theTol;
0365
0366
0367 if (aEndpointsAreSame)
0368 {
0369 return;
0370 }
0371
0372
0373 if (theMode == SearchMode::Min || theMode == SearchMode::MinMax)
0374 {
0375
0376 if (aIsMinAtUMin && !isDuplicate(theUMin, aPtMin))
0377 {
0378 ExtremumResult anExt;
0379 anExt.Parameter = theUMin;
0380 anExt.Point = aPtMin;
0381 anExt.SquareDistance = aSqDistMin;
0382 anExt.IsMinimum = true;
0383 theResult.Extrema.Append(anExt);
0384 }
0385
0386 if (aIsMinAtUMax && !aEndpointsAreSame && !isDuplicate(theUMax, aPtMax))
0387 {
0388 ExtremumResult anExt;
0389 anExt.Parameter = theUMax;
0390 anExt.Point = aPtMax;
0391 anExt.SquareDistance = aSqDistMax;
0392 anExt.IsMinimum = true;
0393 theResult.Extrema.Append(anExt);
0394 }
0395 }
0396
0397 if (theMode == SearchMode::Max || theMode == SearchMode::MinMax)
0398 {
0399
0400 if (aIsMaxAtUMin && !aIsMinAtUMin && !isDuplicate(theUMin, aPtMin))
0401 {
0402 ExtremumResult anExt;
0403 anExt.Parameter = theUMin;
0404 anExt.Point = aPtMin;
0405 anExt.SquareDistance = aSqDistMin;
0406 anExt.IsMinimum = false;
0407 theResult.Extrema.Append(anExt);
0408 }
0409
0410 if (aIsMaxAtUMax && !aIsMinAtUMax && !aEndpointsAreSame && !isDuplicate(theUMax, aPtMax))
0411 {
0412 ExtremumResult anExt;
0413 anExt.Parameter = theUMax;
0414 anExt.Point = aPtMax;
0415 anExt.SquareDistance = aSqDistMax;
0416 anExt.IsMinimum = false;
0417 theResult.Extrema.Append(anExt);
0418 }
0419 }
0420 }
0421
0422 }
0423
0424 #endif