File indexing completed on 2026-09-28 09:19:58
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014 #ifndef _ExtremaPC_Parabola_HeaderFile
0015 #define _ExtremaPC_Parabola_HeaderFile
0016
0017 #include <ElCLib.hxx>
0018 #include <ExtremaPC.hxx>
0019 #include <gp_Parab.hxx>
0020 #include <gp_Pnt.hxx>
0021 #include <gp_Vec.hxx>
0022 #include <MathPoly_Cubic.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 class ExtremaPC_Parabola
0045 {
0046 public:
0047 DEFINE_STANDARD_ALLOC
0048
0049
0050
0051 explicit ExtremaPC_Parabola(const gp_Parab& theParabola)
0052 : myParabola(theParabola),
0053 myDomain(std::nullopt)
0054 {
0055 cacheGeometry();
0056 }
0057
0058
0059
0060
0061 ExtremaPC_Parabola(const gp_Parab& theParabola, const ExtremaPC::Domain1D& theDomain)
0062 : myParabola(theParabola),
0063 myDomain(theDomain.IsFinite() ? std::optional<ExtremaPC::Domain1D>(theDomain)
0064 : std::nullopt)
0065 {
0066 cacheGeometry();
0067 }
0068
0069
0070 ExtremaPC_Parabola(const ExtremaPC_Parabola&) = delete;
0071
0072
0073 ExtremaPC_Parabola& operator=(const ExtremaPC_Parabola&) = delete;
0074
0075
0076 ExtremaPC_Parabola(ExtremaPC_Parabola&&) = default;
0077
0078
0079 ExtremaPC_Parabola& operator=(ExtremaPC_Parabola&&) = default;
0080
0081
0082
0083
0084 gp_Pnt Value(double theU) const
0085 {
0086
0087 const double aX = theU * theU * my1Over4F;
0088 return gp_Pnt(myVertexX + aX * myXDirX + theU * myYDirX,
0089 myVertexY + aX * myXDirY + theU * myYDirY,
0090 myVertexZ + aX * myXDirZ + theU * myYDirZ);
0091 }
0092
0093
0094 bool IsBounded() const { return myDomain.has_value(); }
0095
0096
0097 const ExtremaPC::Domain1D& Domain() const { return *myDomain; }
0098
0099
0100
0101
0102
0103
0104
0105 [[nodiscard]] const ExtremaPC::Result& Perform(
0106 const gp_Pnt& theP,
0107 double theTol,
0108 ExtremaPC::SearchMode theMode = ExtremaPC::SearchMode::MinMax) const
0109 {
0110 performCore(theP, myDomain, theTol, theMode);
0111 return myResult;
0112 }
0113
0114
0115
0116
0117
0118
0119
0120 [[nodiscard]] const ExtremaPC::Result& PerformWithEndpoints(
0121 const gp_Pnt& theP,
0122 double theTol,
0123 ExtremaPC::SearchMode theMode = ExtremaPC::SearchMode::MinMax) const
0124 {
0125 (void)Perform(theP, theTol, theMode);
0126
0127
0128 if (myResult.Status == ExtremaPC::Status::OK && myDomain.has_value())
0129 {
0130 ExtremaPC::AddEndpointExtrema(myResult, theP, *myDomain, *this, theTol, theMode);
0131 }
0132
0133 return myResult;
0134 }
0135
0136
0137 const gp_Parab& Parabola() const { return myParabola; }
0138
0139 private:
0140
0141 void cacheGeometry()
0142 {
0143 const gp_Pnt& aVertex = myParabola.Location();
0144 myVertexX = aVertex.X();
0145 myVertexY = aVertex.Y();
0146 myVertexZ = aVertex.Z();
0147
0148 const gp_Dir aXDir = myParabola.XAxis().Direction();
0149 myXDirX = aXDir.X();
0150 myXDirY = aXDir.Y();
0151 myXDirZ = aXDir.Z();
0152
0153 const gp_Dir aYDir = myParabola.YAxis().Direction();
0154 myYDirX = aYDir.X();
0155 myYDirY = aYDir.Y();
0156 myYDirZ = aYDir.Z();
0157
0158 const gp_Dir& aAxis = myParabola.Axis().Direction();
0159 myAxisX = aAxis.X();
0160 myAxisY = aAxis.Y();
0161 myAxisZ = aAxis.Z();
0162
0163 myFocal = myParabola.Focal();
0164 my1Over4F = 1.0 / (4.0 * myFocal);
0165 my2F = 2.0 * myFocal;
0166 }
0167
0168
0169 MathPoly::PolyResult solveCubic(const gp_Pnt& theP) const
0170 {
0171
0172 const double aDx = theP.X() - myVertexX;
0173 const double aDy = theP.Y() - myVertexY;
0174 const double aDz = theP.Z() - myVertexZ;
0175
0176
0177
0178 const double aHeight = aDx * myAxisX + aDy * myAxisY + aDz * myAxisZ;
0179
0180
0181 const double aPpX = theP.X() - aHeight * myAxisX;
0182 const double aPpY = theP.Y() - aHeight * myAxisY;
0183 const double aPpZ = theP.Z() - aHeight * myAxisZ;
0184
0185
0186 const double aOPpX = aPpX - myVertexX;
0187 const double aOPpY = aPpY - myVertexY;
0188 const double aOPpZ = aPpZ - myVertexZ;
0189
0190
0191 const double aX = aOPpX * myXDirX + aOPpY * myXDirY + aOPpZ * myXDirZ;
0192 const double aY = aOPpX * myYDirX + aOPpY * myYDirY + aOPpZ * myYDirZ;
0193
0194
0195 return MathPoly::Cubic(my1Over4F, 0.0, my2F - aX, -my2F * aY);
0196 }
0197
0198
0199
0200
0201
0202
0203
0204 void performCore(const gp_Pnt& theP,
0205 const std::optional<ExtremaPC::Domain1D>& theDomain,
0206 double theTol,
0207 ExtremaPC::SearchMode theMode) const
0208 {
0209 (void)theTol;
0210
0211 myResult.Clear();
0212
0213 MathPoly::PolyResult aPolyRes = solveCubic(theP);
0214
0215 if (!aPolyRes.IsDone())
0216 {
0217 if (aPolyRes.Status == MathUtils::Status::InfiniteSolutions)
0218 {
0219 myResult.Status = ExtremaPC::Status::InfiniteSolutions;
0220 gp_Pnt aPtOnCurve = Value(0.0);
0221 myResult.InfiniteSquareDistance = theP.SquareDistance(aPtOnCurve);
0222 }
0223 else
0224 {
0225 myResult.Status = ExtremaPC::Status::NumericalError;
0226 }
0227 return;
0228 }
0229
0230 double aTol2 = Precision::SquareConfusion();
0231
0232
0233 for (size_t i = 0; i < aPolyRes.NbRoots; ++i)
0234 {
0235 double aU = aPolyRes.Roots[i];
0236
0237
0238 if (theDomain.has_value())
0239 {
0240 if (aU < theDomain->Min || aU > theDomain->Max)
0241 continue;
0242 }
0243
0244 gp_Pnt aCurvePt = Value(aU);
0245
0246
0247 bool aDuplicate = false;
0248 for (int j = 0; j < myResult.Extrema.Length(); ++j)
0249 {
0250 if (aCurvePt.SquareDistance(myResult.Extrema.Value(j).Point) < aTol2)
0251 {
0252 aDuplicate = true;
0253 break;
0254 }
0255 }
0256 if (aDuplicate)
0257 continue;
0258
0259
0260 double aSqDist = theP.SquareDistance(aCurvePt);
0261 double aNeighborU;
0262 if (theDomain.has_value())
0263 {
0264
0265 aNeighborU = aU + (aU < (theDomain->Min + theDomain->Max) * 0.5 ? 1.0 : -1.0);
0266 aNeighborU = std::max(theDomain->Min, std::min(theDomain->Max, aNeighborU));
0267 }
0268 else
0269 {
0270
0271 aNeighborU = aU + 1.0;
0272 }
0273 gp_Pnt aNeighborPt = Value(aNeighborU);
0274 double aNeighborDist = theP.SquareDistance(aNeighborPt);
0275 bool aIsMin = aSqDist <= aNeighborDist;
0276
0277
0278 if (theMode == ExtremaPC::SearchMode::Min && !aIsMin)
0279 continue;
0280 if (theMode == ExtremaPC::SearchMode::Max && aIsMin)
0281 continue;
0282
0283 ExtremaPC::ExtremumResult anExt;
0284 anExt.Parameter = aU;
0285 anExt.Point = aCurvePt;
0286 anExt.SquareDistance = aSqDist;
0287 anExt.IsMinimum = aIsMin;
0288
0289 myResult.Extrema.Append(anExt);
0290 }
0291
0292 myResult.Status = ExtremaPC::Status::OK;
0293 }
0294
0295 gp_Parab myParabola;
0296 std::optional<ExtremaPC::Domain1D> myDomain;
0297 mutable ExtremaPC::Result myResult;
0298
0299
0300 double myVertexX, myVertexY, myVertexZ;
0301 double myXDirX, myXDirY, myXDirZ;
0302 double myYDirX, myYDirY, myYDirZ;
0303 double myAxisX, myAxisY, myAxisZ;
0304 double myFocal;
0305 double my1Over4F;
0306 double my2F;
0307 };
0308
0309 #endif