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_Hyperbola_HeaderFile
0015 #define _ExtremaPC_Hyperbola_HeaderFile
0016
0017 #include <ElCLib.hxx>
0018 #include <ExtremaPC.hxx>
0019 #include <gp_Hypr.hxx>
0020 #include <gp_Pnt.hxx>
0021 #include <gp_Vec.hxx>
0022 #include <MathPoly_Quartic.hxx>
0023 #include <Precision.hxx>
0024 #include <Standard_DefineAlloc.hxx>
0025
0026 #include <cmath>
0027 #include <optional>
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045 class ExtremaPC_Hyperbola
0046 {
0047 public:
0048 DEFINE_STANDARD_ALLOC
0049
0050
0051
0052 explicit ExtremaPC_Hyperbola(const gp_Hypr& theHyperbola)
0053 : myHyperbola(theHyperbola),
0054 myDomain(std::nullopt)
0055 {
0056 cacheGeometry();
0057 }
0058
0059
0060
0061
0062 ExtremaPC_Hyperbola(const gp_Hypr& theHyperbola, const ExtremaPC::Domain1D& theDomain)
0063 : myHyperbola(theHyperbola),
0064 myDomain(theDomain.IsFinite() ? std::optional<ExtremaPC::Domain1D>(theDomain)
0065 : std::nullopt)
0066 {
0067 cacheGeometry();
0068 }
0069
0070
0071 ExtremaPC_Hyperbola(const ExtremaPC_Hyperbola&) = delete;
0072
0073
0074 ExtremaPC_Hyperbola& operator=(const ExtremaPC_Hyperbola&) = delete;
0075
0076
0077 ExtremaPC_Hyperbola(ExtremaPC_Hyperbola&&) = default;
0078
0079
0080 ExtremaPC_Hyperbola& operator=(ExtremaPC_Hyperbola&&) = default;
0081
0082
0083
0084
0085 gp_Pnt Value(double theU) const
0086 {
0087
0088 const double aCosh = std::cosh(theU);
0089 const double aSinh = std::sinh(theU);
0090 const double aRCosh = myMajorR * aCosh;
0091 const double arSinh = myMinorR * aSinh;
0092 return gp_Pnt(myCenterX + aRCosh * myXDirX + arSinh * myYDirX,
0093 myCenterY + aRCosh * myXDirY + arSinh * myYDirY,
0094 myCenterZ + aRCosh * myXDirZ + arSinh * myYDirZ);
0095 }
0096
0097
0098 bool IsBounded() const { return myDomain.has_value(); }
0099
0100
0101 const ExtremaPC::Domain1D& Domain() const { return *myDomain; }
0102
0103
0104
0105
0106
0107
0108
0109 [[nodiscard]] const ExtremaPC::Result& Perform(
0110 const gp_Pnt& theP,
0111 double theTol,
0112 ExtremaPC::SearchMode theMode = ExtremaPC::SearchMode::MinMax) const
0113 {
0114 performCore(theP, myDomain, theTol, theMode);
0115 return myResult;
0116 }
0117
0118
0119
0120
0121
0122
0123
0124 [[nodiscard]] const ExtremaPC::Result& PerformWithEndpoints(
0125 const gp_Pnt& theP,
0126 double theTol,
0127 ExtremaPC::SearchMode theMode = ExtremaPC::SearchMode::MinMax) const
0128 {
0129 (void)Perform(theP, theTol, theMode);
0130
0131
0132 if (myResult.Status == ExtremaPC::Status::OK && myDomain.has_value())
0133 {
0134 ExtremaPC::AddEndpointExtrema(myResult, theP, *myDomain, *this, theTol, theMode);
0135 }
0136
0137 return myResult;
0138 }
0139
0140
0141 const gp_Hypr& Hyperbola() const { return myHyperbola; }
0142
0143 private:
0144
0145 void cacheGeometry()
0146 {
0147 const gp_Pnt& aCenter = myHyperbola.Location();
0148 myCenterX = aCenter.X();
0149 myCenterY = aCenter.Y();
0150 myCenterZ = aCenter.Z();
0151
0152 const gp_Dir aXDir = myHyperbola.XAxis().Direction();
0153 myXDirX = aXDir.X();
0154 myXDirY = aXDir.Y();
0155 myXDirZ = aXDir.Z();
0156
0157 const gp_Dir aYDir = myHyperbola.YAxis().Direction();
0158 myYDirX = aYDir.X();
0159 myYDirY = aYDir.Y();
0160 myYDirZ = aYDir.Z();
0161
0162 const gp_Dir& aAxis = myHyperbola.Axis().Direction();
0163 myAxisX = aAxis.X();
0164 myAxisY = aAxis.Y();
0165 myAxisZ = aAxis.Z();
0166
0167 myMajorR = myHyperbola.MajorRadius();
0168 myMinorR = myHyperbola.MinorRadius();
0169 myR2PlusR2Over4 = (myMajorR * myMajorR + myMinorR * myMinorR) / 4.0;
0170 }
0171
0172
0173 MathPoly::PolyResult solveQuartic(const gp_Pnt& theP) const
0174 {
0175
0176 const double aDx = theP.X() - myCenterX;
0177 const double aDy = theP.Y() - myCenterY;
0178 const double aDz = theP.Z() - myCenterZ;
0179
0180
0181
0182 const double aHeight = aDx * myAxisX + aDy * myAxisY + aDz * myAxisZ;
0183
0184
0185 const double aPpX = theP.X() - aHeight * myAxisX;
0186 const double aPpY = theP.Y() - aHeight * myAxisY;
0187 const double aPpZ = theP.Z() - aHeight * myAxisZ;
0188
0189
0190 const double aOPpX = aPpX - myCenterX;
0191 const double aOPpY = aPpY - myCenterY;
0192 const double aOPpZ = aPpZ - myCenterZ;
0193
0194
0195 const double aX = aOPpX * myXDirX + aOPpY * myXDirY + aOPpZ * myXDirZ;
0196 const double aY = aOPpX * myYDirX + aOPpY * myYDirY + aOPpZ * myYDirZ;
0197
0198
0199 const double aC1 = myR2PlusR2Over4;
0200 const double aC2 = -(aX * myMajorR + aY * myMinorR) / 2.0;
0201 const double aC3 = 0.0;
0202 const double aC4 = (aX * myMajorR - aY * myMinorR) / 2.0;
0203 const double aC5 = -myR2PlusR2Over4;
0204
0205 return MathPoly::Quartic(aC1, aC2, aC3, aC4, aC5);
0206 }
0207
0208
0209
0210
0211
0212
0213
0214 void performCore(const gp_Pnt& theP,
0215 const std::optional<ExtremaPC::Domain1D>& theDomain,
0216 double theTol,
0217 ExtremaPC::SearchMode theMode) const
0218 {
0219 myResult.Clear();
0220
0221 MathPoly::PolyResult aPolyRes = solveQuartic(theP);
0222
0223 if (!aPolyRes.IsDone())
0224 {
0225 if (aPolyRes.Status == MathUtils::Status::InfiniteSolutions)
0226 {
0227 myResult.Status = ExtremaPC::Status::InfiniteSolutions;
0228 gp_Pnt aPtOnCurve = Value(0.0);
0229 myResult.InfiniteSquareDistance = theP.SquareDistance(aPtOnCurve);
0230 }
0231 else
0232 {
0233 myResult.Status = ExtremaPC::Status::NumericalError;
0234 }
0235 return;
0236 }
0237
0238 double aTol2 = theTol * theTol;
0239
0240
0241 for (size_t i = 0; i < aPolyRes.NbRoots; ++i)
0242 {
0243 double aV = aPolyRes.Roots[i];
0244 if (aV <= 0.0)
0245 continue;
0246
0247 double aU = std::log(aV);
0248
0249
0250 if (theDomain.has_value())
0251 {
0252 if (aU < theDomain->Min || aU > theDomain->Max)
0253 continue;
0254 }
0255
0256 gp_Pnt aCurvePt = Value(aU);
0257
0258
0259 bool aDuplicate = false;
0260 for (int j = 0; j < myResult.Extrema.Length(); ++j)
0261 {
0262 if (aCurvePt.SquareDistance(myResult.Extrema.Value(j).Point) < aTol2)
0263 {
0264 aDuplicate = true;
0265 break;
0266 }
0267 }
0268 if (aDuplicate)
0269 continue;
0270
0271
0272 double aSqDist = theP.SquareDistance(aCurvePt);
0273 double aNeighborU;
0274 if (theDomain.has_value())
0275 {
0276
0277 aNeighborU = aU + (aU < (theDomain->Min + theDomain->Max) * 0.5 ? 1.0 : -1.0);
0278 aNeighborU = std::max(theDomain->Min, std::min(theDomain->Max, aNeighborU));
0279 }
0280 else
0281 {
0282
0283 aNeighborU = aU + 1.0;
0284 }
0285 gp_Pnt aNeighborPt = Value(aNeighborU);
0286 double aNeighborDist = theP.SquareDistance(aNeighborPt);
0287 bool aIsMin = aSqDist < aNeighborDist;
0288
0289
0290 if (theMode == ExtremaPC::SearchMode::Min && !aIsMin)
0291 continue;
0292 if (theMode == ExtremaPC::SearchMode::Max && aIsMin)
0293 continue;
0294
0295 ExtremaPC::ExtremumResult anExt;
0296 anExt.Parameter = aU;
0297 anExt.Point = aCurvePt;
0298 anExt.SquareDistance = aSqDist;
0299 anExt.IsMinimum = aIsMin;
0300
0301 myResult.Extrema.Append(anExt);
0302 }
0303
0304 myResult.Status = ExtremaPC::Status::OK;
0305 }
0306
0307 gp_Hypr myHyperbola;
0308 std::optional<ExtremaPC::Domain1D> myDomain;
0309 mutable ExtremaPC::Result myResult;
0310
0311
0312 double myCenterX, myCenterY, myCenterZ;
0313 double myXDirX, myXDirY, myXDirZ;
0314 double myYDirX, myYDirY, myYDirZ;
0315 double myAxisX, myAxisY, myAxisZ;
0316 double myMajorR;
0317 double myMinorR;
0318 double myR2PlusR2Over4;
0319 };
0320
0321 #endif