// Created on: 1995-07-18 // Created by: Modelistation // Copyright (c) 1995-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_GGenExtCC_HeaderFile #define _Extrema_GGenExtCC_HeaderFile #include #include "Extrema_GlobOptFuncCC.hxx" #include "GCPnts_AbscissaPoint.hxx" #include "math_GlobOptMin.hxx" #include "math_Vector.hxx" #include "NCollection_CellFilter.hxx" #include "NCollection_DynamicArray.hxx" #include "Precision.hxx" #include "Standard.hxx" #include "Standard_DefineAlloc.hxx" #include "Standard_NullObject.hxx" #include "Standard_OutOfRange.hxx" #include "StdFail_NotDone.hxx" #include "NCollection_Array1.hxx" #include "NCollection_HArray1.hxx" #include "Standard_Integer.hxx" #include "NCollection_List.hxx" #include "NCollection_Sequence.hxx" #ifndef M_SQRT2 #define M_SQRT2 1.41421356237309504880168872420969808 #endif //! Template class for computing extremal distances between two curves. //! The function F(u,v)=distance(C1(u),C2(v)) has an extremum when gradient(f)=0. //! The algorithm uses Evtushenko's global optimization solver. //! //! @tparam TheCurve1 Type of the first curve (e.g., Adaptor3d_Curve) //! @tparam TheCurveTool1 Tool class for the first curve //! @tparam TheCurve2 Type of the second curve //! @tparam TheCurveTool2 Tool class for the second curve //! @tparam ThePOnC Point on curve type (e.g., Extrema_POnCurv) //! @tparam ThePoint Point type (e.g., gp_Pnt) //! @tparam TheExtPC Point-to-curve extremum class (e.g., Extrema_ExtPC) template class Extrema_GGenExtCC { public: DEFINE_STANDARD_ALLOC //! Default constructor. Extrema_GGenExtCC(); //! Constructor with two curves. Extrema_GGenExtCC(const TheCurve1& theC1, const TheCurve2& theC2); //! Constructor with two curves and parameter bounds. Extrema_GGenExtCC(const TheCurve1& theC1, const TheCurve2& theC2, const double theUinf, const double theUsup, const double theVinf, const double theVsup); //! Sets parameters for computation. void SetParams(const TheCurve1& theC1, const TheCurve2& theC2, const double theUinf, const double theUsup, const double theVinf, const double theVsup); //! Sets the tolerance. void SetTolerance(const double theTol); //! Set flag for single extrema computation. void SetSingleSolutionFlag(const bool theFlag); //! Get flag for single extrema computation. bool GetSingleSolutionFlag() const; //! Performs calculations. void Perform(); //! Returns True if the distances are found. bool IsDone() const; //! Returns state of myParallel flag. bool IsParallel() const; //! Returns the number of extremum distances. int NbExt() const; //! Returns the value of the Nth square extremum distance. double SquareDistance(const int theN = 1) const; //! Returns the points of the Nth extremum distance. void Points(const int theN, ThePOnC& theP1, ThePOnC& theP2) const; private: bool myIsFindSingleSolution; bool myParallel; double myCurveMinTol; math_Vector myLowBorder; math_Vector myUppBorder; NCollection_Sequence myPoints1; NCollection_Sequence myPoints2; void* myC[2]; bool myDone; }; //================================================================================================= // Static helper functions //================================================================================================= namespace { // Comparator, used in std::sort. inline bool Extrema_GGenExtCC_comp(const gp_XY& theA, const gp_XY& theB) { if (theA.X() < theB.X()) { return true; } else { if (theA.X() == theB.X()) { if (theA.Y() < theB.Y()) return true; } } return false; } inline void Extrema_GGenExtCC_ChangeIntervals(occ::handle>& theInts, const int theNbInts) { int aNbInts = theInts->Length() - 1; int aNbAdd = theNbInts - aNbInts; occ::handle> aNewInts = new NCollection_HArray1(1, theNbInts + 1); int aNbLast = theInts->Length(); int i; if (aNbInts == 1) { aNewInts->SetValue(1, theInts->First()); aNewInts->SetValue(theNbInts + 1, theInts->Last()); double dt = (theInts->Last() - theInts->First()) / theNbInts; double t = theInts->First() + dt; for (i = 2; i <= theNbInts; ++i, t += dt) { aNewInts->SetValue(i, t); } theInts = aNewInts; return; } for (i = 1; i <= aNbLast; ++i) { aNewInts->SetValue(i, theInts->Value(i)); } while (aNbAdd > 0) { double anLIntMax = -1.; int aMaxInd = -1; for (i = 1; i < aNbLast; ++i) { double anL = aNewInts->Value(i + 1) - aNewInts->Value(i); if (anL > anLIntMax) { anLIntMax = anL; aMaxInd = i; } } double t = (aNewInts->Value(aMaxInd + 1) + aNewInts->Value(aMaxInd)) / 2.; for (i = aNbLast; i > aMaxInd; --i) { aNewInts->SetValue(i + 1, aNewInts->Value(i)); } aNbLast++; aNbAdd--; aNewInts->SetValue(aMaxInd + 1, t); } theInts = aNewInts; } class Extrema_GGenExtCC_PointsInspector { public: static constexpr int Dimension = 2; typedef gp_XY Point; typedef gp_XY Target; static double Coord(int i, const Point& thePnt) { return thePnt.Coord(i + 1); } static Point Shift(const Point& thePnt, double theTol) { return Point(thePnt.X() + theTol, thePnt.Y() + theTol); } Extrema_GGenExtCC_PointsInspector(const double theTol) : myTol(theTol * theTol), myIsFind(false) { } void ClearFind() { myIsFind = false; } bool isFind() const { return myIsFind; } void SetCurrent(const gp_XY& theCurPnt) { myCurrent = theCurPnt; } NCollection_CellFilter_Action Inspect(const Target& theObject) { gp_XY aPt = myCurrent.Subtracted(theObject); const double aSQDist = aPt.SquareModulus(); if (aSQDist < myTol) { myIsFind = true; } return CellFilter_Keep; } private: double myTol; gp_XY myCurrent; bool myIsFind; }; template double Extrema_GGenExtCC_ProjPOnC(const ThePointType& theP, TheExtPCType& theProjTool) { double aDist = ::RealLast(); theProjTool.Perform(theP); if (theProjTool.IsDone() && theProjTool.NbExt()) { for (int i = 1; i <= theProjTool.NbExt(); ++i) { double aD = theProjTool.SquareDistance(i); if (aD < aDist) aDist = aD; } } return aDist; } } // namespace //================================================================================================= // Implementation //================================================================================================= //================================================================================================= template Extrema_GGenExtCC:: Extrema_GGenExtCC() : myIsFindSingleSolution(false), myParallel(false), myCurveMinTol(Precision::PConfusion()), myLowBorder(1, 2), myUppBorder(1, 2), myDone(false) { myC[0] = myC[1] = nullptr; } //================================================================================================= template Extrema_GGenExtCC:: Extrema_GGenExtCC(const TheCurve1& theC1, const TheCurve2& theC2) : myIsFindSingleSolution(false), myParallel(false), myCurveMinTol(Precision::PConfusion()), myLowBorder(1, 2), myUppBorder(1, 2), myDone(false) { myC[0] = (void*)&theC1; myC[1] = (void*)&theC2; myLowBorder(1) = theC1.FirstParameter(); myLowBorder(2) = theC2.FirstParameter(); myUppBorder(1) = theC1.LastParameter(); myUppBorder(2) = theC2.LastParameter(); } //================================================================================================= template Extrema_GGenExtCC:: Extrema_GGenExtCC(const TheCurve1& theC1, const TheCurve2& theC2, const double theUinf, const double theUsup, const double theVinf, const double theVsup) : myIsFindSingleSolution(false), myParallel(false), myCurveMinTol(Precision::PConfusion()), myLowBorder(1, 2), myUppBorder(1, 2), myDone(false) { myC[0] = (void*)&theC1; myC[1] = (void*)&theC2; myLowBorder(1) = theUinf; myLowBorder(2) = theVinf; myUppBorder(1) = theUsup; myUppBorder(2) = theVsup; } //================================================================================================= template void Extrema_GGenExtCC::SetParams(const TheCurve1& theC1, const TheCurve2& theC2, const double theUinf, const double theUsup, const double theVinf, const double theVsup) { myC[0] = (void*)&theC1; myC[1] = (void*)&theC2; myLowBorder(1) = theUinf; myLowBorder(2) = theVinf; myUppBorder(1) = theUsup; myUppBorder(2) = theVsup; } //================================================================================================= template void Extrema_GGenExtCC::SetTolerance(const double theTol) { myCurveMinTol = theTol; } //================================================================================================= template void Extrema_GGenExtCC::SetSingleSolutionFlag(const bool theFlag) { myIsFindSingleSolution = theFlag; } //================================================================================================= template bool Extrema_GGenExtCC::GetSingleSolutionFlag() const { return myIsFindSingleSolution; } //================================================================================================= template void Extrema_GGenExtCC::Perform() { myDone = false; myParallel = false; TheCurve1& C1 = *(TheCurve1*)myC[0]; TheCurve2& C2 = *(TheCurve2*)myC[1]; int aNbInter[2]; GeomAbs_Shape aContinuity = GeomAbs_C2; aNbInter[0] = C1.NbIntervals(aContinuity); aNbInter[1] = C2.NbIntervals(aContinuity); if (aNbInter[0] * aNbInter[1] > 100) { aContinuity = GeomAbs_C1; aNbInter[0] = C1.NbIntervals(aContinuity); aNbInter[1] = C2.NbIntervals(aContinuity); } double anL[2]; int indmax = -1, indmin = -1; const double mult = 20.; if (!(Precision::IsInfinite(C1.FirstParameter()) || Precision::IsInfinite(C1.LastParameter()) || Precision::IsInfinite(C2.FirstParameter()) || Precision::IsInfinite(C2.LastParameter()))) { anL[0] = GCPnts_AbscissaPoint::Length(C1); anL[1] = GCPnts_AbscissaPoint::Length(C2); if (anL[0] / aNbInter[0] > mult * anL[1] / aNbInter[1]) { indmax = 0; indmin = 1; } else if (anL[1] / aNbInter[1] > mult * anL[0] / aNbInter[0]) { indmax = 1; indmin = 0; } } int aNbIntOpt = 0; if (indmax >= 0) { aNbIntOpt = RealToInt(anL[indmax] * aNbInter[indmin] / anL[indmin] / (mult / 4.)) + 1; if (aNbIntOpt > 100 || aNbIntOpt < aNbInter[indmax]) { indmax = -1; } else { if (aNbIntOpt * aNbInter[indmin] > 100) { aNbIntOpt = 100 / aNbInter[indmin]; if (aNbIntOpt < aNbInter[indmax]) { indmax = -1; } } } } occ::handle> anIntervals1 = new NCollection_HArray1(1, aNbInter[0] + 1); occ::handle> anIntervals2 = new NCollection_HArray1(1, aNbInter[1] + 1); C1.Intervals(anIntervals1->ChangeArray1(), aContinuity); C2.Intervals(anIntervals2->ChangeArray1(), aContinuity); if (indmax >= 0) { if (indmax == 0) { Extrema_GGenExtCC_ChangeIntervals(anIntervals1, aNbIntOpt); aNbInter[0] = anIntervals1->Length() - 1; } else { Extrema_GGenExtCC_ChangeIntervals(anIntervals2, aNbIntOpt); aNbInter[1] = anIntervals2->Length() - 1; } } if (C1.IsClosed() && aNbInter[0] == 1) { Extrema_GGenExtCC_ChangeIntervals(anIntervals1, 3); aNbInter[0] = anIntervals1->Length() - 1; } if (C2.IsClosed() && aNbInter[1] == 1) { Extrema_GGenExtCC_ChangeIntervals(anIntervals2, 3); aNbInter[1] = anIntervals2->Length() - 1; } const double aMaxLC = 10000.; double aLC = 100.0; const double aMaxDer1 = 1.0 / C1.Resolution(1.0); const double aMaxDer2 = 1.0 / C2.Resolution(1.0); double aMaxDer = std::max(aMaxDer1, aMaxDer2) * M_SQRT2; if (aLC > aMaxDer) aLC = aMaxDer; bool isConstLockedFlag = false; const double aCR = 0.001; if (aMaxDer1 / aMaxDer < aCR || aMaxDer2 / aMaxDer < aCR) { isConstLockedFlag = true; } if (aMaxDer > aMaxLC) { aLC = aMaxLC; isConstLockedFlag = true; } if (C1.GetType() == GeomAbs_Line) { aMaxDer = 1.0 / C2.Resolution(1.0); if (aLC > aMaxDer) { isConstLockedFlag = true; aLC = aMaxDer; } } if (C2.GetType() == GeomAbs_Line) { aMaxDer = 1.0 / C1.Resolution(1.0); if (aLC > aMaxDer) { isConstLockedFlag = true; aLC = aMaxDer; } } Extrema_GlobOptFuncCCC2 aFunc(C1, C2); if (aLC < aMaxLC || aMaxDer > aMaxLC) { math_Vector aT(1, 2), aG(1, 2); double aF, aMaxG = 0.; double t1, t2, dt1, dt2; int n1 = 21, n2 = 21, i1, i2; dt1 = (C1.LastParameter() - C1.FirstParameter()) / (n1 - 1); dt2 = (C2.LastParameter() - C2.FirstParameter()) / (n2 - 1); for (i1 = 1, t1 = C1.FirstParameter(); i1 <= n1; ++i1, t1 += dt1) { aT(1) = t1; for (i2 = 1, t2 = C2.FirstParameter(); i2 <= n2; ++i2, t2 += dt2) { aT(2) = t2; aFunc.Values(aT, aF, aG); double aMod = aG(1) * aG(1) + aG(2) * aG(2); aMaxG = std::max(aMaxG, aMod); } } aMaxG = std::sqrt(aMaxG); if (aMaxG > aMaxDer) { aLC = std::min(aMaxG, aMaxLC); isConstLockedFlag = true; } if (aMaxG > 100. * aMaxLC) { aLC = 100. * aMaxLC; isConstLockedFlag = true; } else if (aMaxG < 0.1 * aMaxDer) { isConstLockedFlag = true; } } math_GlobOptMin aFinder(&aFunc, myLowBorder, myUppBorder, aLC); aFinder.SetLipConstState(isConstLockedFlag); aFinder.SetContinuity(aContinuity == GeomAbs_C2 ? 2 : 1); double aDiscTol = 1.0e-2; double aValueTol = 1.0e-2; double aSameTol = myCurveMinTol / (aDiscTol); aFinder.SetTol(aDiscTol, aSameTol); aFinder.SetFunctionalMinimalValue(0.0); const double aCellSize = std::max(std::max(anIntervals1->Last() - anIntervals1->First(), anIntervals2->Last() - anIntervals2->First()) * Precision::PConfusion() / (2.0 * M_SQRT2), Precision::PConfusion()); Extrema_GGenExtCC_PointsInspector anInspector(aCellSize); NCollection_CellFilter aFilter(aCellSize); NCollection_DynamicArray aPnts; int i, j, k; math_Vector aFirstBorderInterval(1, 2); math_Vector aSecondBorderInterval(1, 2); double aF = RealLast(); double aCurrF = RealLast(); for (i = 1; i <= aNbInter[0]; i++) { for (j = 1; j <= aNbInter[1]; j++) { aFirstBorderInterval(1) = anIntervals1->Value(i); aFirstBorderInterval(2) = anIntervals2->Value(j); aSecondBorderInterval(1) = anIntervals1->Value(i + 1); aSecondBorderInterval(2) = anIntervals2->Value(j + 1); aFinder.SetLocalParams(aFirstBorderInterval, aSecondBorderInterval); aFinder.Perform(GetSingleSolutionFlag()); aCurrF = aFinder.GetF(); if (aCurrF >= aF + aSameTol * aValueTol) { continue; } if (aCurrF > aF - aSameTol * aValueTol) { if (aCurrF < aF) aF = aCurrF; } else { aF = aCurrF; aFilter.Reset(aCellSize); aPnts.Clear(); } math_Vector sol(1, 2); for (k = 1; k <= aFinder.NbExtrema(); k++) { aFinder.Points(k, sol); gp_XY aPnt2d(sol(1), sol(2)); gp_XY aXYmin = anInspector.Shift(aPnt2d, -aCellSize); gp_XY aXYmax = anInspector.Shift(aPnt2d, aCellSize); anInspector.ClearFind(); anInspector.SetCurrent(aPnt2d); aFilter.Inspect(aXYmin, aXYmax, anInspector); if (!anInspector.isFind()) { aFilter.Add(aPnt2d, aPnt2d); aPnts.Append(gp_XY(sol(1), sol(2))); } } } } const int aNbSol = aPnts.Length(); if (aNbSol == 0) { myDone = false; return; } myDone = true; if (aNbSol == 1) { const gp_XY& aSol = aPnts.First(); myPoints1.Append(aSol.X()); myPoints2.Append(aSol.Y()); return; } std::sort(aPnts.begin(), aPnts.end(), Extrema_GGenExtCC_comp); NCollection_List aSolutions; bool bSaveSolution = true; bool bDirsCoinside = true; bool bDifferentSolutions = false; bool isParallel = true; double aVal = 0.0; math_Vector aVec(1, 2, 0.0); for (int anIdx = 0; anIdx < aNbSol - 1; anIdx++) { const gp_XY& aCurrent = aPnts(anIdx); const gp_XY& aNext = aPnts(anIdx + 1); aVec(1) = (aCurrent.X() + aNext.X()) * 0.5; aVec(2) = (aCurrent.Y() + aNext.Y()) * 0.5; aFunc.Value(aVec, aVal); if (std::abs(aVal - aF) < Precision::Confusion()) { if (bSaveSolution) { aSolutions.Append(anIdx); bSaveSolution = false; } } else { isParallel = false; aSolutions.Append(anIdx); bSaveSolution = true; } if (!bDifferentSolutions) { if (aNext.X() > aCurrent.X()) { if (aNext.Y() > aCurrent.Y()) { bDifferentSolutions = true; bDirsCoinside = true; } else if (aNext.Y() < aCurrent.Y()) { bDifferentSolutions = true; bDirsCoinside = false; } } } } aSolutions.Append(aNbSol - 1); if (!bDifferentSolutions) isParallel = false; if (isParallel) { double aT1[2] = {myLowBorder(1), myUppBorder(1)}; double aT2[2] = {bDirsCoinside ? myLowBorder(2) : myUppBorder(2), bDirsCoinside ? myUppBorder(2) : myLowBorder(2)}; TheExtPC anExtPC1, anExtPC2; anExtPC1.Initialize(C1, myLowBorder(1), myUppBorder(1)); anExtPC2.Initialize(C2, myLowBorder(2), myUppBorder(2)); for (int iT = 0; isParallel && (iT < 2); ++iT) { double aDist1 = Extrema_GGenExtCC_ProjPOnC(C1.Value(aT1[iT]), anExtPC2); double aDist2 = Extrema_GGenExtCC_ProjPOnC(C2.Value(aT2[iT]), anExtPC1); isParallel = (std::abs(std::min(aDist1, aDist2) - aF * aF) < Precision::Confusion()); } } if (isParallel) { const gp_XY& aSol = aPnts.First(); myPoints1.Append(aSol.X()); myPoints2.Append(aSol.Y()); myParallel = true; } else { NCollection_List::Iterator aItSol(aSolutions); for (; aItSol.More(); aItSol.Next()) { const gp_XY& aSol = aPnts(aItSol.Value()); myPoints1.Append(aSol.X()); myPoints2.Append(aSol.Y()); } } } //================================================================================================= template bool Extrema_GGenExtCC::IsDone() const { return myDone; } //================================================================================================= template bool Extrema_GGenExtCC::IsParallel() const { if (!IsDone()) throw StdFail_NotDone(); return myParallel; } //================================================================================================= template int Extrema_GGenExtCC::NbExt() const { if (!IsDone()) throw StdFail_NotDone(); return myPoints1.Length(); } //================================================================================================= template double Extrema_GGenExtCC::SquareDistance(const int theN) const { if (theN < 1 || theN > NbExt()) { throw Standard_OutOfRange(); } return TheCurveTool1::Value(*((TheCurve1*)myC[0]), myPoints1(theN)) .SquareDistance(TheCurveTool2::Value(*((TheCurve2*)myC[1]), myPoints2(theN))); } //================================================================================================= template void Extrema_GGenExtCC::Points(const int theN, ThePOnC& theP1, ThePOnC& theP2) const { if (theN < 1 || theN > NbExt()) { throw Standard_OutOfRange(); } theP1.SetValues(myPoints1(theN), TheCurveTool1::Value(*((TheCurve1*)myC[0]), myPoints1(theN))); theP2.SetValues(myPoints2(theN), TheCurveTool2::Value(*((TheCurve2*)myC[1]), myPoints2(theN))); } #endif // _Extrema_GGenExtCC_HeaderFile