File indexing completed on 2026-09-28 09:20:53
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014 #ifndef _MathSys_Newton4D_HeaderFile
0015 #define _MathSys_Newton4D_HeaderFile
0016
0017 #include <MathSys_NewtonTypes.hxx>
0018
0019 #include <gp_Pnt.hxx>
0020 #include <gp_Vec.hxx>
0021
0022 #include <algorithm>
0023 #include <array>
0024 #include <cmath>
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041 namespace MathSys
0042 {
0043 namespace detail
0044 {
0045
0046
0047 inline bool IsBoundsValid4D(const NewtonBoundsN<4>& theBounds)
0048 {
0049 if (!theBounds.HasBounds)
0050 {
0051 return true;
0052 }
0053
0054 return theBounds.Min[0] <= theBounds.Max[0] && theBounds.Min[1] <= theBounds.Max[1]
0055 && theBounds.Min[2] <= theBounds.Max[2] && theBounds.Min[3] <= theBounds.Max[3];
0056 }
0057
0058
0059 inline bool IsOptionsValid4D(const NewtonOptions& theOptions)
0060 {
0061 return theOptions.FTolerance > 0.0 && theOptions.XTolerance > 0.0 && theOptions.MaxIterations > 0
0062 && theOptions.MaxStepRatio > 0.0 && theOptions.SoftBoundsExtension >= 0.0;
0063 }
0064
0065
0066 inline double MaxDomainSize4D(const NewtonBoundsN<4>& theBounds)
0067 {
0068 if (!theBounds.HasBounds)
0069 {
0070 return 1.0;
0071 }
0072
0073 const double aD0 = theBounds.Max[0] - theBounds.Min[0];
0074 const double aD1 = theBounds.Max[1] - theBounds.Min[1];
0075 const double aD2 = theBounds.Max[2] - theBounds.Min[2];
0076 const double aD3 = theBounds.Max[3] - theBounds.Min[3];
0077 return std::max(1.0, std::max(aD0, std::max(aD1, std::max(aD2, aD3))));
0078 }
0079
0080
0081 inline void Clamp4D(std::array<double, 4>& theX,
0082 const NewtonBoundsN<4>& theBounds,
0083 bool theUseSoftBounds,
0084 double theSoftExtRatio)
0085 {
0086 if (!theBounds.HasBounds)
0087 {
0088 return;
0089 }
0090
0091 for (int i = 0; i < 4; ++i)
0092 {
0093 const double aExt =
0094 theUseSoftBounds ? (theBounds.Max[i] - theBounds.Min[i]) * theSoftExtRatio : 0.0;
0095 theX[i] = std::clamp(theX[i], theBounds.Min[i] - aExt, theBounds.Max[i] + aExt);
0096 }
0097 }
0098
0099
0100
0101
0102
0103
0104 inline bool Solve4x4(const double theJ[4][4], const double theF[4], double theDelta[4])
0105 {
0106
0107 double A[4][5];
0108 for (int i = 0; i < 4; ++i)
0109 {
0110 for (int j = 0; j < 4; ++j)
0111 {
0112 A[i][j] = theJ[i][j];
0113 }
0114 A[i][4] = -theF[i];
0115 }
0116
0117
0118 for (int k = 0; k < 4; ++k)
0119 {
0120
0121 int aMaxRow = k;
0122 double aMaxVal = std::abs(A[k][k]);
0123 for (int i = k + 1; i < 4; ++i)
0124 {
0125 const double aVal = std::abs(A[i][k]);
0126 if (aVal > aMaxVal)
0127 {
0128 aMaxVal = aVal;
0129 aMaxRow = i;
0130 }
0131 }
0132
0133
0134 if (aMaxVal < 1.0e-30)
0135 {
0136 return false;
0137 }
0138
0139
0140 if (aMaxRow != k)
0141 {
0142 for (int j = k; j <= 4; ++j)
0143 {
0144 std::swap(A[k][j], A[aMaxRow][j]);
0145 }
0146 }
0147
0148
0149 const double aInvPivot = 1.0 / A[k][k];
0150 for (int i = k + 1; i < 4; ++i)
0151 {
0152 const double aFactor = A[i][k] * aInvPivot;
0153 for (int j = k + 1; j <= 4; ++j)
0154 {
0155 A[i][j] -= aFactor * A[k][j];
0156 }
0157 A[i][k] = 0.0;
0158 }
0159 }
0160
0161
0162 for (int i = 3; i >= 0; --i)
0163 {
0164 double aSum = A[i][4];
0165 for (int j = i + 1; j < 4; ++j)
0166 {
0167 aSum -= A[i][j] * theDelta[j];
0168 }
0169 theDelta[i] = aSum / A[i][i];
0170 }
0171
0172 return true;
0173 }
0174
0175 }
0176
0177
0178
0179
0180
0181
0182
0183
0184
0185
0186
0187
0188
0189
0190
0191
0192
0193
0194
0195 template <typename Function>
0196 NewtonResultN<4> Solve4D(const Function& theFunc,
0197 const std::array<double, 4>& theX0,
0198 const NewtonBoundsN<4>& theBounds,
0199 const NewtonOptions& theOptions = NewtonOptions())
0200 {
0201 NewtonResultN<4> aRes;
0202 aRes.X = theX0;
0203
0204 if (!detail::IsOptionsValid4D(theOptions) || !detail::IsBoundsValid4D(theBounds))
0205 {
0206 aRes.Status = MathUtils::Status::InvalidInput;
0207 return aRes;
0208 }
0209
0210 detail::Clamp4D(aRes.X, theBounds, theOptions.AllowSoftBounds, theOptions.SoftBoundsExtension);
0211
0212 const double aTolSq = theOptions.FTolerance * theOptions.FTolerance;
0213 const double aMaxStep = theOptions.MaxStepRatio * detail::MaxDomainSize4D(theBounds);
0214
0215 for (int anIter = 0; anIter < theOptions.MaxIterations; ++anIter)
0216 {
0217 aRes.NbIterations = static_cast<size_t>(anIter + 1);
0218
0219 double aF[4];
0220 double aJ[4][4];
0221 if (!theFunc(aRes.X[0], aRes.X[1], aRes.X[2], aRes.X[3], aF, aJ))
0222 {
0223 aRes.Status = MathUtils::Status::NumericalError;
0224 return aRes;
0225 }
0226
0227
0228 const double aFNormSq = aF[0] * aF[0] + aF[1] * aF[1] + aF[2] * aF[2] + aF[3] * aF[3];
0229 aRes.ResidualNorm = std::sqrt(aFNormSq);
0230 if (aFNormSq <= aTolSq)
0231 {
0232 aRes.Status = MathUtils::Status::OK;
0233 return aRes;
0234 }
0235
0236
0237 double aDelta[4];
0238 if (!detail::Solve4x4(aJ, aF, aDelta))
0239 {
0240
0241 double aGrad[4] = {0.0, 0.0, 0.0, 0.0};
0242 for (int c = 0; c < 4; ++c)
0243 {
0244 for (int r = 0; r < 4; ++r)
0245 {
0246 aGrad[c] += aJ[r][c] * aF[r];
0247 }
0248 }
0249
0250 const double aGradSq =
0251 aGrad[0] * aGrad[0] + aGrad[1] * aGrad[1] + aGrad[2] * aGrad[2] + aGrad[3] * aGrad[3];
0252 if (aGradSq < 1.0e-60)
0253 {
0254 aRes.Status = MathUtils::Status::Singular;
0255 return aRes;
0256 }
0257
0258 const double aAlpha = std::min(1.0, std::sqrt(aFNormSq / aGradSq) * 0.1);
0259 for (int i = 0; i < 4; ++i)
0260 {
0261 aDelta[i] = -aAlpha * aGrad[i];
0262 }
0263 }
0264
0265
0266 const double aStepNormSq =
0267 aDelta[0] * aDelta[0] + aDelta[1] * aDelta[1] + aDelta[2] * aDelta[2] + aDelta[3] * aDelta[3];
0268 const double aStepNorm = std::sqrt(aStepNormSq);
0269 if (aStepNorm > aMaxStep)
0270 {
0271 const double aScale = aMaxStep / aStepNorm;
0272 for (int i = 0; i < 4; ++i)
0273 {
0274 aDelta[i] *= aScale;
0275 }
0276 }
0277
0278
0279 std::array<double, 4> aNewX = {aRes.X[0] + aDelta[0],
0280 aRes.X[1] + aDelta[1],
0281 aRes.X[2] + aDelta[2],
0282 aRes.X[3] + aDelta[3]};
0283 detail::Clamp4D(aNewX, theBounds, theOptions.AllowSoftBounds, theOptions.SoftBoundsExtension);
0284
0285 aRes.StepNorm = std::sqrt((aNewX[0] - aRes.X[0]) * (aNewX[0] - aRes.X[0])
0286 + (aNewX[1] - aRes.X[1]) * (aNewX[1] - aRes.X[1])
0287 + (aNewX[2] - aRes.X[2]) * (aNewX[2] - aRes.X[2])
0288 + (aNewX[3] - aRes.X[3]) * (aNewX[3] - aRes.X[3]));
0289 aRes.X = aNewX;
0290
0291 const double aScaleRef = std::max(
0292 1.0,
0293 std::max(std::abs(aRes.X[0]),
0294 std::max(std::abs(aRes.X[1]), std::max(std::abs(aRes.X[2]), std::abs(aRes.X[3])))));
0295 if (aRes.StepNorm <= theOptions.XTolerance * aScaleRef)
0296 {
0297 double aCheckF[4];
0298 double aCheckJ[4][4];
0299 if (!theFunc(aRes.X[0], aRes.X[1], aRes.X[2], aRes.X[3], aCheckF, aCheckJ))
0300 {
0301 aRes.Status = MathUtils::Status::NumericalError;
0302 return aRes;
0303 }
0304
0305 aRes.ResidualNorm = std::sqrt(aCheckF[0] * aCheckF[0] + aCheckF[1] * aCheckF[1]
0306 + aCheckF[2] * aCheckF[2] + aCheckF[3] * aCheckF[3]);
0307 aRes.Status = (aRes.ResidualNorm <= theOptions.FTolerance) ? MathUtils::Status::OK
0308 : MathUtils::Status::MaxIterations;
0309 return aRes;
0310 }
0311 }
0312
0313
0314 double aF[4];
0315 double aJ[4][4];
0316 if (!theFunc(aRes.X[0], aRes.X[1], aRes.X[2], aRes.X[3], aF, aJ))
0317 {
0318 aRes.Status = MathUtils::Status::NumericalError;
0319 return aRes;
0320 }
0321
0322 aRes.ResidualNorm = std::sqrt(aF[0] * aF[0] + aF[1] * aF[1] + aF[2] * aF[2] + aF[3] * aF[3]);
0323 aRes.Status = (aRes.ResidualNorm <= theOptions.FTolerance) ? MathUtils::Status::OK
0324 : MathUtils::Status::MaxIterations;
0325 return aRes;
0326 }
0327
0328
0329
0330
0331
0332
0333
0334
0335
0336
0337
0338
0339
0340
0341
0342
0343
0344
0345
0346
0347 template <typename SurfaceEvaluator1, typename SurfaceEvaluator2>
0348 NewtonResultN<4> SolveSurfaceSurfaceExtrema4D(const SurfaceEvaluator1& theSurf1,
0349 const SurfaceEvaluator2& theSurf2,
0350 const std::array<double, 4>& theX0,
0351 const NewtonBoundsN<4>& theBounds,
0352 const NewtonOptions& theOptions = NewtonOptions())
0353 {
0354 auto aFunc = [&theSurf1, &theSurf2](double theU1,
0355 double theV1,
0356 double theU2,
0357 double theV2,
0358 double theF[4],
0359 double theJ[4][4]) -> bool {
0360 gp_Pnt aPt1, aPt2;
0361 gp_Vec aD1U1, aD1V1, aD2UU1, aD2VV1, aD2UV1;
0362 gp_Vec aD1U2, aD1V2, aD2UU2, aD2VV2, aD2UV2;
0363
0364 theSurf1.D2(theU1, theV1, aPt1, aD1U1, aD1V1, aD2UU1, aD2VV1, aD2UV1);
0365 theSurf2.D2(theU2, theV2, aPt2, aD1U2, aD1V2, aD2UU2, aD2VV2, aD2UV2);
0366
0367
0368 const gp_Vec aD(aPt2, aPt1);
0369
0370
0371 theF[0] = aD.Dot(aD1U1);
0372 theF[1] = aD.Dot(aD1V1);
0373 theF[2] = -aD.Dot(aD1U2);
0374 theF[3] = -aD.Dot(aD1V2);
0375
0376
0377
0378 theJ[0][0] = aD1U1.Dot(aD1U1) + aD.Dot(aD2UU1);
0379 theJ[0][1] = aD1V1.Dot(aD1U1) + aD.Dot(aD2UV1);
0380 theJ[1][0] = theJ[0][1];
0381 theJ[1][1] = aD1V1.Dot(aD1V1) + aD.Dot(aD2VV1);
0382
0383
0384 theJ[0][2] = -aD1U2.Dot(aD1U1);
0385 theJ[0][3] = -aD1V2.Dot(aD1U1);
0386 theJ[1][2] = -aD1U2.Dot(aD1V1);
0387 theJ[1][3] = -aD1V2.Dot(aD1V1);
0388
0389
0390 theJ[2][0] = theJ[0][2];
0391 theJ[2][1] = theJ[1][2];
0392 theJ[3][0] = theJ[0][3];
0393 theJ[3][1] = theJ[1][3];
0394
0395
0396 theJ[2][2] = aD1U2.Dot(aD1U2) - aD.Dot(aD2UU2);
0397 theJ[2][3] = aD1V2.Dot(aD1U2) - aD.Dot(aD2UV2);
0398 theJ[3][2] = theJ[2][3];
0399 theJ[3][3] = aD1V2.Dot(aD1V2) - aD.Dot(aD2VV2);
0400 return true;
0401 };
0402
0403 return Solve4D(aFunc, theX0, theBounds, theOptions);
0404 }
0405
0406 }
0407
0408 #endif