Loading files
3d097a032This file is pinned to the selected release. Download the original to open it in your design tool.
// Created on: 1991-02-26
// Created by: Isabelle GRIGNON
// Copyright (c) 1991-1999 Matra Datavision
// Copyright (c) 1999-2014 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#ifndef _Extrema_GFuncExtPC_HeaderFile
#define _Extrema_GFuncExtPC_HeaderFile
#include <Standard.hxx>
#include <Standard_DefineAlloc.hxx>
#include <GeomAbs_CurveType.hxx>
#include <math_FunctionWithDerivative.hxx>
#include <Precision.hxx>
#include <Standard_TypeMismatch.hxx>
#include <Standard_Integer.hxx>
#include <NCollection_Sequence.hxx>
#include <cmath>
//! Template class for computing extremal distance function between a point and a curve.
//! Searches for a parameter value u such that dist(P, C(u)) passes through an extremum.
//! Inherits from math_FunctionWithDerivative and is used by math_FunctionRoot and
//! math_FunctionRoots algorithms.
//!
//! If D1c and D2c are the first and second derivatives:
//! F(u) = (C(u)-P).D1c(u) / ||D1c||
//! DF(u) = ||D1c|| + (C(u)-P).D2c(u)/||D1c|| - F(u)*D2c.D1c/||D1c||^2
//!
//! @tparam TheCurve Curve type (e.g., Adaptor3d_Curve, Adaptor2d_Curve2d)
//! @tparam TheCurveTool Tool for curve operations
//! @tparam ThePOnC Point on curve type
//! @tparam ThePoint Point type (e.g., gp_Pnt, gp_Pnt2d)
//! @tparam TheVector Vector type (e.g., gp_Vec, gp_Vec2d)
//! @tparam TheSeqPOnC Sequence of points on curve type
template <typename TheCurve,
typename TheCurveTool,
typename ThePOnC,
typename ThePoint,
typename TheVector,
typename TheSeqPOnC>
class Extrema_GFuncExtPC : public math_FunctionWithDerivative
{
public:
DEFINE_STANDARD_ALLOC
//! Default constructor.
Extrema_GFuncExtPC()
: myC(nullptr),
myU(0.0),
myD1f(0.0),
myPinit(false),
myCinit(false),
myD1Init(false),
myTol(MinTol),
myMaxDerivOrder(0),
myUinfium(0.0),
myUsupremum(0.0)
{
}
//! Constructor with point and curve initialization.
//! @param theP Point to compute distance from
//! @param theC Curve to compute distance to
Extrema_GFuncExtPC(const ThePoint& theP, const TheCurve& theC)
: myP(theP),
myC(const_cast<TheCurve*>(&theC)),
myU(0.0),
myD1f(0.0),
myPinit(true),
myCinit(true),
myD1Init(false)
{
SubIntervalInitialize(TheCurveTool::FirstParameter(theC), TheCurveTool::LastParameter(theC));
switch (TheCurveTool::GetType(theC))
{
case GeomAbs_BezierCurve:
case GeomAbs_BSplineCurve:
case GeomAbs_OffsetCurve:
case GeomAbs_OtherCurve:
myMaxDerivOrder = MaxOrder;
myTol = SearchOfTolerance();
break;
default:
myMaxDerivOrder = 0;
myTol = MinTol;
break;
}
}
//! Sets the curve field.
//! @param theC Curve to set
void Initialize(const TheCurve& theC)
{
myC = const_cast<TheCurve*>(&theC);
myCinit = true;
myPoint.Clear();
mySqDist.Clear();
myIsMin.Clear();
SubIntervalInitialize(TheCurveTool::FirstParameter(theC), TheCurveTool::LastParameter(theC));
switch (TheCurveTool::GetType(theC))
{
case GeomAbs_BezierCurve:
case GeomAbs_BSplineCurve:
case GeomAbs_OffsetCurve:
case GeomAbs_OtherCurve:
myMaxDerivOrder = MaxOrder;
myTol = SearchOfTolerance();
break;
default:
myMaxDerivOrder = 0;
myTol = MinTol;
break;
}
}
//! Sets the point field.
//! @param theP Point to set
void SetPoint(const ThePoint& theP)
{
myP = theP;
myPinit = true;
myPoint.Clear();
mySqDist.Clear();
myIsMin.Clear();
}
//! Calculation of F(u).
//! @param theU Parameter value
//! @param theF Output function value
//! @return True if computation succeeded
bool Value(const double theU, double& theF) override
{
if (!myPinit || !myCinit)
{
throw Standard_TypeMismatch("No init");
}
myU = theU;
TheVector D1c;
TheCurveTool::D1(*myC, myU, myPc, D1c);
if (Precision::IsInfinite(D1c.X()) || Precision::IsInfinite(D1c.Y()))
{
theF = Precision::Infinite();
return false;
}
double Ndu = D1c.Magnitude();
if (myMaxDerivOrder != 0)
{
if (Ndu <= myTol) // Singular case
{
const double DivisionFactor = 1.e-3;
double du;
if ((myUsupremum >= RealLast()) || (myUinfium <= RealFirst()))
du = 0.0;
else
du = myUsupremum - myUinfium;
const double aDelta = std::max(du * DivisionFactor, MinStep);
// Derivative is approximated by Taylor-series
int n = 1; // Derivative order
TheVector V;
bool IsDeriveFound;
do
{
V = TheCurveTool::DN(*myC, myU, ++n);
Ndu = V.Magnitude();
IsDeriveFound = (Ndu > myTol);
} while (!IsDeriveFound && n < myMaxDerivOrder);
if (IsDeriveFound)
{
double u;
if (myU - myUinfium < aDelta)
u = myU + aDelta;
else
u = myU - aDelta;
ThePoint P1, P2;
TheCurveTool::D0(*myC, std::min(myU, u), P1);
TheCurveTool::D0(*myC, std::max(myU, u), P2);
TheVector V1(P1, P2);
double aDirFactor = V.Dot(V1);
if (aDirFactor < 0.0)
D1c = -V;
else
D1c = V;
}
else
{
// Derivative is approximated by three points
ThePoint Ptemp;
ThePoint P1, P2, P3;
bool IsParameterGrown;
if (myU - myUinfium < 2 * aDelta)
{
TheCurveTool::D0(*myC, myU, P1);
TheCurveTool::D0(*myC, myU + aDelta, P2);
TheCurveTool::D0(*myC, myU + 2 * aDelta, P3);
IsParameterGrown = true;
}
else
{
TheCurveTool::D0(*myC, myU - 2 * aDelta, P1);
TheCurveTool::D0(*myC, myU - aDelta, P2);
TheCurveTool::D0(*myC, myU, P3);
IsParameterGrown = false;
}
TheVector V1(Ptemp, P1), V2(Ptemp, P2), V3(Ptemp, P3);
if (IsParameterGrown)
D1c = -3 * V1 + 4 * V2 - V3;
else
D1c = V1 - 4 * V2 + 3 * V3;
}
Ndu = D1c.Magnitude();
}
}
if (Ndu <= MinTol)
{
// Warning: 1st derivative is equal to zero!
return false;
}
TheVector PPc(myP, myPc);
theF = PPc.Dot(D1c) / Ndu;
return true;
}
//! Calculation of F'(u).
//! @param theU Parameter value
//! @param theDF Output derivative value
//! @return True if computation succeeded
bool Derivative(const double theU, double& theDF) override
{
if (!myPinit || !myCinit)
{
throw Standard_TypeMismatch();
}
double F;
return Values(theU, F, theDF);
}
//! Calculation of F(u) and F'(u).
//! @param theU Parameter value
//! @param theF Output function value
//! @param theDF Output derivative value
//! @return True if computation succeeded
bool Values(const double theU, double& theF, double& theDF) override
{
if (!myPinit || !myCinit)
{
throw Standard_TypeMismatch("No init");
}
ThePoint myPc_old = myPc, myP_old = myP;
if (Value(theU, theF) == false)
{
myD1Init = false;
return false;
}
myU = theU;
myPc = myPc_old;
myP = myP_old;
TheVector D1c, D2c;
TheCurveTool::D2(*myC, myU, myPc, D1c, D2c);
double Ndu = D1c.Magnitude();
if (Ndu <= myTol) // Singular case
{
// Derivative is approximated by three points
const double DivisionFactor = 0.01;
double du;
if ((myUsupremum >= RealLast()) || (myUinfium <= RealFirst()))
du = 0.0;
else
du = myUsupremum - myUinfium;
const double aDelta = std::max(du * DivisionFactor, MinStep);
double F1, F2, F3;
if (myU - myUinfium < 2 * aDelta)
{
F1 = theF;
const double U2 = myU + aDelta;
const double U3 = myU + aDelta * 2.0;
if (!((Value(U2, F2)) && (Value(U3, F3))))
{
myD1Init = false;
return false;
}
theDF = (-3 * F1 + 4 * F2 - F3) / (2.0 * aDelta);
}
else
{
F3 = theF;
const double U1 = myU - aDelta * 2.0;
const double U2 = myU - aDelta;
if (!((Value(U2, F2)) && (Value(U1, F1))))
{
myD1Init = false;
return false;
}
theDF = (F1 - 4 * F2 + 3 * F3) / (2.0 * aDelta);
}
myU = theU;
myPc = myPc_old;
myP = myP_old;
}
else
{
TheVector PPc(myP, myPc);
theDF = Ndu + (PPc.Dot(D2c) / Ndu) - theF * (D1c.Dot(D2c)) / (Ndu * Ndu);
}
myD1f = theDF;
myD1Init = true;
return true;
}
//! Save the found extremum.
//! @return State number
int GetStateNumber() override
{
if (!myPinit || !myCinit)
{
throw Standard_TypeMismatch();
}
mySqDist.Append(myPc.SquareDistance(myP));
// It is necessary to always compute myD1f.
myD1Init = true;
double FF, DD;
Values(myU, FF, DD);
int IntVal = 0;
if (myD1f > 0.0)
{
IntVal = 1;
}
myIsMin.Append(IntVal);
myPoint.Append(ThePOnC(myU, myPc));
return 0;
}
//! Return the number of found extrema.
int NbExt() const { return mySqDist.Length(); }
//! Returns the Nth square distance.
//! @param theN Index of extremum
double SquareDistance(const int theN) const
{
if (!myPinit || !myCinit)
{
throw Standard_TypeMismatch();
}
return mySqDist.Value(theN);
}
//! Shows if the Nth distance is a minimum.
//! @param theN Index of extremum
bool IsMin(const int theN) const
{
if (!myPinit || !myCinit)
{
throw Standard_TypeMismatch();
}
return (myIsMin.Value(theN) == 1);
}
//! Returns the Nth extremum point.
//! @param theN Index of extremum
const ThePOnC& Point(const int theN) const
{
if (!myPinit || !myCinit)
{
throw Standard_TypeMismatch();
}
return myPoint.Value(theN);
}
//! Determines boundaries of subinterval for root finding.
//! @param theUfirst First parameter bound
//! @param theUlast Last parameter bound
void SubIntervalInitialize(const double theUfirst, const double theUlast)
{
myUinfium = theUfirst;
myUsupremum = theUlast;
}
//! Computes a tolerance value. If 1st derivative of curve |D1| < Tol,
//! it is considered D1=0.
double SearchOfTolerance()
{
const int NPoint = 10;
const double aStep = (myUsupremum - myUinfium) / static_cast<double>(NPoint);
int aNum = 0;
double aMax = -Precision::Infinite();
do
{
double u = myUinfium + aNum * aStep;
if (u > myUsupremum)
u = myUsupremum;
ThePoint Ptemp;
TheVector VDer;
TheCurveTool::D1(*myC, u, Ptemp, VDer);
if (Precision::IsInfinite(VDer.X()) || Precision::IsInfinite(VDer.Y()))
{
continue;
}
double vm = VDer.Magnitude();
if (vm > aMax)
aMax = vm;
} while (++aNum < NPoint + 1);
return std::max(aMax * TolFactor, MinTol);
}
private:
static constexpr double TolFactor = 1.e-12;
static constexpr double MinTol = 1.e-20;
static constexpr double MinStep = 1.e-7;
static constexpr int MaxOrder = 3;
ThePoint myP;
TheCurve* myC;
double myU;
ThePoint myPc;
double myD1f;
NCollection_Sequence<double> mySqDist;
NCollection_Sequence<int> myIsMin;
TheSeqPOnC myPoint;
bool myPinit;
bool myCinit;
bool myD1Init;
double myTol;
int myMaxDerivOrder;
double myUinfium;
double myUsupremum;
};
#endif // _Extrema_GFuncExtPC_HeaderFile