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_Newton3D_HeaderFile
0015 #define _MathSys_Newton3D_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 IsBoundsValid3D(const NewtonBoundsN<3>& 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];
0056 }
0057
0058
0059 inline bool IsOptionsValid3D(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 MaxDomainSize3D(const NewtonBoundsN<3>& theBounds)
0067 {
0068 if (!theBounds.HasBounds)
0069 {
0070 return 1.0;
0071 }
0072
0073 const double aDX = theBounds.Max[0] - theBounds.Min[0];
0074 const double aDY = theBounds.Max[1] - theBounds.Min[1];
0075 const double aDZ = theBounds.Max[2] - theBounds.Min[2];
0076 return std::max(1.0, std::max(aDX, std::max(aDY, aDZ)));
0077 }
0078
0079
0080 inline void Clamp3D(std::array<double, 3>& theX,
0081 const NewtonBoundsN<3>& theBounds,
0082 bool theUseSoftBounds,
0083 double theSoftExtRatio)
0084 {
0085 if (!theBounds.HasBounds)
0086 {
0087 return;
0088 }
0089
0090 const double aExtX =
0091 theUseSoftBounds ? (theBounds.Max[0] - theBounds.Min[0]) * theSoftExtRatio : 0.0;
0092 const double aExtY =
0093 theUseSoftBounds ? (theBounds.Max[1] - theBounds.Min[1]) * theSoftExtRatio : 0.0;
0094 const double aExtZ =
0095 theUseSoftBounds ? (theBounds.Max[2] - theBounds.Min[2]) * theSoftExtRatio : 0.0;
0096
0097 theX[0] = std::clamp(theX[0], theBounds.Min[0] - aExtX, theBounds.Max[0] + aExtX);
0098 theX[1] = std::clamp(theX[1], theBounds.Min[1] - aExtY, theBounds.Max[1] + aExtY);
0099 theX[2] = std::clamp(theX[2], theBounds.Min[2] - aExtZ, theBounds.Max[2] + aExtZ);
0100 }
0101
0102
0103
0104
0105
0106
0107 inline bool Solve3x3(const double theJ[3][3], const double theF[3], double theDelta[3])
0108 {
0109
0110 const double aCof00 = theJ[1][1] * theJ[2][2] - theJ[1][2] * theJ[2][1];
0111 const double aCof01 = theJ[1][2] * theJ[2][0] - theJ[1][0] * theJ[2][2];
0112 const double aCof02 = theJ[1][0] * theJ[2][1] - theJ[1][1] * theJ[2][0];
0113
0114
0115 const double aDet = theJ[0][0] * aCof00 + theJ[0][1] * aCof01 + theJ[0][2] * aCof02;
0116
0117 if (std::abs(aDet) < 1.0e-30)
0118 {
0119 return false;
0120 }
0121
0122 const double aInvDet = 1.0 / aDet;
0123
0124
0125 const double aCof10 = theJ[0][2] * theJ[2][1] - theJ[0][1] * theJ[2][2];
0126 const double aCof11 = theJ[0][0] * theJ[2][2] - theJ[0][2] * theJ[2][0];
0127 const double aCof12 = theJ[0][1] * theJ[2][0] - theJ[0][0] * theJ[2][1];
0128
0129 const double aCof20 = theJ[0][1] * theJ[1][2] - theJ[0][2] * theJ[1][1];
0130 const double aCof21 = theJ[0][2] * theJ[1][0] - theJ[0][0] * theJ[1][2];
0131 const double aCof22 = theJ[0][0] * theJ[1][1] - theJ[0][1] * theJ[1][0];
0132
0133
0134 theDelta[0] = -(aCof00 * theF[0] + aCof10 * theF[1] + aCof20 * theF[2]) * aInvDet;
0135 theDelta[1] = -(aCof01 * theF[0] + aCof11 * theF[1] + aCof21 * theF[2]) * aInvDet;
0136 theDelta[2] = -(aCof02 * theF[0] + aCof12 * theF[1] + aCof22 * theF[2]) * aInvDet;
0137 return true;
0138 }
0139
0140 }
0141
0142
0143
0144
0145
0146
0147
0148
0149
0150
0151
0152
0153
0154
0155
0156
0157
0158
0159
0160 template <typename Function>
0161 NewtonResultN<3> Solve3D(const Function& theFunc,
0162 const std::array<double, 3>& theX0,
0163 const NewtonBoundsN<3>& theBounds,
0164 const NewtonOptions& theOptions = NewtonOptions())
0165 {
0166 NewtonResultN<3> aRes;
0167 aRes.X = theX0;
0168
0169 if (!detail::IsOptionsValid3D(theOptions) || !detail::IsBoundsValid3D(theBounds))
0170 {
0171 aRes.Status = MathUtils::Status::InvalidInput;
0172 return aRes;
0173 }
0174
0175 detail::Clamp3D(aRes.X, theBounds, theOptions.AllowSoftBounds, theOptions.SoftBoundsExtension);
0176
0177 const double aTolSq = theOptions.FTolerance * theOptions.FTolerance;
0178 const double aMaxStep = theOptions.MaxStepRatio * detail::MaxDomainSize3D(theBounds);
0179
0180 for (int anIter = 0; anIter < theOptions.MaxIterations; ++anIter)
0181 {
0182 aRes.NbIterations = static_cast<size_t>(anIter + 1);
0183
0184 double aF[3];
0185 double aJ[3][3];
0186 if (!theFunc(aRes.X[0], aRes.X[1], aRes.X[2], aF, aJ))
0187 {
0188 aRes.Status = MathUtils::Status::NumericalError;
0189 return aRes;
0190 }
0191
0192
0193 const double aFNormSq = aF[0] * aF[0] + aF[1] * aF[1] + aF[2] * aF[2];
0194 aRes.ResidualNorm = std::sqrt(aFNormSq);
0195 if (aFNormSq <= aTolSq)
0196 {
0197 aRes.Status = MathUtils::Status::OK;
0198 return aRes;
0199 }
0200
0201
0202 double aDelta[3];
0203 if (!detail::Solve3x3(aJ, aF, aDelta))
0204 {
0205
0206 const double aGradX = aJ[0][0] * aF[0] + aJ[1][0] * aF[1] + aJ[2][0] * aF[2];
0207 const double aGradY = aJ[0][1] * aF[0] + aJ[1][1] * aF[1] + aJ[2][1] * aF[2];
0208 const double aGradZ = aJ[0][2] * aF[0] + aJ[1][2] * aF[1] + aJ[2][2] * aF[2];
0209 const double aGradSq = aGradX * aGradX + aGradY * aGradY + aGradZ * aGradZ;
0210 if (aGradSq < 1.0e-60)
0211 {
0212 aRes.Status = MathUtils::Status::Singular;
0213 return aRes;
0214 }
0215
0216 const double aAlpha = std::min(1.0, std::sqrt(aFNormSq / aGradSq) * 0.1);
0217 aDelta[0] = -aAlpha * aGradX;
0218 aDelta[1] = -aAlpha * aGradY;
0219 aDelta[2] = -aAlpha * aGradZ;
0220 }
0221
0222
0223 const double aStepNormSq =
0224 aDelta[0] * aDelta[0] + aDelta[1] * aDelta[1] + aDelta[2] * aDelta[2];
0225 const double aStepNorm = std::sqrt(aStepNormSq);
0226 if (aStepNorm > aMaxStep)
0227 {
0228 const double aScale = aMaxStep / aStepNorm;
0229 aDelta[0] *= aScale;
0230 aDelta[1] *= aScale;
0231 aDelta[2] *= aScale;
0232 }
0233
0234
0235 std::array<double, 3> aNewX = {aRes.X[0] + aDelta[0],
0236 aRes.X[1] + aDelta[1],
0237 aRes.X[2] + aDelta[2]};
0238 detail::Clamp3D(aNewX, theBounds, theOptions.AllowSoftBounds, theOptions.SoftBoundsExtension);
0239
0240 aRes.StepNorm = std::sqrt((aNewX[0] - aRes.X[0]) * (aNewX[0] - aRes.X[0])
0241 + (aNewX[1] - aRes.X[1]) * (aNewX[1] - aRes.X[1])
0242 + (aNewX[2] - aRes.X[2]) * (aNewX[2] - aRes.X[2]));
0243 aRes.X = aNewX;
0244
0245 const double aScaleRef =
0246 std::max(1.0,
0247 std::max(std::abs(aRes.X[0]), std::max(std::abs(aRes.X[1]), std::abs(aRes.X[2]))));
0248 if (aRes.StepNorm <= theOptions.XTolerance * aScaleRef)
0249 {
0250 double aCheckF[3];
0251 double aCheckJ[3][3];
0252 if (!theFunc(aRes.X[0], aRes.X[1], aRes.X[2], aCheckF, aCheckJ))
0253 {
0254 aRes.Status = MathUtils::Status::NumericalError;
0255 return aRes;
0256 }
0257
0258 aRes.ResidualNorm =
0259 std::sqrt(aCheckF[0] * aCheckF[0] + aCheckF[1] * aCheckF[1] + aCheckF[2] * aCheckF[2]);
0260 aRes.Status = (aRes.ResidualNorm <= theOptions.FTolerance) ? MathUtils::Status::OK
0261 : MathUtils::Status::MaxIterations;
0262 return aRes;
0263 }
0264 }
0265
0266
0267 double aF[3];
0268 double aJ[3][3];
0269 if (!theFunc(aRes.X[0], aRes.X[1], aRes.X[2], aF, aJ))
0270 {
0271 aRes.Status = MathUtils::Status::NumericalError;
0272 return aRes;
0273 }
0274
0275 aRes.ResidualNorm = std::sqrt(aF[0] * aF[0] + aF[1] * aF[1] + aF[2] * aF[2]);
0276 aRes.Status = (aRes.ResidualNorm <= theOptions.FTolerance) ? MathUtils::Status::OK
0277 : MathUtils::Status::MaxIterations;
0278 return aRes;
0279 }
0280
0281
0282
0283
0284
0285
0286
0287
0288
0289
0290
0291
0292
0293
0294
0295
0296
0297 template <typename CurveEvaluator, typename SurfaceEvaluator>
0298 NewtonResultN<3> SolveCurveSurfaceExtrema3D(const CurveEvaluator& theCurve,
0299 const SurfaceEvaluator& theSurface,
0300 const std::array<double, 3>& theX0,
0301 const NewtonBoundsN<3>& theBounds,
0302 const NewtonOptions& theOptions = NewtonOptions())
0303 {
0304 auto aFunc = [&theCurve, &theSurface](double theT,
0305 double theU,
0306 double theV,
0307 double theF[3],
0308 double theJ[3][3]) -> bool {
0309 gp_Pnt aPtC, aPtS;
0310 gp_Vec aD1C, aD2C;
0311 gp_Vec aD1U, aD1V, aD2UU, aD2VV, aD2UV;
0312
0313 theCurve.D2(theT, aPtC, aD1C, aD2C);
0314 theSurface.D2(theU, theV, aPtS, aD1U, aD1V, aD2UU, aD2VV, aD2UV);
0315
0316
0317 const gp_Vec aD(aPtS, aPtC);
0318
0319
0320 theF[0] = aD.Dot(aD1C);
0321 theF[1] = -aD.Dot(aD1U);
0322 theF[2] = -aD.Dot(aD1V);
0323
0324
0325
0326 theJ[0][0] = aD1C.Dot(aD1C) + aD.Dot(aD2C);
0327
0328 theJ[0][1] = -aD1U.Dot(aD1C);
0329
0330 theJ[0][2] = -aD1V.Dot(aD1C);
0331
0332
0333 theJ[1][0] = theJ[0][1];
0334
0335 theJ[1][1] = aD1U.Dot(aD1U) - aD.Dot(aD2UU);
0336
0337 theJ[1][2] = aD1V.Dot(aD1U) - aD.Dot(aD2UV);
0338
0339
0340 theJ[2][0] = theJ[0][2];
0341
0342 theJ[2][1] = theJ[1][2];
0343
0344 theJ[2][2] = aD1V.Dot(aD1V) - aD.Dot(aD2VV);
0345 return true;
0346 };
0347
0348 return Solve3D(aFunc, theX0, theBounds, theOptions);
0349 }
0350
0351 }
0352
0353 #endif