diff --git a/src/FoundationClasses/TKMath/BSplCLib/BSplCLib_Cache.cxx b/src/FoundationClasses/TKMath/BSplCLib/BSplCLib_Cache.cxx index c267e9a722..6a67afb27d 100644 --- a/src/FoundationClasses/TKMath/BSplCLib/BSplCLib_Cache.cxx +++ b/src/FoundationClasses/TKMath/BSplCLib/BSplCLib_Cache.cxx @@ -21,6 +21,8 @@ IMPLEMENT_STANDARD_RTTIEXT(BSplCLib_Cache, Standard_Transient) +//================================================================================================== + BSplCLib_Cache::BSplCLib_Cache( const Standard_Integer& theDegree, const Standard_Boolean& thePeriodic, @@ -33,6 +35,8 @@ BSplCLib_Cache::BSplCLib_Cache( { } +//================================================================================================== + BSplCLib_Cache::BSplCLib_Cache( const Standard_Integer& theDegree, const Standard_Boolean& thePeriodic, @@ -45,11 +49,15 @@ BSplCLib_Cache::BSplCLib_Cache( { } +//================================================================================================== + Standard_Boolean BSplCLib_Cache::IsCacheValid(Standard_Real theParameter) const { return myParams.IsCacheValid(theParameter); } +//================================================================================================== + void BSplCLib_Cache::BuildCache(const Standard_Real& theParameter, const TColStd_Array1OfReal& theFlatKnots, const TColgp_Array1OfPnt2d& thePoles2d, @@ -106,60 +114,77 @@ void BSplCLib_Cache::BuildCache(const Standard_Real& theParameter, aPolesWeights); } -void BSplCLib_Cache::CalculateDerivative(const Standard_Real& theParameter, - const Standard_Integer& theDerivative, - Standard_Real& theDerivArray) const +//================================================================================================== + +void BSplCLib_Cache::calculateDerivative(double theParameter, + int theDerivative, + Standard_Real* theDerivArray) const { - Standard_Real aNewParameter = myParams.PeriodicNormalization(theParameter); - aNewParameter = (aNewParameter - myParams.SpanStart) / myParams.SpanLength; + double aLocalParam = myParams.PeriodicNormalization(theParameter); + aLocalParam = (aLocalParam - myParams.SpanStart) / myParams.SpanLength; + calculateDerivativeLocal(aLocalParam, theDerivative, theDerivArray); +} - Standard_Real* aPolesArray = const_cast(myPolesWeightsBuffer); - const Standard_Integer aDimension = myRowLength; +//================================================================================================== + +void BSplCLib_Cache::calculateDerivativeLocal(double theLocalParam, + int theDerivative, + Standard_Real* theDerivArray) const +{ + const int aDimension = myRowLength; // Temporary container. The maximal size of this container is defined by: // 1) maximal derivative for cache evaluation, which is 3, plus one row for function values, // 2) and maximal dimension of the point, which is 3, plus one column for weights. Standard_Real aTmpContainer[16]; - // When the PLib::RationaDerivative needs to be called, use temporary container - Standard_Real* aPntDeriv = myIsRational ? aTmpContainer : &theDerivArray; + // When the PLib::RationalDerivative needs to be called, use temporary container + Standard_Real* aPntDeriv = myIsRational ? aTmpContainer : theDerivArray; // When the degree of curve is lesser than the requested derivative, // nullify array cells corresponding to greater derivatives - Standard_Integer aDerivative = theDerivative; + int aDerivative = theDerivative; if (!myIsRational && myParams.Degree < theDerivative) { aDerivative = myParams.Degree; - for (Standard_Integer ind = myParams.Degree * aDimension; - ind < (theDerivative + 1) * aDimension; - ind++) + for (int ind = myParams.Degree * aDimension; ind < (theDerivative + 1) * aDimension; ind++) { aPntDeriv[ind] = 0.0; - // clang-format off - (&theDerivArray)[ind] = 0.0; // should be cleared separately, because aPntDeriv may look to another memory area - // clang-format on } } - PLib::EvalPolynomial(aNewParameter, + PLib::EvalPolynomial(theLocalParam, aDerivative, myParams.Degree, aDimension, - aPolesArray[0], + myPolesWeightsBuffer[0], aPntDeriv[0]); + // Unnormalize derivatives since those are computed normalized + // Use division by SpanLength instead of multiplication by precomputed inverse + // for better numerical stability with very small span lengths Standard_Real aFactor = 1.0; - for (Standard_Integer deriv = 1; deriv <= aDerivative; deriv++) + for (int deriv = 1; deriv <= aDerivative; deriv++) { aFactor /= myParams.SpanLength; - for (Standard_Integer ind = 0; ind < aDimension; ind++) + for (int ind = 0; ind < aDimension; ind++) + { aPntDeriv[aDimension * deriv + ind] *= aFactor; + } } - if (myIsRational) // calculate derivatives divided by weights derivatives - PLib::RationalDerivative(aDerivative, aDerivative, aDimension - 1, aPntDeriv[0], theDerivArray); + if (myIsRational) + { + PLib::RationalDerivative(aDerivative, + aDerivative, + aDimension - 1, + aPntDeriv[0], + theDerivArray[0]); + } } +//================================================================================================== + void BSplCLib_Cache::D0(const Standard_Real& theParameter, gp_Pnt2d& thePoint) const { Standard_Real aNewParameter = myParams.PeriodicNormalization(theParameter); @@ -181,25 +206,34 @@ void BSplCLib_Cache::D0(const Standard_Real& theParameter, gp_Pnt2d& thePoint) c thePoint.ChangeCoord().Divide(aPoint[2]); } +//================================================================================================== + void BSplCLib_Cache::D0(const Standard_Real& theParameter, gp_Pnt& thePoint) const { - Standard_Real aNewParameter = myParams.PeriodicNormalization(theParameter); - aNewParameter = (aNewParameter - myParams.SpanStart) / myParams.SpanLength; + Standard_Real aLocalParam = myParams.PeriodicNormalization(theParameter); + aLocalParam = (aLocalParam - myParams.SpanStart) / myParams.SpanLength; + D0Local(aLocalParam, thePoint); +} - Standard_Real* aPolesArray = const_cast(myPolesWeightsBuffer); - Standard_Real aPoint[4]; - const Standard_Integer aDimension = myRowLength; +//================================================================================================== - PLib::NoDerivativeEvalPolynomial(aNewParameter, +void BSplCLib_Cache::D0Local(double theLocalParam, gp_Pnt& thePoint) const +{ + // theLocalParam is already computed as (param - SpanStart) / SpanLength + Standard_Real aPoint[4]; + + PLib::NoDerivativeEvalPolynomial(theLocalParam, myParams.Degree, - aDimension, - myParams.Degree * aDimension, - aPolesArray[0], + myRowLength, + myParams.Degree * myRowLength, + myPolesWeightsBuffer[0], aPoint[0]); thePoint.SetCoord(aPoint[0], aPoint[1], aPoint[2]); if (myIsRational) + { thePoint.ChangeCoord().Divide(aPoint[3]); + } } void BSplCLib_Cache::D1(const Standard_Real& theParameter, @@ -209,7 +243,7 @@ void BSplCLib_Cache::D1(const Standard_Real& theParameter, Standard_Integer aDimension = myRowLength; Standard_Real aPntDeriv[8]; // result storage (point and derivative coordinates) - this->CalculateDerivative(theParameter, 1, aPntDeriv[0]); + calculateDerivative(theParameter, 1, aPntDeriv); if (myIsRational) // the size of aPntDeriv was changed by PLib::RationalDerivative aDimension -= 1; @@ -221,15 +255,21 @@ void BSplCLib_Cache::D1(const Standard_Real& theParameter, gp_Pnt& thePoint, gp_Vec& theTangent) const { - Standard_Integer aDimension = myRowLength; - Standard_Real aPntDeriv[8]; // result storage (point and derivative coordinates) + Standard_Real aLocalParam = myParams.PeriodicNormalization(theParameter); + aLocalParam = (aLocalParam - myParams.SpanStart) / myParams.SpanLength; + D1Local(aLocalParam, thePoint, theTangent); +} - this->CalculateDerivative(theParameter, 1, aPntDeriv[0]); - if (myIsRational) // the size of aPntDeriv was changed by PLib::RationalDerivative - aDimension -= 1; +//================================================================================================== - thePoint.SetCoord(aPntDeriv[0], aPntDeriv[1], aPntDeriv[2]); - theTangent.SetCoord(aPntDeriv[aDimension], aPntDeriv[aDimension + 1], aPntDeriv[aDimension + 2]); +void BSplCLib_Cache::D1Local(double theLocalParam, gp_Pnt& thePoint, gp_Vec& theTangent) const +{ + Standard_Real aDerivArray[8]; + calculateDerivativeLocal(theLocalParam, 1, aDerivArray); + + const int aDim = myIsRational ? myRowLength - 1 : myRowLength; + thePoint.SetCoord(aDerivArray[0], aDerivArray[1], aDerivArray[2]); + theTangent.SetCoord(aDerivArray[aDim], aDerivArray[aDim + 1], aDerivArray[aDim + 2]); } void BSplCLib_Cache::D2(const Standard_Real& theParameter, @@ -240,7 +280,7 @@ void BSplCLib_Cache::D2(const Standard_Real& theParameter, Standard_Integer aDimension = myRowLength; Standard_Real aPntDeriv[12]; // result storage (point and derivatives coordinates) - this->CalculateDerivative(theParameter, 2, aPntDeriv[0]); + calculateDerivative(theParameter, 2, aPntDeriv); if (myIsRational) // the size of aPntDeriv was changed by PLib::RationalDerivative aDimension -= 1; @@ -254,18 +294,26 @@ void BSplCLib_Cache::D2(const Standard_Real& theParameter, gp_Vec& theTangent, gp_Vec& theCurvature) const { - Standard_Integer aDimension = myRowLength; - Standard_Real aPntDeriv[12]; // result storage (point and derivatives coordinates) + Standard_Real aLocalParam = myParams.PeriodicNormalization(theParameter); + aLocalParam = (aLocalParam - myParams.SpanStart) / myParams.SpanLength; + D2Local(aLocalParam, thePoint, theTangent, theCurvature); +} - this->CalculateDerivative(theParameter, 2, aPntDeriv[0]); - if (myIsRational) // the size of aPntDeriv was changed by PLib::RationalDerivative - aDimension -= 1; +//================================================================================================== - thePoint.SetCoord(aPntDeriv[0], aPntDeriv[1], aPntDeriv[2]); - theTangent.SetCoord(aPntDeriv[aDimension], aPntDeriv[aDimension + 1], aPntDeriv[aDimension + 2]); - theCurvature.SetCoord(aPntDeriv[aDimension << 1], - aPntDeriv[(aDimension << 1) + 1], - aPntDeriv[(aDimension << 1) + 2]); +void BSplCLib_Cache::D2Local(double theLocalParam, + gp_Pnt& thePoint, + gp_Vec& theTangent, + gp_Vec& theCurvature) const +{ + Standard_Real aDerivArray[12]; + calculateDerivativeLocal(theLocalParam, 2, aDerivArray); + + const int aDim = myIsRational ? myRowLength - 1 : myRowLength; + const int aShift = aDim << 1; + thePoint.SetCoord(aDerivArray[0], aDerivArray[1], aDerivArray[2]); + theTangent.SetCoord(aDerivArray[aDim], aDerivArray[aDim + 1], aDerivArray[aDim + 2]); + theCurvature.SetCoord(aDerivArray[aShift], aDerivArray[aShift + 1], aDerivArray[aShift + 2]); } void BSplCLib_Cache::D3(const Standard_Real& theParameter, @@ -277,7 +325,7 @@ void BSplCLib_Cache::D3(const Standard_Real& theParameter, Standard_Integer aDimension = myRowLength; Standard_Real aPntDeriv[16]; // result storage (point and derivatives coordinates) - this->CalculateDerivative(theParameter, 3, aPntDeriv[0]); + calculateDerivative(theParameter, 3, aPntDeriv); if (myIsRational) // the size of aPntDeriv was changed by PLib::RationalDerivative aDimension -= 1; @@ -295,17 +343,27 @@ void BSplCLib_Cache::D3(const Standard_Real& theParameter, gp_Vec& theCurvature, gp_Vec& theTorsion) const { - Standard_Integer aDimension = myRowLength; - Standard_Real aPntDeriv[16]; // result storage (point and derivatives coordinates) + Standard_Real aLocalParam = myParams.PeriodicNormalization(theParameter); + aLocalParam = (aLocalParam - myParams.SpanStart) / myParams.SpanLength; + D3Local(aLocalParam, thePoint, theTangent, theCurvature, theTorsion); +} - this->CalculateDerivative(theParameter, 3, aPntDeriv[0]); - if (myIsRational) // the size of aPntDeriv was changed by PLib::RationalDerivative - aDimension -= 1; +//================================================================================================== - thePoint.SetCoord(aPntDeriv[0], aPntDeriv[1], aPntDeriv[2]); - theTangent.SetCoord(aPntDeriv[aDimension], aPntDeriv[aDimension + 1], aPntDeriv[aDimension + 2]); - Standard_Integer aShift = aDimension << 1; - theCurvature.SetCoord(aPntDeriv[aShift], aPntDeriv[aShift + 1], aPntDeriv[aShift + 2]); - aShift += aDimension; - theTorsion.SetCoord(aPntDeriv[aShift], aPntDeriv[aShift + 1], aPntDeriv[aShift + 2]); +void BSplCLib_Cache::D3Local(double theLocalParam, + gp_Pnt& thePoint, + gp_Vec& theTangent, + gp_Vec& theCurvature, + gp_Vec& theTorsion) const +{ + Standard_Real aDerivArray[16]; + calculateDerivativeLocal(theLocalParam, 3, aDerivArray); + + const int aDim = myIsRational ? myRowLength - 1 : myRowLength; + const int aShift2 = aDim << 1; + const int aShift3 = aShift2 + aDim; + thePoint.SetCoord(aDerivArray[0], aDerivArray[1], aDerivArray[2]); + theTangent.SetCoord(aDerivArray[aDim], aDerivArray[aDim + 1], aDerivArray[aDim + 2]); + theCurvature.SetCoord(aDerivArray[aShift2], aDerivArray[aShift2 + 1], aDerivArray[aShift2 + 2]); + theTorsion.SetCoord(aDerivArray[aShift3], aDerivArray[aShift3 + 1], aDerivArray[aShift3 + 2]); } diff --git a/src/FoundationClasses/TKMath/BSplCLib/BSplCLib_Cache.hxx b/src/FoundationClasses/TKMath/BSplCLib/BSplCLib_Cache.hxx index 0471090f5d..b20b2aad03 100644 --- a/src/FoundationClasses/TKMath/BSplCLib/BSplCLib_Cache.hxx +++ b/src/FoundationClasses/TKMath/BSplCLib/BSplCLib_Cache.hxx @@ -122,22 +122,65 @@ public: gp_Vec& theCurvature, gp_Vec& theTorsion) const; + //! Calculates the 3D point using pre-computed local parameter in [0, 1] range. + //! This bypasses periodic normalization and local parameter calculation. + //! @param[in] theLocalParam pre-computed local parameter: (Param - SpanStart) / SpanLength + //! @param[out] thePoint the result of calculation (the point on the curve) + Standard_EXPORT void D0Local(double theLocalParam, gp_Pnt& thePoint) const; + + //! Calculates the 3D point and first derivative using pre-computed local parameter. + //! @param[in] theLocalParam pre-computed local parameter: (Param - SpanStart) / SpanLength + //! @param[out] thePoint the point on the curve + //! @param[out] theTangent first derivative (tangent vector) + Standard_EXPORT void D1Local(double theLocalParam, gp_Pnt& thePoint, gp_Vec& theTangent) const; + + //! Calculates the 3D point, first and second derivatives using pre-computed local parameter. + //! @param[in] theLocalParam pre-computed local parameter: (Param - SpanStart) / SpanLength + //! @param[out] thePoint the point on the curve + //! @param[out] theTangent first derivative (tangent vector) + //! @param[out] theCurvature second derivative (curvature vector) + Standard_EXPORT void D2Local(double theLocalParam, + gp_Pnt& thePoint, + gp_Vec& theTangent, + gp_Vec& theCurvature) const; + + //! Calculates the 3D point, first, second and third derivatives using pre-computed local + //! parameter. + //! @param[in] theLocalParam pre-computed local parameter: (Param - SpanStart) / SpanLength + //! @param[out] thePoint the point on the curve + //! @param[out] theTangent first derivative (tangent vector) + //! @param[out] theCurvature second derivative (curvature vector) + //! @param[out] theTorsion third derivative (torsion vector) + Standard_EXPORT void D3Local(double theLocalParam, + gp_Pnt& thePoint, + gp_Vec& theTangent, + gp_Vec& theCurvature, + gp_Vec& theTorsion) const; + DEFINE_STANDARD_RTTIEXT(BSplCLib_Cache, Standard_Transient) protected: - //! Fills array of derivatives in the selected point of the curve - //! \param[in] theParameter parameter of the calculation - //! \param[in] theDerivative maximal derivative to be calculated (computes all derivatives lesser - //! than specified) \param[out] theDerivArray result array of derivatives (with size - //! (theDerivative+1)*(PntDim+1), - //! where PntDim = 2 or 3 is a dimension of the curve) - void CalculateDerivative(const Standard_Real& theParameter, - const Standard_Integer& theDerivative, - Standard_Real& theDerivArray) const; + //! Fills array of derivatives in the selected point of the curve. + //! @param[in] theParameter parameter of the calculation + //! @param[in] theDerivative maximal derivative to be calculated (computes all derivatives + //! lesser than specified) + //! @param[out] theDerivArray result array of derivatives with size (theDerivative+1)*(PntDim+1), + //! where PntDim = 2 or 3 is a dimension of the curve + void calculateDerivative(double theParameter, + int theDerivative, + Standard_Real* theDerivArray) const; + + //! Fills array of derivatives using pre-computed local parameter. + //! @param[in] theLocalParam pre-computed local parameter: (Param - SpanStart) / SpanLength + //! @param[in] theDerivative maximal derivative to be calculated (1, 2, or 3) + //! @param[out] theDerivArray result array of derivatives + void calculateDerivativeLocal(double theLocalParam, + int theDerivative, + Standard_Real* theDerivArray) const; // copying is prohibited - BSplCLib_Cache(const BSplCLib_Cache&); - void operator=(const BSplCLib_Cache&); + BSplCLib_Cache(const BSplCLib_Cache&) = delete; + void operator=(const BSplCLib_Cache&) = delete; private: // clang-format off diff --git a/src/FoundationClasses/TKMath/BSplSLib/BSplSLib_Cache.cxx b/src/FoundationClasses/TKMath/BSplSLib/BSplSLib_Cache.cxx index 68c0da511f..0837fc2e5c 100644 --- a/src/FoundationClasses/TKMath/BSplSLib/BSplSLib_Cache.cxx +++ b/src/FoundationClasses/TKMath/BSplSLib/BSplSLib_Cache.cxx @@ -19,15 +19,215 @@ #include #include +#include + IMPLEMENT_STANDARD_RTTIEXT(BSplSLib_Cache, Standard_Transient) +namespace +{ + //! Converts handle of array of Standard_Real into the pointer to Standard_Real -static Standard_Real* ConvertArray(const Handle(TColStd_HArray2OfReal)& theHArray) +Standard_Real* ConvertArray(const Handle(TColStd_HArray2OfReal)& theHArray) { const TColStd_Array2OfReal& anArray = theHArray->Array2(); return (Standard_Real*)&(anArray(anArray.LowerRow(), anArray.LowerCol())); } +//================================================================================================== + +//! Computes local UV parameters for D0 evaluation (no derivative scaling needed). +//! BSplSLib uses different convention for span parameters than BSplCLib +//! (Start is in the middle of the span and length is half-span). +std::pair toLocalParamsD0(double theU, + double theV, + const BSplCLib_CacheParams& theParamsU, + const BSplCLib_CacheParams& theParamsV) +{ + const double aNewU = theParamsU.PeriodicNormalization(theU); + const double aNewV = theParamsV.PeriodicNormalization(theV); + const double aSpanLengthU = 0.5 * theParamsU.SpanLength; + const double aSpanStartU = theParamsU.SpanStart + aSpanLengthU; + const double aSpanLengthV = 0.5 * theParamsV.SpanLength; + const double aSpanStartV = theParamsV.SpanStart + aSpanLengthV; + return {(aNewU - aSpanStartU) / aSpanLengthU, (aNewV - aSpanStartV) / aSpanLengthV}; +} + +//! Computes local UV parameters and inverse span lengths for derivative evaluation. +//! The same inverse values are used for both parameter transformation and derivative scaling +//! to maintain numerical consistency with the original implementation. +std::pair toLocalParams(double theU, + double theV, + const BSplCLib_CacheParams& theParamsU, + const BSplCLib_CacheParams& theParamsV, + double& theInvU, + double& theInvV) +{ + const double aNewU = theParamsU.PeriodicNormalization(theU); + const double aNewV = theParamsV.PeriodicNormalization(theV); + const double aSpanLengthU = 0.5 * theParamsU.SpanLength; + const double aSpanStartU = theParamsU.SpanStart + aSpanLengthU; + const double aSpanLengthV = 0.5 * theParamsV.SpanLength; + const double aSpanStartV = theParamsV.SpanStart + aSpanLengthV; + // Compute inverses once and reuse for both parameter transformation and derivative scaling + // to maintain numerical consistency with the original implementation + theInvU = 1.0 / aSpanLengthU; + theInvV = 1.0 / aSpanLengthV; + return {(aNewU - aSpanStartU) * theInvU, (aNewV - aSpanStartV) * theInvV}; +} + +//================================================================================================== + +//! Evaluates the polynomials and their derivatives. +//! @param[in] thePolesWeights handle to the array of poles and weights +//! @param[in] theParamsU parameters for U direction +//! @param[in] theParamsV parameters for V direction +//! @param[in] theIsRational flag indicating if the surface is rational +//! @param[in] theLocalU local U parameter +//! @param[in] theLocalV local V parameter +//! @param[in] theUDerivMax maximum U derivative +//! @param[in] theVDerivMax maximum V derivative +//! @param[out] theResultArray array to store the results +void EvaluatePolynomials(const Handle(TColStd_HArray2OfReal)& thePolesWeights, + const BSplCLib_CacheParams& theParamsU, + const BSplCLib_CacheParams& theParamsV, + const Standard_Boolean theIsRational, + double theLocalU, + double theLocalV, + int theUDerivMax, + int theVDerivMax, + Standard_Real* theResultArray) +{ + Standard_Real* const aPolesArray = ConvertArray(thePolesWeights); + const Standard_Integer aDimension = theIsRational ? 4 : 3; + const Standard_Integer aCacheCols = thePolesWeights->RowLength(); + + const bool isMaxU = (theParamsU.Degree > theParamsV.Degree); + const auto [aMinParam, aMaxParam] = + isMaxU ? std::make_pair(theLocalV, theLocalU) : std::make_pair(theLocalU, theLocalV); + + // Determine derivatives to calculate along each direction + const int aMaxDeriv = isMaxU ? theUDerivMax : theVDerivMax; + const int aMinDeriv = isMaxU ? theVDerivMax : theUDerivMax; + + // Stride between rows in the result array (corresponds to one step in MaxParam direction) + // For full grid (required by RationalDerivative), stride is (aMinDeriv + 1) points. + const int aRowStride = (aMinDeriv + 1) * aDimension; + + // clang-format off + // Transient coefficients array size: + // (aMaxDeriv + 1) * CacheCols for the first evaluation (along max degree parameter) + // (aMinDeriv + 1) * Dimension for the second evaluation (along min degree parameter) + NCollection_LocalArray aTransientCoeffs(std::max((aMaxDeriv + 1) * aCacheCols, (aMinDeriv + 1) * aDimension)); + // clang-format on + + // Calculate intermediate values and derivatives of bivariate polynomial along variable with + // maximal degree + PLib::EvalPolynomial(aMaxParam, + aMaxDeriv, + isMaxU ? theParamsU.Degree : theParamsV.Degree, + aCacheCols, + aPolesArray[0], + aTransientCoeffs[0]); + + // Block 0: Evaluate derivatives along variable with minimal degree for D0_max + // Produces (0,0), (0,1)...(0, aMinDeriv) + // Writes to offset 0 + PLib::EvalPolynomial(aMinParam, + aMinDeriv, + isMaxU ? theParamsV.Degree : theParamsU.Degree, + aDimension, + aTransientCoeffs[0], + theResultArray[0]); + + if (aMaxDeriv > 0) + { + // Block 1: Evaluate derivatives along variable with minimal degree for D1_max + // Writes to offset aRowStride (start of second row) + // If Rational, we need full row (up to aMinDeriv). + // If Not Rational, we can optimize: we strictly need (1,0) and (1,1). + // D1Local calls with (1,1) -> aMinDeriv=1. We need up to 1. + // D2Local calls with (2,2) -> aMinDeriv=2. We need up to 1 for mixed D2. + // So usually min(aMinDeriv, 1) is sufficient for non-rational. + const int aDeriv = theIsRational ? aMinDeriv : std::min(aMinDeriv, 1); + + PLib::EvalPolynomial(aMinParam, + aDeriv, + isMaxU ? theParamsV.Degree : theParamsU.Degree, + aDimension, + aTransientCoeffs[aCacheCols], + theResultArray[aRowStride]); + + if (aMaxDeriv > 1) + { + // Block 2: Evaluate derivatives along variable with minimal degree for D2_max + // Writes to offset 2 * aRowStride (start of third row) + // If Rational, full row. + // If Not Rational, we only need (2,0) for standard D2. + const int aDeriv2 = theIsRational ? aMinDeriv : 0; + + if (aDeriv2 == 0) + { + PLib::NoDerivativeEvalPolynomial(aMinParam, + isMaxU ? theParamsV.Degree : theParamsU.Degree, + aDimension, + (isMaxU ? theParamsV.Degree : theParamsU.Degree) + * aDimension, + aTransientCoeffs[aCacheCols << 1], + theResultArray[aRowStride << 1]); + } + else + { + PLib::EvalPolynomial(aMinParam, + aDeriv2, + isMaxU ? theParamsV.Degree : theParamsU.Degree, + aDimension, + aTransientCoeffs[aCacheCols << 1], + theResultArray[aRowStride << 1]); + } + } + } + + if (theIsRational) + { + // RationalDerivative is NOT safe for in-place operation because it reads 4-component data + // and writes 3-component data to potentially overlapping memory locations. + // We need a separate temporary storage for the output. + const int aResultSize = (theUDerivMax + 1) * (theVDerivMax + 1) * 3; + NCollection_LocalArray aTempStorage(aResultSize); + + if (isMaxU) + { + BSplSLib::RationalDerivative(theUDerivMax, + theVDerivMax, + theUDerivMax, + theVDerivMax, + theResultArray[0], + aTempStorage[0]); + } + else + { + // If V is max degree, our result array is V-major (transposed relative to what + // RationalDerivative expects) We swap U/V arguments to trick RationalDerivative into + // processing it correctly. + BSplSLib::RationalDerivative(theVDerivMax, + theUDerivMax, + theVDerivMax, + theUDerivMax, + theResultArray[0], + aTempStorage[0]); + } + + // Copy results back to the output array + for (int i = 0; i < aResultSize; ++i) + { + theResultArray[i] = aTempStorage[i]; + } + } +} +} // namespace + +//================================================================================================== + BSplSLib_Cache::BSplSLib_Cache(const Standard_Integer& theDegreeU, const Standard_Boolean& thePeriodicU, const TColStd_Array1OfReal& theFlatKnotsU, @@ -45,12 +245,16 @@ BSplSLib_Cache::BSplSLib_Cache(const Standard_Integer& theDegreeU, myPolesWeights = new TColStd_HArray2OfReal(1, aMaxDegree + 1, 1, aPWColNumber * (aMinDegree + 1)); } +//================================================================================================== + Standard_Boolean BSplSLib_Cache::IsCacheValid(Standard_Real theParameterU, Standard_Real theParameterV) const { return myParamsU.IsCacheValid(theParameterU) && myParamsV.IsCacheValid(theParameterV); } +//================================================================================================== + void BSplSLib_Cache::BuildCache(const Standard_Real& theParameterU, const Standard_Real& theParameterV, const TColStd_Array1OfReal& theFlatKnotsU, @@ -91,167 +295,235 @@ void BSplSLib_Cache::BuildCache(const Standard_Real& theParameterU, myPolesWeights->ChangeArray2()); } +//================================================================================================== + void BSplSLib_Cache::D0(const Standard_Real& theU, const Standard_Real& theV, gp_Pnt& thePoint) const { - Standard_Real aNewU = myParamsU.PeriodicNormalization(theU); - Standard_Real aNewV = myParamsV.PeriodicNormalization(theV); - - // BSplSLib uses different convention for span parameters than BSplCLib - // (Start is in the middle of the span and length is half-span), - // thus we need to amend them here - Standard_Real aSpanLengthU = 0.5 * myParamsU.SpanLength; - Standard_Real aSpanStartU = myParamsU.SpanStart + aSpanLengthU; - Standard_Real aSpanLengthV = 0.5 * myParamsV.SpanLength; - Standard_Real aSpanStartV = myParamsV.SpanStart + aSpanLengthV; + const auto [aLocalU, aLocalV] = toLocalParamsD0(theU, theV, myParamsU, myParamsV); + D0Local(aLocalU, aLocalV, thePoint); +} - aNewU = (aNewU - aSpanStartU) / aSpanLengthU; - aNewV = (aNewV - aSpanStartV) / aSpanLengthV; +//================================================================================================== +void BSplSLib_Cache::D0Local(double theLocalU, double theLocalV, gp_Pnt& thePoint) const +{ Standard_Real* aPolesArray = ConvertArray(myPolesWeights); - Standard_Real aPoint[4]; + Standard_Real aPoint[4] = {}; - Standard_Integer aDimension = myIsRational ? 4 : 3; - Standard_Integer aCacheCols = myPolesWeights->RowLength(); - Standard_Integer aMinMaxDegree[2] = {std::min(myParamsU.Degree, myParamsV.Degree), - std::max(myParamsU.Degree, myParamsV.Degree)}; - Standard_Real aParameters[2]; - if (myParamsU.Degree > myParamsV.Degree) - { - aParameters[0] = aNewV; - aParameters[1] = aNewU; - } - else - { - aParameters[0] = aNewU; - aParameters[1] = aNewV; - } + const int aDimension = myIsRational ? 4 : 3; + const int aCacheCols = myPolesWeights->RowLength(); + const bool isMaxU = (myParamsU.Degree > myParamsV.Degree); + const auto [aMinDegree, aMaxDegree] = std::minmax(myParamsU.Degree, myParamsV.Degree); + const auto [aMinParam, aMaxParam] = + isMaxU ? std::make_pair(theLocalV, theLocalU) : std::make_pair(theLocalU, theLocalV); - // clang-format off - NCollection_LocalArray aTransientCoeffs(aCacheCols); // array for intermediate results - // clang-format on + // Array for intermediate results + NCollection_LocalArray aTransientCoeffs(aCacheCols); - // Calculate intermediate value of cached polynomial along columns - PLib::NoDerivativeEvalPolynomial(aParameters[1], - aMinMaxDegree[1], + // Calculate intermediate value of cached polynomial along variable with maximal degree + PLib::NoDerivativeEvalPolynomial(aMaxParam, + aMaxDegree, aCacheCols, - aMinMaxDegree[1] * aCacheCols, + aMaxDegree * aCacheCols, aPolesArray[0], aTransientCoeffs[0]); - // Calculate total value - PLib::NoDerivativeEvalPolynomial(aParameters[0], - aMinMaxDegree[0], + // Calculate total value along variable with minimal degree + PLib::NoDerivativeEvalPolynomial(aMinParam, + aMinDegree, aDimension, - aDimension * aMinMaxDegree[0], + aDimension * aMinDegree, aTransientCoeffs[0], aPoint[0]); thePoint.SetCoord(aPoint[0], aPoint[1], aPoint[2]); if (myIsRational) + { thePoint.ChangeCoord().Divide(aPoint[3]); + } } -void BSplSLib_Cache::D1(const Standard_Real& theU, - const Standard_Real& theV, - gp_Pnt& thePoint, - gp_Vec& theTangentU, - gp_Vec& theTangentV) const -{ - Standard_Real aNewU = myParamsU.PeriodicNormalization(theU); - Standard_Real aNewV = myParamsV.PeriodicNormalization(theV); - - // BSplSLib uses different convention for span parameters than BSplCLib - // (Start is in the middle of the span and length is half-span), - // thus we need to amend them here - Standard_Real aSpanLengthU = 0.5 * myParamsU.SpanLength; - Standard_Real aSpanStartU = myParamsU.SpanStart + aSpanLengthU; - Standard_Real aSpanLengthV = 0.5 * myParamsV.SpanLength; - Standard_Real aSpanStartV = myParamsV.SpanStart + aSpanLengthV; +//================================================================================================== - Standard_Real anInvU = 1.0 / aSpanLengthU; - Standard_Real anInvV = 1.0 / aSpanLengthV; - aNewU = (aNewU - aSpanStartU) * anInvU; - aNewV = (aNewV - aSpanStartV) * anInvV; +void BSplSLib_Cache::D1Local(double theLocalU, + double theLocalV, + gp_Pnt& thePoint, + gp_Vec& theTangentU, + gp_Vec& theTangentV) const +{ + Standard_Real aPntDeriv[16] = {}; // Result storage for D1, zero-initialized + EvaluatePolynomials(myPolesWeights, + myParamsU, + myParamsV, + myIsRational, + theLocalU, + theLocalV, + 1, + 1, + aPntDeriv); + + // After RationalDerivative (for rational surfaces), the output dimension is 3 (not 4) + // because weights have been processed out + const Standard_Integer aDimension = 3; + + thePoint.SetCoord(aPntDeriv[0], aPntDeriv[1], aPntDeriv[2]); + + // Tangents are stored after the point coordinates + // Order depends on which parameter has higher degree + // If U degree > V degree: layout is [P, DV, DU, ...] + // If V degree >= U degree: layout is [P, DU, DV, ...] + if (myParamsU.Degree > myParamsV.Degree) + { + theTangentV.SetCoord(aPntDeriv[aDimension], + aPntDeriv[aDimension + 1], + aPntDeriv[aDimension + 2]); + theTangentU.SetCoord(aPntDeriv[aDimension << 1], + aPntDeriv[(aDimension << 1) + 1], + aPntDeriv[(aDimension << 1) + 2]); + } + else + { + theTangentU.SetCoord(aPntDeriv[aDimension], + aPntDeriv[aDimension + 1], + aPntDeriv[aDimension + 2]); + theTangentV.SetCoord(aPntDeriv[aDimension << 1], + aPntDeriv[(aDimension << 1) + 1], + aPntDeriv[(aDimension << 1) + 2]); + } - Standard_Real* aPolesArray = ConvertArray(myPolesWeights); - Standard_Real aPntDeriv[16]; // result storage (point and derivative coordinates) - for (Standard_Integer i = 0; i < 16; i++) - aPntDeriv[i] = 0.0; + // Use direct division for better numerical stability with very small span lengths + const Standard_Real aSpanLengthU = 0.5 * myParamsU.SpanLength; + const Standard_Real aSpanLengthV = 0.5 * myParamsV.SpanLength; + theTangentU.Divide(aSpanLengthU); + theTangentV.Divide(aSpanLengthV); +} - Standard_Integer aDimension = myIsRational ? 4 : 3; - Standard_Integer aCacheCols = myPolesWeights->RowLength(); - Standard_Integer aMinMaxDegree[2] = {std::min(myParamsU.Degree, myParamsV.Degree), - std::max(myParamsU.Degree, myParamsV.Degree)}; +//================================================================================================== - Standard_Real aParameters[2]; +void BSplSLib_Cache::D2Local(double theLocalU, + double theLocalV, + gp_Pnt& thePoint, + gp_Vec& theTangentU, + gp_Vec& theTangentV, + gp_Vec& theCurvatureU, + gp_Vec& theCurvatureV, + gp_Vec& theCurvatureUV) const +{ + Standard_Real aPntDeriv[36] = {}; // Result storage for D2, zero-initialized + EvaluatePolynomials(myPolesWeights, + myParamsU, + myParamsV, + myIsRational, + theLocalU, + theLocalV, + 2, + 2, + aPntDeriv); + + // After RationalDerivative (for rational surfaces), the output dimension is 3 (not 4) + // because weights have been processed out + const Standard_Integer aDimension = 3; + const Standard_Integer aShift = aDimension; // Shift for first derivatives + const Standard_Integer aShift2 = aDimension << 1; + const Standard_Integer aShift3 = aShift2 + aDimension; + const Standard_Integer aShift4 = aShift3 + aDimension; + const Standard_Integer aShift6 = 6 * aDimension; + + thePoint.SetCoord(aPntDeriv[0], aPntDeriv[1], aPntDeriv[2]); + + // Derivatives are stored consecutively + // If Max=U (U degree > V degree): + // [0]=P, [Dim]=DV, [2Dim]=DVV + // [3Dim]=DU, [4Dim]=DUV + // [6Dim]=DUU if (myParamsU.Degree > myParamsV.Degree) { - aParameters[0] = aNewV; - aParameters[1] = aNewU; + theTangentV.SetCoord(aPntDeriv[aShift], aPntDeriv[aShift + 1], aPntDeriv[aShift + 2]); + theCurvatureV.SetCoord(aPntDeriv[aShift2], aPntDeriv[aShift2 + 1], aPntDeriv[aShift2 + 2]); + theTangentU.SetCoord(aPntDeriv[aShift3], aPntDeriv[aShift3 + 1], aPntDeriv[aShift3 + 2]); + theCurvatureUV.SetCoord(aPntDeriv[aShift4], aPntDeriv[aShift4 + 1], aPntDeriv[aShift4 + 2]); + theCurvatureU.SetCoord(aPntDeriv[aShift6], aPntDeriv[aShift6 + 1], aPntDeriv[aShift6 + 2]); } else { - aParameters[0] = aNewU; - aParameters[1] = aNewV; + // If Max=V (V degree >= U degree): + // [0]=P, [Dim]=DU, [2Dim]=DUU + // [3Dim]=DV, [4Dim]=DUV (DVU is symmetric) + // [6Dim]=DVV + theTangentU.SetCoord(aPntDeriv[aShift], aPntDeriv[aShift + 1], aPntDeriv[aShift + 2]); + theCurvatureU.SetCoord(aPntDeriv[aShift2], aPntDeriv[aShift2 + 1], aPntDeriv[aShift2 + 2]); + theTangentV.SetCoord(aPntDeriv[aShift3], aPntDeriv[aShift3 + 1], aPntDeriv[aShift3 + 2]); + theCurvatureUV.SetCoord(aPntDeriv[aShift4], aPntDeriv[aShift4 + 1], aPntDeriv[aShift4 + 2]); + theCurvatureV.SetCoord(aPntDeriv[aShift6], aPntDeriv[aShift6 + 1], aPntDeriv[aShift6 + 2]); } - // clang-format off - NCollection_LocalArray aTransientCoeffs(aCacheCols<<1); // array for intermediate results - // clang-format on - - // Calculate intermediate values and derivatives of bivariate polynomial along variable with - // maximal degree - PLib::EvalPolynomial(aParameters[1], - 1, - aMinMaxDegree[1], - aCacheCols, - aPolesArray[0], - aTransientCoeffs[0]); - - // Calculate a point on surface and a derivative along variable with minimal degree - PLib::EvalPolynomial(aParameters[0], - 1, - aMinMaxDegree[0], - aDimension, - aTransientCoeffs[0], - aPntDeriv[0]); + // Use direct division for better numerical stability with very small span lengths + const Standard_Real aSpanLengthU = 0.5 * myParamsU.SpanLength; + const Standard_Real aSpanLengthV = 0.5 * myParamsV.SpanLength; + const Standard_Real aSpanLengthU2 = aSpanLengthU * aSpanLengthU; + const Standard_Real aSpanLengthV2 = aSpanLengthV * aSpanLengthV; + theTangentU.Divide(aSpanLengthU); + theTangentV.Divide(aSpanLengthV); + theCurvatureU.Divide(aSpanLengthU2); + theCurvatureV.Divide(aSpanLengthV2); + theCurvatureUV.Divide(aSpanLengthU * aSpanLengthV); +} - // Calculate derivative along variable with maximal degree - PLib::NoDerivativeEvalPolynomial(aParameters[0], - aMinMaxDegree[0], - aDimension, - aMinMaxDegree[0] * aDimension, - aTransientCoeffs[aCacheCols], - aPntDeriv[aDimension << 1]); +//================================================================================================== - Standard_Real* aResult = aPntDeriv; - Standard_Real aTempStorage[12]; - if (myIsRational) // calculate derivatives divided by weight's derivatives - { - BSplSLib::RationalDerivative(1, 1, 1, 1, aPntDeriv[0], aTempStorage[0]); - aResult = aTempStorage; - aDimension--; - } +void BSplSLib_Cache::D1(const Standard_Real& theU, + const Standard_Real& theV, + gp_Pnt& thePoint, + gp_Vec& theTangentU, + gp_Vec& theTangentV) const +{ + // Use the same inverse values for both parameter transformation and derivative scaling + // to maintain numerical consistency with the original implementation + double anInvU = 0.0, anInvV = 0.0; + const auto [aLocalU, aLocalV] = toLocalParams(theU, theV, myParamsU, myParamsV, anInvU, anInvV); + + Standard_Real aPntDeriv[16] = {}; + EvaluatePolynomials(myPolesWeights, + myParamsU, + myParamsV, + myIsRational, + aLocalU, + aLocalV, + 1, + 1, + aPntDeriv); + + const Standard_Integer aDimension = 3; + thePoint.SetCoord(aPntDeriv[0], aPntDeriv[1], aPntDeriv[2]); - thePoint.SetCoord(aResult[0], aResult[1], aResult[2]); if (myParamsU.Degree > myParamsV.Degree) { - theTangentV.SetCoord(aResult[aDimension], aResult[aDimension + 1], aResult[aDimension + 2]); - Standard_Integer aShift = aDimension << 1; - theTangentU.SetCoord(aResult[aShift], aResult[aShift + 1], aResult[aShift + 2]); + theTangentV.SetCoord(aPntDeriv[aDimension], + aPntDeriv[aDimension + 1], + aPntDeriv[aDimension + 2]); + theTangentU.SetCoord(aPntDeriv[aDimension << 1], + aPntDeriv[(aDimension << 1) + 1], + aPntDeriv[(aDimension << 1) + 2]); } else { - theTangentU.SetCoord(aResult[aDimension], aResult[aDimension + 1], aResult[aDimension + 2]); - Standard_Integer aShift = aDimension << 1; - theTangentV.SetCoord(aResult[aShift], aResult[aShift + 1], aResult[aShift + 2]); + theTangentU.SetCoord(aPntDeriv[aDimension], + aPntDeriv[aDimension + 1], + aPntDeriv[aDimension + 2]); + theTangentV.SetCoord(aPntDeriv[aDimension << 1], + aPntDeriv[(aDimension << 1) + 1], + aPntDeriv[(aDimension << 1) + 2]); } + + // Scale derivatives using the same inverse values used for parameter transformation theTangentU.Multiply(anInvU); theTangentV.Multiply(anInvV); } +//================================================================================================== + void BSplSLib_Cache::D2(const Standard_Real& theU, const Standard_Real& theV, gp_Pnt& thePoint, @@ -261,124 +533,49 @@ void BSplSLib_Cache::D2(const Standard_Real& theU, gp_Vec& theCurvatureV, gp_Vec& theCurvatureUV) const { - Standard_Real aNewU = myParamsU.PeriodicNormalization(theU); - Standard_Real aNewV = myParamsV.PeriodicNormalization(theV); - - // BSplSLib uses different convention for span parameters than BSplCLib - // (Start is in the middle of the span and length is half-span), - // thus we need to amend them here - Standard_Real aSpanLengthU = 0.5 * myParamsU.SpanLength; - Standard_Real aSpanStartU = myParamsU.SpanStart + aSpanLengthU; - Standard_Real aSpanLengthV = 0.5 * myParamsV.SpanLength; - Standard_Real aSpanStartV = myParamsV.SpanStart + aSpanLengthV; - - Standard_Real anInvU = 1.0 / aSpanLengthU; - Standard_Real anInvV = 1.0 / aSpanLengthV; - aNewU = (aNewU - aSpanStartU) * anInvU; - aNewV = (aNewV - aSpanStartV) * anInvV; - - Standard_Real* aPolesArray = ConvertArray(myPolesWeights); - Standard_Real aPntDeriv[36]; // result storage (point and derivative coordinates) - for (Standard_Integer i = 0; i < 36; i++) - aPntDeriv[i] = 0.0; - - Standard_Integer aDimension = myIsRational ? 4 : 3; - Standard_Integer aCacheCols = myPolesWeights->RowLength(); - Standard_Integer aMinMaxDegree[2] = {std::min(myParamsU.Degree, myParamsV.Degree), - std::max(myParamsU.Degree, myParamsV.Degree)}; + // Use the same inverse values for both parameter transformation and derivative scaling + // to maintain numerical consistency with the original implementation + double anInvU = 0.0, anInvV = 0.0; + const auto [aLocalU, aLocalV] = toLocalParams(theU, theV, myParamsU, myParamsV, anInvU, anInvV); + + Standard_Real aPntDeriv[36] = {}; + EvaluatePolynomials(myPolesWeights, + myParamsU, + myParamsV, + myIsRational, + aLocalU, + aLocalV, + 2, + 2, + aPntDeriv); + + const Standard_Integer aDimension = 3; + const Standard_Integer aShift = aDimension; + const Standard_Integer aShift2 = aDimension << 1; + const Standard_Integer aShift3 = aShift2 + aDimension; + const Standard_Integer aShift4 = aShift3 + aDimension; + const Standard_Integer aShift6 = 6 * aDimension; + + thePoint.SetCoord(aPntDeriv[0], aPntDeriv[1], aPntDeriv[2]); - Standard_Real aParameters[2]; if (myParamsU.Degree > myParamsV.Degree) { - aParameters[0] = aNewV; - aParameters[1] = aNewU; + theTangentV.SetCoord(aPntDeriv[aShift], aPntDeriv[aShift + 1], aPntDeriv[aShift + 2]); + theCurvatureV.SetCoord(aPntDeriv[aShift2], aPntDeriv[aShift2 + 1], aPntDeriv[aShift2 + 2]); + theTangentU.SetCoord(aPntDeriv[aShift3], aPntDeriv[aShift3 + 1], aPntDeriv[aShift3 + 2]); + theCurvatureUV.SetCoord(aPntDeriv[aShift4], aPntDeriv[aShift4 + 1], aPntDeriv[aShift4 + 2]); + theCurvatureU.SetCoord(aPntDeriv[aShift6], aPntDeriv[aShift6 + 1], aPntDeriv[aShift6 + 2]); } else { - aParameters[0] = aNewU; - aParameters[1] = aNewV; - } - - // clang-format off - NCollection_LocalArray aTransientCoeffs(3 * aCacheCols); // array for intermediate results - // Calculating derivative to be evaluate and - // nulling transient coefficients when max or min derivative is less than 2 - // clang-format on - Standard_Integer aMinMaxDeriv[2] = {std::min(2, aMinMaxDegree[0]), std::min(2, aMinMaxDegree[1])}; - for (Standard_Integer i = aMinMaxDeriv[1] + 1; i < 3; i++) - { - Standard_Integer index = i * aCacheCols; - for (Standard_Integer j = 0; j < aCacheCols; j++) - aTransientCoeffs[index++] = 0.0; - } - - // Calculate intermediate values and derivatives of bivariate polynomial along variable with - // maximal degree - PLib::EvalPolynomial(aParameters[1], - aMinMaxDeriv[1], - aMinMaxDegree[1], - aCacheCols, - aPolesArray[0], - aTransientCoeffs[0]); - - // Calculate a point on surface and a derivatives along variable with minimal degree - PLib::EvalPolynomial(aParameters[0], - aMinMaxDeriv[0], - aMinMaxDegree[0], - aDimension, - aTransientCoeffs[0], - aPntDeriv[0]); - - // Calculate derivative along variable with maximal degree and mixed derivative - PLib::EvalPolynomial(aParameters[0], - 1, - aMinMaxDegree[0], - aDimension, - aTransientCoeffs[aCacheCols], - aPntDeriv[3 * aDimension]); - - // Calculate second derivative along variable with maximal degree - PLib::NoDerivativeEvalPolynomial(aParameters[0], - aMinMaxDegree[0], - aDimension, - aMinMaxDegree[0] * aDimension, - aTransientCoeffs[aCacheCols << 1], - aPntDeriv[6 * aDimension]); - - Standard_Real* aResult = aPntDeriv; - Standard_Real aTempStorage[36]; - if (myIsRational) // calculate derivatives divided by weight's derivatives - { - BSplSLib::RationalDerivative(2, 2, 2, 2, aPntDeriv[0], aTempStorage[0]); - aResult = aTempStorage; - aDimension--; + theTangentU.SetCoord(aPntDeriv[aShift], aPntDeriv[aShift + 1], aPntDeriv[aShift + 2]); + theCurvatureU.SetCoord(aPntDeriv[aShift2], aPntDeriv[aShift2 + 1], aPntDeriv[aShift2 + 2]); + theTangentV.SetCoord(aPntDeriv[aShift3], aPntDeriv[aShift3 + 1], aPntDeriv[aShift3 + 2]); + theCurvatureUV.SetCoord(aPntDeriv[aShift4], aPntDeriv[aShift4 + 1], aPntDeriv[aShift4 + 2]); + theCurvatureV.SetCoord(aPntDeriv[aShift6], aPntDeriv[aShift6 + 1], aPntDeriv[aShift6 + 2]); } - thePoint.SetCoord(aResult[0], aResult[1], aResult[2]); - if (myParamsU.Degree > myParamsV.Degree) - { - theTangentV.SetCoord(aResult[aDimension], aResult[aDimension + 1], aResult[aDimension + 2]); - Standard_Integer aShift = aDimension << 1; - theCurvatureV.SetCoord(aResult[aShift], aResult[aShift + 1], aResult[aShift + 2]); - aShift += aDimension; - theTangentU.SetCoord(aResult[aShift], aResult[aShift + 1], aResult[aShift + 2]); - aShift += aDimension; - theCurvatureUV.SetCoord(aResult[aShift], aResult[aShift + 1], aResult[aShift + 2]); - aShift += (aDimension << 1); - theCurvatureU.SetCoord(aResult[aShift], aResult[aShift + 1], aResult[aShift + 2]); - } - else - { - theTangentU.SetCoord(aResult[aDimension], aResult[aDimension + 1], aResult[aDimension + 2]); - Standard_Integer aShift = aDimension << 1; - theCurvatureU.SetCoord(aResult[aShift], aResult[aShift + 1], aResult[aShift + 2]); - aShift += aDimension; - theTangentV.SetCoord(aResult[aShift], aResult[aShift + 1], aResult[aShift + 2]); - aShift += aDimension; - theCurvatureUV.SetCoord(aResult[aShift], aResult[aShift + 1], aResult[aShift + 2]); - aShift += (aDimension << 1); - theCurvatureV.SetCoord(aResult[aShift], aResult[aShift + 1], aResult[aShift + 2]); - } + // Scale derivatives using the same inverse values used for parameter transformation theTangentU.Multiply(anInvU); theTangentV.Multiply(anInvV); theCurvatureU.Multiply(anInvU * anInvU); diff --git a/src/FoundationClasses/TKMath/BSplSLib/BSplSLib_Cache.hxx b/src/FoundationClasses/TKMath/BSplSLib/BSplSLib_Cache.hxx index fb90f6fdfd..93aa6210ce 100644 --- a/src/FoundationClasses/TKMath/BSplSLib/BSplSLib_Cache.hxx +++ b/src/FoundationClasses/TKMath/BSplSLib/BSplSLib_Cache.hxx @@ -104,12 +104,51 @@ public: gp_Vec& theCurvatureV, gp_Vec& theCurvatureUV) const; + //! Calculates the point using pre-computed local parameters in [-1, 1] range. + //! This bypasses periodic normalization and local parameter calculation. + //! @param[in] theLocalU pre-computed local U parameter: (U - SpanMid) / SpanHalfLen + //! @param[in] theLocalV pre-computed local V parameter: (V - SpanMid) / SpanHalfLen + //! @param[out] thePoint the result of calculation (the point on the surface) + Standard_EXPORT void D0Local(double theLocalU, double theLocalV, gp_Pnt& thePoint) const; + + //! Calculates the point and first derivatives using pre-computed local parameters in [-1, 1] + //! range. This bypasses periodic normalization and local parameter calculation. + //! @param[in] theLocalU pre-computed local U parameter: (U - SpanMid) / SpanHalfLen + //! @param[in] theLocalV pre-computed local V parameter: (V - SpanMid) / SpanHalfLen + //! @param[out] thePoint the result of calculation (the point on the surface) + //! @param[out] theTangentU tangent vector along U axis in the calculated point + //! @param[out] theTangentV tangent vector along V axis in the calculated point + Standard_EXPORT void D1Local(double theLocalU, + double theLocalV, + gp_Pnt& thePoint, + gp_Vec& theTangentU, + gp_Vec& theTangentV) const; + + //! Calculates the point and derivatives till second order using pre-computed local parameters. + //! This bypasses periodic normalization and local parameter calculation. + //! @param[in] theLocalU pre-computed local U parameter: (U - SpanMid) / SpanHalfLen + //! @param[in] theLocalV pre-computed local V parameter: (V - SpanMid) / SpanHalfLen + //! @param[out] thePoint the result of calculation (the point on the surface) + //! @param[out] theTangentU tangent vector along U axis in the calculated point + //! @param[out] theTangentV tangent vector along V axis in the calculated point + //! @param[out] theCurvatureU curvature vector (2nd derivative on U) along U axis + //! @param[out] theCurvatureV curvature vector (2nd derivative on V) along V axis + //! @param[out] theCurvatureUV 2nd mixed derivative on U and V + Standard_EXPORT void D2Local(double theLocalU, + double theLocalV, + gp_Pnt& thePoint, + gp_Vec& theTangentU, + gp_Vec& theTangentV, + gp_Vec& theCurvatureU, + gp_Vec& theCurvatureV, + gp_Vec& theCurvatureUV) const; + DEFINE_STANDARD_RTTIEXT(BSplSLib_Cache, Standard_Transient) private: // copying is prohibited - BSplSLib_Cache(const BSplSLib_Cache&); - void operator=(const BSplSLib_Cache&); + BSplSLib_Cache(const BSplSLib_Cache&) = delete; + void operator=(const BSplSLib_Cache&) = delete; private: // clang-format off diff --git a/src/FoundationClasses/TKMath/GTests/BSplCLib_Cache_Test.cxx b/src/FoundationClasses/TKMath/GTests/BSplCLib_Cache_Test.cxx new file mode 100644 index 0000000000..9553f5b821 --- /dev/null +++ b/src/FoundationClasses/TKMath/GTests/BSplCLib_Cache_Test.cxx @@ -0,0 +1,517 @@ +// Copyright (c) 2025 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. + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace +{ +constexpr double THE_TOLERANCE = 1e-10; +} + +//================================================================================================== +// Test fixture for BSplCLib_Cache tests +//================================================================================================== + +class BSplCLib_CacheTest : public ::testing::Test +{ +protected: + void SetUp() override {} + + //! Creates flat knots array from knots and multiplicities + void createFlatKnots(const TColStd_Array1OfReal& theKnots, + const TColStd_Array1OfInteger& theMults, + TColStd_Array1OfReal& theFlatKnots) const + { + int aFlatIndex = theFlatKnots.Lower(); + for (int i = theKnots.Lower(); i <= theKnots.Upper(); ++i) + { + for (int j = 0; j < theMults(i); ++j) + { + theFlatKnots(aFlatIndex++) = theKnots(i); + } + } + } +}; + +//================================================================================================== +// Non-rational 3D curve tests +//================================================================================================== + +TEST_F(BSplCLib_CacheTest, D0_NonRationalCurve3D) +{ + // Create a cubic Bezier curve + TColgp_Array1OfPnt aPoles(1, 4); + aPoles(1) = gp_Pnt(0, 0, 0); + aPoles(2) = gp_Pnt(1, 2, 0); + aPoles(3) = gp_Pnt(2, 2, 0); + aPoles(4) = gp_Pnt(3, 0, 0); + + TColStd_Array1OfReal aKnots(1, 2); + aKnots(1) = 0.0; + aKnots(2) = 1.0; + + TColStd_Array1OfInteger aMults(1, 2); + aMults(1) = 4; + aMults(2) = 4; + + TColStd_Array1OfReal aFlatKnots(1, 8); + createFlatKnots(aKnots, aMults, aFlatKnots); + + const int aDegree = 3; + + // Create cache + Handle(BSplCLib_Cache) aCache = new BSplCLib_Cache(aDegree, false, aFlatKnots, aPoles, nullptr); + aCache->BuildCache(0.5, aFlatKnots, aPoles, nullptr); + + // Test at several parameter values + for (double u = 0.0; u <= 1.0; u += 0.1) + { + gp_Pnt aCachePnt, aDirectPnt; + + // Cached evaluation + if (!aCache->IsCacheValid(u)) + { + aCache->BuildCache(u, aFlatKnots, aPoles, nullptr); + } + aCache->D0(u, aCachePnt); + + // Direct evaluation using BSplCLib::D0 + BSplCLib::D0(u, 0, aDegree, false, aPoles, nullptr, aKnots, &aMults, aDirectPnt); + + // Compare + EXPECT_NEAR(aCachePnt.X(), aDirectPnt.X(), THE_TOLERANCE) << "D0 X mismatch at u=" << u; + EXPECT_NEAR(aCachePnt.Y(), aDirectPnt.Y(), THE_TOLERANCE) << "D0 Y mismatch at u=" << u; + EXPECT_NEAR(aCachePnt.Z(), aDirectPnt.Z(), THE_TOLERANCE) << "D0 Z mismatch at u=" << u; + } +} + +TEST_F(BSplCLib_CacheTest, D1_NonRationalCurve3D) +{ + // Create a cubic Bezier curve + TColgp_Array1OfPnt aPoles(1, 4); + aPoles(1) = gp_Pnt(0, 0, 0); + aPoles(2) = gp_Pnt(1, 2, 1); + aPoles(3) = gp_Pnt(2, 2, 1); + aPoles(4) = gp_Pnt(3, 0, 0); + + TColStd_Array1OfReal aKnots(1, 2); + aKnots(1) = 0.0; + aKnots(2) = 1.0; + + TColStd_Array1OfInteger aMults(1, 2); + aMults(1) = 4; + aMults(2) = 4; + + TColStd_Array1OfReal aFlatKnots(1, 8); + createFlatKnots(aKnots, aMults, aFlatKnots); + + const int aDegree = 3; + + // Create cache + Handle(BSplCLib_Cache) aCache = new BSplCLib_Cache(aDegree, false, aFlatKnots, aPoles, nullptr); + aCache->BuildCache(0.5, aFlatKnots, aPoles, nullptr); + + // Test at several parameter values + for (double u = 0.0; u <= 1.0; u += 0.1) + { + gp_Pnt aCachePnt, aDirectPnt; + gp_Vec aCacheTan, aDirectTan; + + // Cached evaluation + if (!aCache->IsCacheValid(u)) + { + aCache->BuildCache(u, aFlatKnots, aPoles, nullptr); + } + aCache->D1(u, aCachePnt, aCacheTan); + + // Direct evaluation using BSplCLib::D1 + BSplCLib::D1(u, 0, aDegree, false, aPoles, nullptr, aKnots, &aMults, aDirectPnt, aDirectTan); + + // Compare point + EXPECT_NEAR(aCachePnt.X(), aDirectPnt.X(), THE_TOLERANCE) << "D1 point X mismatch at u=" << u; + EXPECT_NEAR(aCachePnt.Y(), aDirectPnt.Y(), THE_TOLERANCE) << "D1 point Y mismatch at u=" << u; + EXPECT_NEAR(aCachePnt.Z(), aDirectPnt.Z(), THE_TOLERANCE) << "D1 point Z mismatch at u=" << u; + + // Compare tangent + EXPECT_NEAR(aCacheTan.X(), aDirectTan.X(), THE_TOLERANCE) << "D1 tangent X mismatch at u=" << u; + EXPECT_NEAR(aCacheTan.Y(), aDirectTan.Y(), THE_TOLERANCE) << "D1 tangent Y mismatch at u=" << u; + EXPECT_NEAR(aCacheTan.Z(), aDirectTan.Z(), THE_TOLERANCE) << "D1 tangent Z mismatch at u=" << u; + } +} + +TEST_F(BSplCLib_CacheTest, D2_NonRationalCurve3D) +{ + // Create a cubic Bezier curve + TColgp_Array1OfPnt aPoles(1, 4); + aPoles(1) = gp_Pnt(0, 0, 0); + aPoles(2) = gp_Pnt(1, 2, 1); + aPoles(3) = gp_Pnt(2, 2, 1); + aPoles(4) = gp_Pnt(3, 0, 0); + + TColStd_Array1OfReal aKnots(1, 2); + aKnots(1) = 0.0; + aKnots(2) = 1.0; + + TColStd_Array1OfInteger aMults(1, 2); + aMults(1) = 4; + aMults(2) = 4; + + TColStd_Array1OfReal aFlatKnots(1, 8); + createFlatKnots(aKnots, aMults, aFlatKnots); + + const int aDegree = 3; + + // Create cache + Handle(BSplCLib_Cache) aCache = new BSplCLib_Cache(aDegree, false, aFlatKnots, aPoles, nullptr); + aCache->BuildCache(0.5, aFlatKnots, aPoles, nullptr); + + // Test at several parameter values + for (double u = 0.0; u <= 1.0; u += 0.1) + { + gp_Pnt aCachePnt, aDirectPnt; + gp_Vec aCacheTan, aDirectTan; + gp_Vec aCacheCurv, aDirectCurv; + + // Cached evaluation + if (!aCache->IsCacheValid(u)) + { + aCache->BuildCache(u, aFlatKnots, aPoles, nullptr); + } + aCache->D2(u, aCachePnt, aCacheTan, aCacheCurv); + + // Direct evaluation using BSplCLib::D2 + BSplCLib::D2(u, + 0, + aDegree, + false, + aPoles, + nullptr, + aKnots, + &aMults, + aDirectPnt, + aDirectTan, + aDirectCurv); + + // Compare point + EXPECT_NEAR(aCachePnt.X(), aDirectPnt.X(), THE_TOLERANCE) << "D2 point X mismatch at u=" << u; + EXPECT_NEAR(aCachePnt.Y(), aDirectPnt.Y(), THE_TOLERANCE) << "D2 point Y mismatch at u=" << u; + EXPECT_NEAR(aCachePnt.Z(), aDirectPnt.Z(), THE_TOLERANCE) << "D2 point Z mismatch at u=" << u; + + // Compare tangent + EXPECT_NEAR(aCacheTan.X(), aDirectTan.X(), THE_TOLERANCE) << "D2 tangent X mismatch at u=" << u; + EXPECT_NEAR(aCacheTan.Y(), aDirectTan.Y(), THE_TOLERANCE) << "D2 tangent Y mismatch at u=" << u; + EXPECT_NEAR(aCacheTan.Z(), aDirectTan.Z(), THE_TOLERANCE) << "D2 tangent Z mismatch at u=" << u; + + // Compare curvature + EXPECT_NEAR(aCacheCurv.X(), aDirectCurv.X(), THE_TOLERANCE) + << "D2 curvature X mismatch at u=" << u; + EXPECT_NEAR(aCacheCurv.Y(), aDirectCurv.Y(), THE_TOLERANCE) + << "D2 curvature Y mismatch at u=" << u; + EXPECT_NEAR(aCacheCurv.Z(), aDirectCurv.Z(), THE_TOLERANCE) + << "D2 curvature Z mismatch at u=" << u; + } +} + +//================================================================================================== +// Rational 3D curve tests +//================================================================================================== + +TEST_F(BSplCLib_CacheTest, D0_RationalCurve3D) +{ + // Create a rational quadratic Bezier curve (can represent a circle arc) + TColgp_Array1OfPnt aPoles(1, 3); + aPoles(1) = gp_Pnt(1, 0, 0); + aPoles(2) = gp_Pnt(1, 1, 0); + aPoles(3) = gp_Pnt(0, 1, 0); + + TColStd_Array1OfReal aWeights(1, 3); + aWeights(1) = 1.0; + aWeights(2) = 0.707106781186548; // sqrt(2)/2 + aWeights(3) = 1.0; + + TColStd_Array1OfReal aKnots(1, 2); + aKnots(1) = 0.0; + aKnots(2) = 1.0; + + TColStd_Array1OfInteger aMults(1, 2); + aMults(1) = 3; + aMults(2) = 3; + + TColStd_Array1OfReal aFlatKnots(1, 6); + createFlatKnots(aKnots, aMults, aFlatKnots); + + const int aDegree = 2; + + // Create cache + Handle(BSplCLib_Cache) aCache = new BSplCLib_Cache(aDegree, false, aFlatKnots, aPoles, &aWeights); + aCache->BuildCache(0.5, aFlatKnots, aPoles, &aWeights); + + // Test at several parameter values + for (double u = 0.0; u <= 1.0; u += 0.1) + { + gp_Pnt aCachePnt, aDirectPnt; + + // Cached evaluation + if (!aCache->IsCacheValid(u)) + { + aCache->BuildCache(u, aFlatKnots, aPoles, &aWeights); + } + aCache->D0(u, aCachePnt); + + // Direct evaluation using BSplCLib::D0 + BSplCLib::D0(u, 0, aDegree, false, aPoles, &aWeights, aKnots, &aMults, aDirectPnt); + + // Compare + EXPECT_NEAR(aCachePnt.X(), aDirectPnt.X(), THE_TOLERANCE) + << "Rational D0 X mismatch at u=" << u; + EXPECT_NEAR(aCachePnt.Y(), aDirectPnt.Y(), THE_TOLERANCE) + << "Rational D0 Y mismatch at u=" << u; + EXPECT_NEAR(aCachePnt.Z(), aDirectPnt.Z(), THE_TOLERANCE) + << "Rational D0 Z mismatch at u=" << u; + } +} + +TEST_F(BSplCLib_CacheTest, D1_RationalCurve3D) +{ + // Create a rational quadratic Bezier curve + TColgp_Array1OfPnt aPoles(1, 3); + aPoles(1) = gp_Pnt(1, 0, 0); + aPoles(2) = gp_Pnt(1, 1, 0); + aPoles(3) = gp_Pnt(0, 1, 0); + + TColStd_Array1OfReal aWeights(1, 3); + aWeights(1) = 1.0; + aWeights(2) = 0.707106781186548; // sqrt(2)/2 + aWeights(3) = 1.0; + + TColStd_Array1OfReal aKnots(1, 2); + aKnots(1) = 0.0; + aKnots(2) = 1.0; + + TColStd_Array1OfInteger aMults(1, 2); + aMults(1) = 3; + aMults(2) = 3; + + TColStd_Array1OfReal aFlatKnots(1, 6); + createFlatKnots(aKnots, aMults, aFlatKnots); + + const int aDegree = 2; + + // Create cache + Handle(BSplCLib_Cache) aCache = new BSplCLib_Cache(aDegree, false, aFlatKnots, aPoles, &aWeights); + aCache->BuildCache(0.5, aFlatKnots, aPoles, &aWeights); + + // Test at several parameter values + for (double u = 0.0; u <= 1.0; u += 0.1) + { + gp_Pnt aCachePnt, aDirectPnt; + gp_Vec aCacheTan, aDirectTan; + + // Cached evaluation + if (!aCache->IsCacheValid(u)) + { + aCache->BuildCache(u, aFlatKnots, aPoles, &aWeights); + } + aCache->D1(u, aCachePnt, aCacheTan); + + // Direct evaluation using BSplCLib::D1 + BSplCLib::D1(u, 0, aDegree, false, aPoles, &aWeights, aKnots, &aMults, aDirectPnt, aDirectTan); + + // Compare point + EXPECT_NEAR(aCachePnt.X(), aDirectPnt.X(), THE_TOLERANCE) + << "Rational D1 point X mismatch at u=" << u; + EXPECT_NEAR(aCachePnt.Y(), aDirectPnt.Y(), THE_TOLERANCE) + << "Rational D1 point Y mismatch at u=" << u; + EXPECT_NEAR(aCachePnt.Z(), aDirectPnt.Z(), THE_TOLERANCE) + << "Rational D1 point Z mismatch at u=" << u; + + // Compare tangent + EXPECT_NEAR(aCacheTan.X(), aDirectTan.X(), THE_TOLERANCE) + << "Rational D1 tangent X mismatch at u=" << u; + EXPECT_NEAR(aCacheTan.Y(), aDirectTan.Y(), THE_TOLERANCE) + << "Rational D1 tangent Y mismatch at u=" << u; + EXPECT_NEAR(aCacheTan.Z(), aDirectTan.Z(), THE_TOLERANCE) + << "Rational D1 tangent Z mismatch at u=" << u; + } +} + +TEST_F(BSplCLib_CacheTest, D2_RationalCurve3D) +{ + // Create a rational quadratic Bezier curve + TColgp_Array1OfPnt aPoles(1, 3); + aPoles(1) = gp_Pnt(1, 0, 0); + aPoles(2) = gp_Pnt(1, 1, 0); + aPoles(3) = gp_Pnt(0, 1, 0); + + TColStd_Array1OfReal aWeights(1, 3); + aWeights(1) = 1.0; + aWeights(2) = 0.707106781186548; // sqrt(2)/2 + aWeights(3) = 1.0; + + TColStd_Array1OfReal aKnots(1, 2); + aKnots(1) = 0.0; + aKnots(2) = 1.0; + + TColStd_Array1OfInteger aMults(1, 2); + aMults(1) = 3; + aMults(2) = 3; + + TColStd_Array1OfReal aFlatKnots(1, 6); + createFlatKnots(aKnots, aMults, aFlatKnots); + + const int aDegree = 2; + + // Create cache + Handle(BSplCLib_Cache) aCache = new BSplCLib_Cache(aDegree, false, aFlatKnots, aPoles, &aWeights); + aCache->BuildCache(0.5, aFlatKnots, aPoles, &aWeights); + + // Test at several parameter values + for (double u = 0.0; u <= 1.0; u += 0.1) + { + gp_Pnt aCachePnt, aDirectPnt; + gp_Vec aCacheTan, aDirectTan; + gp_Vec aCacheCurv, aDirectCurv; + + // Cached evaluation + if (!aCache->IsCacheValid(u)) + { + aCache->BuildCache(u, aFlatKnots, aPoles, &aWeights); + } + aCache->D2(u, aCachePnt, aCacheTan, aCacheCurv); + + // Direct evaluation using BSplCLib::D2 + BSplCLib::D2(u, + 0, + aDegree, + false, + aPoles, + &aWeights, + aKnots, + &aMults, + aDirectPnt, + aDirectTan, + aDirectCurv); + + // Compare point + EXPECT_NEAR(aCachePnt.X(), aDirectPnt.X(), THE_TOLERANCE) + << "Rational D2 point X mismatch at u=" << u; + EXPECT_NEAR(aCachePnt.Y(), aDirectPnt.Y(), THE_TOLERANCE) + << "Rational D2 point Y mismatch at u=" << u; + EXPECT_NEAR(aCachePnt.Z(), aDirectPnt.Z(), THE_TOLERANCE) + << "Rational D2 point Z mismatch at u=" << u; + + // Compare tangent + EXPECT_NEAR(aCacheTan.X(), aDirectTan.X(), THE_TOLERANCE) + << "Rational D2 tangent X mismatch at u=" << u; + EXPECT_NEAR(aCacheTan.Y(), aDirectTan.Y(), THE_TOLERANCE) + << "Rational D2 tangent Y mismatch at u=" << u; + EXPECT_NEAR(aCacheTan.Z(), aDirectTan.Z(), THE_TOLERANCE) + << "Rational D2 tangent Z mismatch at u=" << u; + + // Compare curvature + EXPECT_NEAR(aCacheCurv.X(), aDirectCurv.X(), THE_TOLERANCE) + << "Rational D2 curvature X mismatch at u=" << u; + EXPECT_NEAR(aCacheCurv.Y(), aDirectCurv.Y(), THE_TOLERANCE) + << "Rational D2 curvature Y mismatch at u=" << u; + EXPECT_NEAR(aCacheCurv.Z(), aDirectCurv.Z(), THE_TOLERANCE) + << "Rational D2 curvature Z mismatch at u=" << u; + } +} + +//================================================================================================== +// Test D3 for completeness +//================================================================================================== + +TEST_F(BSplCLib_CacheTest, D3_NonRationalCurve3D) +{ + // Create a cubic Bezier curve + TColgp_Array1OfPnt aPoles(1, 4); + aPoles(1) = gp_Pnt(0, 0, 0); + aPoles(2) = gp_Pnt(1, 2, 1); + aPoles(3) = gp_Pnt(2, 2, 1); + aPoles(4) = gp_Pnt(3, 0, 0); + + TColStd_Array1OfReal aKnots(1, 2); + aKnots(1) = 0.0; + aKnots(2) = 1.0; + + TColStd_Array1OfInteger aMults(1, 2); + aMults(1) = 4; + aMults(2) = 4; + + TColStd_Array1OfReal aFlatKnots(1, 8); + createFlatKnots(aKnots, aMults, aFlatKnots); + + const int aDegree = 3; + + // Create cache + Handle(BSplCLib_Cache) aCache = new BSplCLib_Cache(aDegree, false, aFlatKnots, aPoles, nullptr); + aCache->BuildCache(0.5, aFlatKnots, aPoles, nullptr); + + // Test at several parameter values + for (double u = 0.0; u <= 1.0; u += 0.1) + { + gp_Pnt aCachePnt, aDirectPnt; + gp_Vec aCacheTan, aDirectTan; + gp_Vec aCacheCurv, aDirectCurv; + gp_Vec aCacheTors, aDirectTors; + + // Cached evaluation + if (!aCache->IsCacheValid(u)) + { + aCache->BuildCache(u, aFlatKnots, aPoles, nullptr); + } + aCache->D3(u, aCachePnt, aCacheTan, aCacheCurv, aCacheTors); + + // Direct evaluation using BSplCLib::D3 + BSplCLib::D3(u, + 0, + aDegree, + false, + aPoles, + nullptr, + aKnots, + &aMults, + aDirectPnt, + aDirectTan, + aDirectCurv, + aDirectTors); + + // Compare point + EXPECT_NEAR(aCachePnt.X(), aDirectPnt.X(), THE_TOLERANCE) << "D3 point X mismatch at u=" << u; + + // Compare tangent + EXPECT_NEAR(aCacheTan.X(), aDirectTan.X(), THE_TOLERANCE) << "D3 tangent X mismatch at u=" << u; + + // Compare curvature + EXPECT_NEAR(aCacheCurv.X(), aDirectCurv.X(), THE_TOLERANCE) + << "D3 curvature X mismatch at u=" << u; + + // Compare torsion + EXPECT_NEAR(aCacheTors.X(), aDirectTors.X(), THE_TOLERANCE) + << "D3 torsion X mismatch at u=" << u; + } +} diff --git a/src/FoundationClasses/TKMath/GTests/BSplSLib_Cache_Test.cxx b/src/FoundationClasses/TKMath/GTests/BSplSLib_Cache_Test.cxx new file mode 100644 index 0000000000..63ac411976 --- /dev/null +++ b/src/FoundationClasses/TKMath/GTests/BSplSLib_Cache_Test.cxx @@ -0,0 +1,979 @@ +// Copyright (c) 2025 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. + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +namespace +{ +constexpr double THE_TOLERANCE = 1e-10; +} + +//================================================================================================== +// Test fixture for BSplSLib_Cache tests +//================================================================================================== + +class BSplSLib_CacheTest : public ::testing::Test +{ +protected: + void SetUp() override {} + + //! Creates flat knots array from knots and multiplicities + void createFlatKnots(const TColStd_Array1OfReal& theKnots, + const TColStd_Array1OfInteger& theMults, + TColStd_Array1OfReal& theFlatKnots) const + { + int aFlatIndex = theFlatKnots.Lower(); + for (int i = theKnots.Lower(); i <= theKnots.Upper(); ++i) + { + for (int j = 0; j < theMults(i); ++j) + { + theFlatKnots(aFlatIndex++) = theKnots(i); + } + } + } +}; + +//================================================================================================== +// Non-rational surface tests +//================================================================================================== + +TEST_F(BSplSLib_CacheTest, D0_NonRationalSurface) +{ + // Create a biquadratic Bezier surface + TColgp_Array2OfPnt aPoles(1, 3, 1, 3); + + // Row 1 (v=0) + aPoles(1, 1) = gp_Pnt(0, 0, 0); + aPoles(2, 1) = gp_Pnt(1, 0, 1); + aPoles(3, 1) = gp_Pnt(2, 0, 0); + + // Row 2 (v=0.5) + aPoles(1, 2) = gp_Pnt(0, 1, 1); + aPoles(2, 2) = gp_Pnt(1, 1, 2); + aPoles(3, 2) = gp_Pnt(2, 1, 1); + + // Row 3 (v=1) + aPoles(1, 3) = gp_Pnt(0, 2, 0); + aPoles(2, 3) = gp_Pnt(1, 2, 1); + aPoles(3, 3) = gp_Pnt(2, 2, 0); + + TColStd_Array1OfReal aKnotsU(1, 2); + aKnotsU(1) = 0.0; + aKnotsU(2) = 1.0; + + TColStd_Array1OfReal aKnotsV(1, 2); + aKnotsV(1) = 0.0; + aKnotsV(2) = 1.0; + + TColStd_Array1OfInteger aMultsU(1, 2); + aMultsU(1) = 3; + aMultsU(2) = 3; + + TColStd_Array1OfInteger aMultsV(1, 2); + aMultsV(1) = 3; + aMultsV(2) = 3; + + TColStd_Array1OfReal aFlatKnotsU(1, 6); + createFlatKnots(aKnotsU, aMultsU, aFlatKnotsU); + + TColStd_Array1OfReal aFlatKnotsV(1, 6); + createFlatKnots(aKnotsV, aMultsV, aFlatKnotsV); + + const int aDegreeU = 2; + const int aDegreeV = 2; + + // Create cache + Handle(BSplSLib_Cache) aCache = + new BSplSLib_Cache(aDegreeU, false, aFlatKnotsU, aDegreeV, false, aFlatKnotsV, nullptr); + aCache->BuildCache(0.5, 0.5, aFlatKnotsU, aFlatKnotsV, aPoles, nullptr); + + // Test at several parameter values + for (double u = 0.0; u <= 1.0; u += 0.2) + { + for (double v = 0.0; v <= 1.0; v += 0.2) + { + gp_Pnt aCachePnt, aDirectPnt; + + // Cached evaluation + if (!aCache->IsCacheValid(u, v)) + { + aCache->BuildCache(u, v, aFlatKnotsU, aFlatKnotsV, aPoles, nullptr); + } + aCache->D0(u, v, aCachePnt); + + // Direct evaluation using BSplSLib::D0 + BSplSLib::D0(u, + v, + 0, + 0, + aPoles, + nullptr, + aKnotsU, + aKnotsV, + &aMultsU, + &aMultsV, + aDegreeU, + aDegreeV, + false, + false, + false, + false, + aDirectPnt); + + // Compare + EXPECT_NEAR(aCachePnt.X(), aDirectPnt.X(), THE_TOLERANCE) + << "D0 X mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCachePnt.Y(), aDirectPnt.Y(), THE_TOLERANCE) + << "D0 Y mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCachePnt.Z(), aDirectPnt.Z(), THE_TOLERANCE) + << "D0 Z mismatch at u=" << u << ", v=" << v; + } + } +} + +TEST_F(BSplSLib_CacheTest, D1_NonRationalSurface) +{ + // Create a biquadratic Bezier surface + TColgp_Array2OfPnt aPoles(1, 3, 1, 3); + + // Row 1 (v=0) + aPoles(1, 1) = gp_Pnt(0, 0, 0); + aPoles(2, 1) = gp_Pnt(1, 0, 1); + aPoles(3, 1) = gp_Pnt(2, 0, 0); + + // Row 2 (v=0.5) + aPoles(1, 2) = gp_Pnt(0, 1, 1); + aPoles(2, 2) = gp_Pnt(1, 1, 2); + aPoles(3, 2) = gp_Pnt(2, 1, 1); + + // Row 3 (v=1) + aPoles(1, 3) = gp_Pnt(0, 2, 0); + aPoles(2, 3) = gp_Pnt(1, 2, 1); + aPoles(3, 3) = gp_Pnt(2, 2, 0); + + TColStd_Array1OfReal aKnotsU(1, 2); + aKnotsU(1) = 0.0; + aKnotsU(2) = 1.0; + + TColStd_Array1OfReal aKnotsV(1, 2); + aKnotsV(1) = 0.0; + aKnotsV(2) = 1.0; + + TColStd_Array1OfInteger aMultsU(1, 2); + aMultsU(1) = 3; + aMultsU(2) = 3; + + TColStd_Array1OfInteger aMultsV(1, 2); + aMultsV(1) = 3; + aMultsV(2) = 3; + + TColStd_Array1OfReal aFlatKnotsU(1, 6); + createFlatKnots(aKnotsU, aMultsU, aFlatKnotsU); + + TColStd_Array1OfReal aFlatKnotsV(1, 6); + createFlatKnots(aKnotsV, aMultsV, aFlatKnotsV); + + const int aDegreeU = 2; + const int aDegreeV = 2; + + // Create cache + Handle(BSplSLib_Cache) aCache = + new BSplSLib_Cache(aDegreeU, false, aFlatKnotsU, aDegreeV, false, aFlatKnotsV, nullptr); + aCache->BuildCache(0.5, 0.5, aFlatKnotsU, aFlatKnotsV, aPoles, nullptr); + + // Test at several parameter values + for (double u = 0.0; u <= 1.0; u += 0.2) + { + for (double v = 0.0; v <= 1.0; v += 0.2) + { + gp_Pnt aCachePnt, aDirectPnt; + gp_Vec aCacheTanU, aCacheTanV, aDirectTanU, aDirectTanV; + + // Cached evaluation + if (!aCache->IsCacheValid(u, v)) + { + aCache->BuildCache(u, v, aFlatKnotsU, aFlatKnotsV, aPoles, nullptr); + } + aCache->D1(u, v, aCachePnt, aCacheTanU, aCacheTanV); + + // Direct evaluation using BSplSLib::D1 + BSplSLib::D1(u, + v, + 0, + 0, + aPoles, + nullptr, + aKnotsU, + aKnotsV, + &aMultsU, + &aMultsV, + aDegreeU, + aDegreeV, + false, + false, + false, + false, + aDirectPnt, + aDirectTanU, + aDirectTanV); + + // Compare point + EXPECT_NEAR(aCachePnt.X(), aDirectPnt.X(), THE_TOLERANCE) + << "D1 point X mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCachePnt.Y(), aDirectPnt.Y(), THE_TOLERANCE) + << "D1 point Y mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCachePnt.Z(), aDirectPnt.Z(), THE_TOLERANCE) + << "D1 point Z mismatch at u=" << u << ", v=" << v; + + // Compare tangent U + EXPECT_NEAR(aCacheTanU.X(), aDirectTanU.X(), THE_TOLERANCE) + << "D1 tangentU X mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheTanU.Y(), aDirectTanU.Y(), THE_TOLERANCE) + << "D1 tangentU Y mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheTanU.Z(), aDirectTanU.Z(), THE_TOLERANCE) + << "D1 tangentU Z mismatch at u=" << u << ", v=" << v; + + // Compare tangent V + EXPECT_NEAR(aCacheTanV.X(), aDirectTanV.X(), THE_TOLERANCE) + << "D1 tangentV X mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheTanV.Y(), aDirectTanV.Y(), THE_TOLERANCE) + << "D1 tangentV Y mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheTanV.Z(), aDirectTanV.Z(), THE_TOLERANCE) + << "D1 tangentV Z mismatch at u=" << u << ", v=" << v; + } + } +} + +TEST_F(BSplSLib_CacheTest, D2_NonRationalSurface) +{ + // Create a biquadratic Bezier surface + TColgp_Array2OfPnt aPoles(1, 3, 1, 3); + + // Row 1 (v=0) + aPoles(1, 1) = gp_Pnt(0, 0, 0); + aPoles(2, 1) = gp_Pnt(1, 0, 1); + aPoles(3, 1) = gp_Pnt(2, 0, 0); + + // Row 2 (v=0.5) + aPoles(1, 2) = gp_Pnt(0, 1, 1); + aPoles(2, 2) = gp_Pnt(1, 1, 2); + aPoles(3, 2) = gp_Pnt(2, 1, 1); + + // Row 3 (v=1) + aPoles(1, 3) = gp_Pnt(0, 2, 0); + aPoles(2, 3) = gp_Pnt(1, 2, 1); + aPoles(3, 3) = gp_Pnt(2, 2, 0); + + TColStd_Array1OfReal aKnotsU(1, 2); + aKnotsU(1) = 0.0; + aKnotsU(2) = 1.0; + + TColStd_Array1OfReal aKnotsV(1, 2); + aKnotsV(1) = 0.0; + aKnotsV(2) = 1.0; + + TColStd_Array1OfInteger aMultsU(1, 2); + aMultsU(1) = 3; + aMultsU(2) = 3; + + TColStd_Array1OfInteger aMultsV(1, 2); + aMultsV(1) = 3; + aMultsV(2) = 3; + + TColStd_Array1OfReal aFlatKnotsU(1, 6); + createFlatKnots(aKnotsU, aMultsU, aFlatKnotsU); + + TColStd_Array1OfReal aFlatKnotsV(1, 6); + createFlatKnots(aKnotsV, aMultsV, aFlatKnotsV); + + const int aDegreeU = 2; + const int aDegreeV = 2; + + // Create cache + Handle(BSplSLib_Cache) aCache = + new BSplSLib_Cache(aDegreeU, false, aFlatKnotsU, aDegreeV, false, aFlatKnotsV, nullptr); + aCache->BuildCache(0.5, 0.5, aFlatKnotsU, aFlatKnotsV, aPoles, nullptr); + + // Test at several parameter values + for (double u = 0.0; u <= 1.0; u += 0.2) + { + for (double v = 0.0; v <= 1.0; v += 0.2) + { + gp_Pnt aCachePnt, aDirectPnt; + gp_Vec aCacheTanU, aCacheTanV, aDirectTanU, aDirectTanV; + gp_Vec aCacheCurvU, aCacheCurvV, aCacheCurvUV; + gp_Vec aDirectCurvU, aDirectCurvV, aDirectCurvUV; + + // Cached evaluation + if (!aCache->IsCacheValid(u, v)) + { + aCache->BuildCache(u, v, aFlatKnotsU, aFlatKnotsV, aPoles, nullptr); + } + aCache->D2(u, v, aCachePnt, aCacheTanU, aCacheTanV, aCacheCurvU, aCacheCurvV, aCacheCurvUV); + + // Direct evaluation using BSplSLib::D2 + BSplSLib::D2(u, + v, + 0, + 0, + aPoles, + nullptr, + aKnotsU, + aKnotsV, + &aMultsU, + &aMultsV, + aDegreeU, + aDegreeV, + false, + false, + false, + false, + aDirectPnt, + aDirectTanU, + aDirectTanV, + aDirectCurvU, + aDirectCurvV, + aDirectCurvUV); + + // Compare point + EXPECT_NEAR(aCachePnt.X(), aDirectPnt.X(), THE_TOLERANCE) + << "D2 point X mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCachePnt.Y(), aDirectPnt.Y(), THE_TOLERANCE) + << "D2 point Y mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCachePnt.Z(), aDirectPnt.Z(), THE_TOLERANCE) + << "D2 point Z mismatch at u=" << u << ", v=" << v; + + // Compare tangent U + EXPECT_NEAR(aCacheTanU.X(), aDirectTanU.X(), THE_TOLERANCE) + << "D2 tangentU X mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheTanU.Y(), aDirectTanU.Y(), THE_TOLERANCE) + << "D2 tangentU Y mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheTanU.Z(), aDirectTanU.Z(), THE_TOLERANCE) + << "D2 tangentU Z mismatch at u=" << u << ", v=" << v; + + // Compare tangent V + EXPECT_NEAR(aCacheTanV.X(), aDirectTanV.X(), THE_TOLERANCE) + << "D2 tangentV X mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheTanV.Y(), aDirectTanV.Y(), THE_TOLERANCE) + << "D2 tangentV Y mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheTanV.Z(), aDirectTanV.Z(), THE_TOLERANCE) + << "D2 tangentV Z mismatch at u=" << u << ", v=" << v; + + // Compare second derivatives + EXPECT_NEAR(aCacheCurvU.X(), aDirectCurvU.X(), THE_TOLERANCE) + << "D2 curvU X mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheCurvU.Y(), aDirectCurvU.Y(), THE_TOLERANCE) + << "D2 curvU Y mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheCurvU.Z(), aDirectCurvU.Z(), THE_TOLERANCE) + << "D2 curvU Z mismatch at u=" << u << ", v=" << v; + + EXPECT_NEAR(aCacheCurvV.X(), aDirectCurvV.X(), THE_TOLERANCE) + << "D2 curvV X mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheCurvV.Y(), aDirectCurvV.Y(), THE_TOLERANCE) + << "D2 curvV Y mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheCurvV.Z(), aDirectCurvV.Z(), THE_TOLERANCE) + << "D2 curvV Z mismatch at u=" << u << ", v=" << v; + + EXPECT_NEAR(aCacheCurvUV.X(), aDirectCurvUV.X(), THE_TOLERANCE) + << "D2 curvUV X mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheCurvUV.Y(), aDirectCurvUV.Y(), THE_TOLERANCE) + << "D2 curvUV Y mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheCurvUV.Z(), aDirectCurvUV.Z(), THE_TOLERANCE) + << "D2 curvUV Z mismatch at u=" << u << ", v=" << v; + } + } +} + +//================================================================================================== +// Rational surface tests +//================================================================================================== + +TEST_F(BSplSLib_CacheTest, D0_RationalSurface) +{ + // Create a rational biquadratic Bezier surface (can represent sphere patch) + TColgp_Array2OfPnt aPoles(1, 3, 1, 3); + + // Row 1 (v=0) + aPoles(1, 1) = gp_Pnt(1, 0, 0); + aPoles(2, 1) = gp_Pnt(1, 1, 0); + aPoles(3, 1) = gp_Pnt(0, 1, 0); + + // Row 2 (v=0.5) + aPoles(1, 2) = gp_Pnt(1, 0, 1); + aPoles(2, 2) = gp_Pnt(1, 1, 1); + aPoles(3, 2) = gp_Pnt(0, 1, 1); + + // Row 3 (v=1) + aPoles(1, 3) = gp_Pnt(0, 0, 1); + aPoles(2, 3) = gp_Pnt(0, 0, 1); + aPoles(3, 3) = gp_Pnt(0, 0, 1); + + TColStd_Array2OfReal aWeights(1, 3, 1, 3); + const double aSqrt2_2 = 0.707106781186548; + aWeights(1, 1) = 1.0; + aWeights(2, 1) = aSqrt2_2; + aWeights(3, 1) = 1.0; + aWeights(1, 2) = aSqrt2_2; + aWeights(2, 2) = 0.5; + aWeights(3, 2) = aSqrt2_2; + aWeights(1, 3) = 1.0; + aWeights(2, 3) = aSqrt2_2; + aWeights(3, 3) = 1.0; + + TColStd_Array1OfReal aKnotsU(1, 2); + aKnotsU(1) = 0.0; + aKnotsU(2) = 1.0; + + TColStd_Array1OfReal aKnotsV(1, 2); + aKnotsV(1) = 0.0; + aKnotsV(2) = 1.0; + + TColStd_Array1OfInteger aMultsU(1, 2); + aMultsU(1) = 3; + aMultsU(2) = 3; + + TColStd_Array1OfInteger aMultsV(1, 2); + aMultsV(1) = 3; + aMultsV(2) = 3; + + TColStd_Array1OfReal aFlatKnotsU(1, 6); + createFlatKnots(aKnotsU, aMultsU, aFlatKnotsU); + + TColStd_Array1OfReal aFlatKnotsV(1, 6); + createFlatKnots(aKnotsV, aMultsV, aFlatKnotsV); + + const int aDegreeU = 2; + const int aDegreeV = 2; + + // Create cache + Handle(BSplSLib_Cache) aCache = + new BSplSLib_Cache(aDegreeU, false, aFlatKnotsU, aDegreeV, false, aFlatKnotsV, &aWeights); + aCache->BuildCache(0.5, 0.5, aFlatKnotsU, aFlatKnotsV, aPoles, &aWeights); + + // Test at several parameter values + for (double u = 0.0; u <= 1.0; u += 0.2) + { + for (double v = 0.0; v <= 1.0; v += 0.2) + { + gp_Pnt aCachePnt, aDirectPnt; + + // Cached evaluation + if (!aCache->IsCacheValid(u, v)) + { + aCache->BuildCache(u, v, aFlatKnotsU, aFlatKnotsV, aPoles, &aWeights); + } + aCache->D0(u, v, aCachePnt); + + // Direct evaluation using BSplSLib::D0 + BSplSLib::D0(u, + v, + 0, + 0, + aPoles, + &aWeights, + aKnotsU, + aKnotsV, + &aMultsU, + &aMultsV, + aDegreeU, + aDegreeV, + true, + true, + false, + false, + aDirectPnt); + + // Compare + EXPECT_NEAR(aCachePnt.X(), aDirectPnt.X(), THE_TOLERANCE) + << "Rational D0 X mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCachePnt.Y(), aDirectPnt.Y(), THE_TOLERANCE) + << "Rational D0 Y mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCachePnt.Z(), aDirectPnt.Z(), THE_TOLERANCE) + << "Rational D0 Z mismatch at u=" << u << ", v=" << v; + } + } +} + +TEST_F(BSplSLib_CacheTest, D1_RationalSurface) +{ + // Create a rational biquadratic Bezier surface + TColgp_Array2OfPnt aPoles(1, 3, 1, 3); + + // Row 1 (v=0) + aPoles(1, 1) = gp_Pnt(1, 0, 0); + aPoles(2, 1) = gp_Pnt(1, 1, 0); + aPoles(3, 1) = gp_Pnt(0, 1, 0); + + // Row 2 (v=0.5) + aPoles(1, 2) = gp_Pnt(1, 0, 1); + aPoles(2, 2) = gp_Pnt(1, 1, 1); + aPoles(3, 2) = gp_Pnt(0, 1, 1); + + // Row 3 (v=1) + aPoles(1, 3) = gp_Pnt(0, 0, 1); + aPoles(2, 3) = gp_Pnt(0, 0, 1); + aPoles(3, 3) = gp_Pnt(0, 0, 1); + + TColStd_Array2OfReal aWeights(1, 3, 1, 3); + const double aSqrt2_2 = 0.707106781186548; + aWeights(1, 1) = 1.0; + aWeights(2, 1) = aSqrt2_2; + aWeights(3, 1) = 1.0; + aWeights(1, 2) = aSqrt2_2; + aWeights(2, 2) = 0.5; + aWeights(3, 2) = aSqrt2_2; + aWeights(1, 3) = 1.0; + aWeights(2, 3) = aSqrt2_2; + aWeights(3, 3) = 1.0; + + TColStd_Array1OfReal aKnotsU(1, 2); + aKnotsU(1) = 0.0; + aKnotsU(2) = 1.0; + + TColStd_Array1OfReal aKnotsV(1, 2); + aKnotsV(1) = 0.0; + aKnotsV(2) = 1.0; + + TColStd_Array1OfInteger aMultsU(1, 2); + aMultsU(1) = 3; + aMultsU(2) = 3; + + TColStd_Array1OfInteger aMultsV(1, 2); + aMultsV(1) = 3; + aMultsV(2) = 3; + + TColStd_Array1OfReal aFlatKnotsU(1, 6); + createFlatKnots(aKnotsU, aMultsU, aFlatKnotsU); + + TColStd_Array1OfReal aFlatKnotsV(1, 6); + createFlatKnots(aKnotsV, aMultsV, aFlatKnotsV); + + const int aDegreeU = 2; + const int aDegreeV = 2; + + // Create cache + Handle(BSplSLib_Cache) aCache = + new BSplSLib_Cache(aDegreeU, false, aFlatKnotsU, aDegreeV, false, aFlatKnotsV, &aWeights); + aCache->BuildCache(0.5, 0.5, aFlatKnotsU, aFlatKnotsV, aPoles, &aWeights); + + // Test at several parameter values + for (double u = 0.0; u <= 1.0; u += 0.2) + { + for (double v = 0.0; v <= 1.0; v += 0.2) + { + gp_Pnt aCachePnt, aDirectPnt; + gp_Vec aCacheTanU, aCacheTanV, aDirectTanU, aDirectTanV; + + // Cached evaluation + if (!aCache->IsCacheValid(u, v)) + { + aCache->BuildCache(u, v, aFlatKnotsU, aFlatKnotsV, aPoles, &aWeights); + } + aCache->D1(u, v, aCachePnt, aCacheTanU, aCacheTanV); + + // Direct evaluation using BSplSLib::D1 + BSplSLib::D1(u, + v, + 0, + 0, + aPoles, + &aWeights, + aKnotsU, + aKnotsV, + &aMultsU, + &aMultsV, + aDegreeU, + aDegreeV, + true, + true, + false, + false, + aDirectPnt, + aDirectTanU, + aDirectTanV); + + // Compare point + EXPECT_NEAR(aCachePnt.X(), aDirectPnt.X(), THE_TOLERANCE) + << "Rational D1 point X mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCachePnt.Y(), aDirectPnt.Y(), THE_TOLERANCE) + << "Rational D1 point Y mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCachePnt.Z(), aDirectPnt.Z(), THE_TOLERANCE) + << "Rational D1 point Z mismatch at u=" << u << ", v=" << v; + + // Compare tangent U + EXPECT_NEAR(aCacheTanU.X(), aDirectTanU.X(), THE_TOLERANCE) + << "Rational D1 tangentU X mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheTanU.Y(), aDirectTanU.Y(), THE_TOLERANCE) + << "Rational D1 tangentU Y mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheTanU.Z(), aDirectTanU.Z(), THE_TOLERANCE) + << "Rational D1 tangentU Z mismatch at u=" << u << ", v=" << v; + + // Compare tangent V + EXPECT_NEAR(aCacheTanV.X(), aDirectTanV.X(), THE_TOLERANCE) + << "Rational D1 tangentV X mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheTanV.Y(), aDirectTanV.Y(), THE_TOLERANCE) + << "Rational D1 tangentV Y mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheTanV.Z(), aDirectTanV.Z(), THE_TOLERANCE) + << "Rational D1 tangentV Z mismatch at u=" << u << ", v=" << v; + } + } +} + +TEST_F(BSplSLib_CacheTest, D2_RationalSurface) +{ + // Create a rational biquadratic Bezier surface + TColgp_Array2OfPnt aPoles(1, 3, 1, 3); + + // Row 1 (v=0) + aPoles(1, 1) = gp_Pnt(1, 0, 0); + aPoles(2, 1) = gp_Pnt(1, 1, 0); + aPoles(3, 1) = gp_Pnt(0, 1, 0); + + // Row 2 (v=0.5) + aPoles(1, 2) = gp_Pnt(1, 0, 1); + aPoles(2, 2) = gp_Pnt(1, 1, 1); + aPoles(3, 2) = gp_Pnt(0, 1, 1); + + // Row 3 (v=1) + aPoles(1, 3) = gp_Pnt(0, 0, 1); + aPoles(2, 3) = gp_Pnt(0, 0, 1); + aPoles(3, 3) = gp_Pnt(0, 0, 1); + + TColStd_Array2OfReal aWeights(1, 3, 1, 3); + const double aSqrt2_2 = 0.707106781186548; + aWeights(1, 1) = 1.0; + aWeights(2, 1) = aSqrt2_2; + aWeights(3, 1) = 1.0; + aWeights(1, 2) = aSqrt2_2; + aWeights(2, 2) = 0.5; + aWeights(3, 2) = aSqrt2_2; + aWeights(1, 3) = 1.0; + aWeights(2, 3) = aSqrt2_2; + aWeights(3, 3) = 1.0; + + TColStd_Array1OfReal aKnotsU(1, 2); + aKnotsU(1) = 0.0; + aKnotsU(2) = 1.0; + + TColStd_Array1OfReal aKnotsV(1, 2); + aKnotsV(1) = 0.0; + aKnotsV(2) = 1.0; + + TColStd_Array1OfInteger aMultsU(1, 2); + aMultsU(1) = 3; + aMultsU(2) = 3; + + TColStd_Array1OfInteger aMultsV(1, 2); + aMultsV(1) = 3; + aMultsV(2) = 3; + + TColStd_Array1OfReal aFlatKnotsU(1, 6); + createFlatKnots(aKnotsU, aMultsU, aFlatKnotsU); + + TColStd_Array1OfReal aFlatKnotsV(1, 6); + createFlatKnots(aKnotsV, aMultsV, aFlatKnotsV); + + const int aDegreeU = 2; + const int aDegreeV = 2; + + // Create cache + Handle(BSplSLib_Cache) aCache = + new BSplSLib_Cache(aDegreeU, false, aFlatKnotsU, aDegreeV, false, aFlatKnotsV, &aWeights); + aCache->BuildCache(0.5, 0.5, aFlatKnotsU, aFlatKnotsV, aPoles, &aWeights); + + // Test at several parameter values + for (double u = 0.0; u <= 1.0; u += 0.2) + { + for (double v = 0.0; v <= 1.0; v += 0.2) + { + gp_Pnt aCachePnt, aDirectPnt; + gp_Vec aCacheTanU, aCacheTanV, aDirectTanU, aDirectTanV; + gp_Vec aCacheCurvU, aCacheCurvV, aCacheCurvUV; + gp_Vec aDirectCurvU, aDirectCurvV, aDirectCurvUV; + + // Cached evaluation + if (!aCache->IsCacheValid(u, v)) + { + aCache->BuildCache(u, v, aFlatKnotsU, aFlatKnotsV, aPoles, &aWeights); + } + aCache->D2(u, v, aCachePnt, aCacheTanU, aCacheTanV, aCacheCurvU, aCacheCurvV, aCacheCurvUV); + + // Direct evaluation using BSplSLib::D2 + BSplSLib::D2(u, + v, + 0, + 0, + aPoles, + &aWeights, + aKnotsU, + aKnotsV, + &aMultsU, + &aMultsV, + aDegreeU, + aDegreeV, + true, + true, + false, + false, + aDirectPnt, + aDirectTanU, + aDirectTanV, + aDirectCurvU, + aDirectCurvV, + aDirectCurvUV); + + // Compare point + EXPECT_NEAR(aCachePnt.X(), aDirectPnt.X(), THE_TOLERANCE) + << "Rational D2 point X mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCachePnt.Y(), aDirectPnt.Y(), THE_TOLERANCE) + << "Rational D2 point Y mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCachePnt.Z(), aDirectPnt.Z(), THE_TOLERANCE) + << "Rational D2 point Z mismatch at u=" << u << ", v=" << v; + + // Compare tangent U + EXPECT_NEAR(aCacheTanU.X(), aDirectTanU.X(), THE_TOLERANCE) + << "Rational D2 tangentU X mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheTanU.Y(), aDirectTanU.Y(), THE_TOLERANCE) + << "Rational D2 tangentU Y mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheTanU.Z(), aDirectTanU.Z(), THE_TOLERANCE) + << "Rational D2 tangentU Z mismatch at u=" << u << ", v=" << v; + + // Compare tangent V + EXPECT_NEAR(aCacheTanV.X(), aDirectTanV.X(), THE_TOLERANCE) + << "Rational D2 tangentV X mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheTanV.Y(), aDirectTanV.Y(), THE_TOLERANCE) + << "Rational D2 tangentV Y mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheTanV.Z(), aDirectTanV.Z(), THE_TOLERANCE) + << "Rational D2 tangentV Z mismatch at u=" << u << ", v=" << v; + + // Compare second derivatives + EXPECT_NEAR(aCacheCurvU.X(), aDirectCurvU.X(), THE_TOLERANCE) + << "Rational D2 curvU X mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheCurvU.Y(), aDirectCurvU.Y(), THE_TOLERANCE) + << "Rational D2 curvU Y mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheCurvU.Z(), aDirectCurvU.Z(), THE_TOLERANCE) + << "Rational D2 curvU Z mismatch at u=" << u << ", v=" << v; + + EXPECT_NEAR(aCacheCurvV.X(), aDirectCurvV.X(), THE_TOLERANCE) + << "Rational D2 curvV X mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheCurvV.Y(), aDirectCurvV.Y(), THE_TOLERANCE) + << "Rational D2 curvV Y mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheCurvV.Z(), aDirectCurvV.Z(), THE_TOLERANCE) + << "Rational D2 curvV Z mismatch at u=" << u << ", v=" << v; + + EXPECT_NEAR(aCacheCurvUV.X(), aDirectCurvUV.X(), THE_TOLERANCE) + << "Rational D2 curvUV X mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheCurvUV.Y(), aDirectCurvUV.Y(), THE_TOLERANCE) + << "Rational D2 curvUV Y mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheCurvUV.Z(), aDirectCurvUV.Z(), THE_TOLERANCE) + << "Rational D2 curvUV Z mismatch at u=" << u << ", v=" << v; + } + } +} + +//================================================================================================== +// Test with different degrees in U and V +//================================================================================================== + +TEST_F(BSplSLib_CacheTest, D1_DifferentDegrees_UGreaterV) +{ + // Create a surface with degree 3 in U, degree 2 in V + TColgp_Array2OfPnt aPoles(1, 4, 1, 3); + + // Fill poles + for (int i = 1; i <= 4; ++i) + { + for (int j = 1; j <= 3; ++j) + { + aPoles(i, j) = gp_Pnt((i - 1) * 1.0, (j - 1) * 1.0, sin((i - 1) * 0.5) * cos((j - 1) * 0.5)); + } + } + + TColStd_Array1OfReal aKnotsU(1, 2); + aKnotsU(1) = 0.0; + aKnotsU(2) = 1.0; + + TColStd_Array1OfReal aKnotsV(1, 2); + aKnotsV(1) = 0.0; + aKnotsV(2) = 1.0; + + TColStd_Array1OfInteger aMultsU(1, 2); + aMultsU(1) = 4; + aMultsU(2) = 4; + + TColStd_Array1OfInteger aMultsV(1, 2); + aMultsV(1) = 3; + aMultsV(2) = 3; + + TColStd_Array1OfReal aFlatKnotsU(1, 8); + createFlatKnots(aKnotsU, aMultsU, aFlatKnotsU); + + TColStd_Array1OfReal aFlatKnotsV(1, 6); + createFlatKnots(aKnotsV, aMultsV, aFlatKnotsV); + + const int aDegreeU = 3; + const int aDegreeV = 2; + + // Create cache + Handle(BSplSLib_Cache) aCache = + new BSplSLib_Cache(aDegreeU, false, aFlatKnotsU, aDegreeV, false, aFlatKnotsV, nullptr); + aCache->BuildCache(0.5, 0.5, aFlatKnotsU, aFlatKnotsV, aPoles, nullptr); + + // Test at several parameter values + for (double u = 0.0; u <= 1.0; u += 0.25) + { + for (double v = 0.0; v <= 1.0; v += 0.25) + { + gp_Pnt aCachePnt, aDirectPnt; + gp_Vec aCacheTanU, aCacheTanV, aDirectTanU, aDirectTanV; + + // Cached evaluation + if (!aCache->IsCacheValid(u, v)) + { + aCache->BuildCache(u, v, aFlatKnotsU, aFlatKnotsV, aPoles, nullptr); + } + aCache->D1(u, v, aCachePnt, aCacheTanU, aCacheTanV); + + // Direct evaluation using BSplSLib::D1 + BSplSLib::D1(u, + v, + 0, + 0, + aPoles, + nullptr, + aKnotsU, + aKnotsV, + &aMultsU, + &aMultsV, + aDegreeU, + aDegreeV, + false, + false, + false, + false, + aDirectPnt, + aDirectTanU, + aDirectTanV); + + // Compare + EXPECT_NEAR(aCachePnt.X(), aDirectPnt.X(), THE_TOLERANCE) + << "DifferentDeg D1 point X mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheTanU.X(), aDirectTanU.X(), THE_TOLERANCE) + << "DifferentDeg D1 tanU X mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheTanV.X(), aDirectTanV.X(), THE_TOLERANCE) + << "DifferentDeg D1 tanV X mismatch at u=" << u << ", v=" << v; + } + } +} + +TEST_F(BSplSLib_CacheTest, D1_DifferentDegrees_VGreaterU) +{ + // Create a surface with degree 2 in U, degree 3 in V + TColgp_Array2OfPnt aPoles(1, 3, 1, 4); + + // Fill poles + for (int i = 1; i <= 3; ++i) + { + for (int j = 1; j <= 4; ++j) + { + aPoles(i, j) = gp_Pnt((i - 1) * 1.0, (j - 1) * 1.0, sin((i - 1) * 0.5) * cos((j - 1) * 0.5)); + } + } + + TColStd_Array1OfReal aKnotsU(1, 2); + aKnotsU(1) = 0.0; + aKnotsU(2) = 1.0; + + TColStd_Array1OfReal aKnotsV(1, 2); + aKnotsV(1) = 0.0; + aKnotsV(2) = 1.0; + + TColStd_Array1OfInteger aMultsU(1, 2); + aMultsU(1) = 3; + aMultsU(2) = 3; + + TColStd_Array1OfInteger aMultsV(1, 2); + aMultsV(1) = 4; + aMultsV(2) = 4; + + TColStd_Array1OfReal aFlatKnotsU(1, 6); + createFlatKnots(aKnotsU, aMultsU, aFlatKnotsU); + + TColStd_Array1OfReal aFlatKnotsV(1, 8); + createFlatKnots(aKnotsV, aMultsV, aFlatKnotsV); + + const int aDegreeU = 2; + const int aDegreeV = 3; + + // Create cache + Handle(BSplSLib_Cache) aCache = + new BSplSLib_Cache(aDegreeU, false, aFlatKnotsU, aDegreeV, false, aFlatKnotsV, nullptr); + aCache->BuildCache(0.5, 0.5, aFlatKnotsU, aFlatKnotsV, aPoles, nullptr); + + // Test at several parameter values + for (double u = 0.0; u <= 1.0; u += 0.25) + { + for (double v = 0.0; v <= 1.0; v += 0.25) + { + gp_Pnt aCachePnt, aDirectPnt; + gp_Vec aCacheTanU, aCacheTanV, aDirectTanU, aDirectTanV; + + // Cached evaluation + if (!aCache->IsCacheValid(u, v)) + { + aCache->BuildCache(u, v, aFlatKnotsU, aFlatKnotsV, aPoles, nullptr); + } + aCache->D1(u, v, aCachePnt, aCacheTanU, aCacheTanV); + + // Direct evaluation using BSplSLib::D1 + BSplSLib::D1(u, + v, + 0, + 0, + aPoles, + nullptr, + aKnotsU, + aKnotsV, + &aMultsU, + &aMultsV, + aDegreeU, + aDegreeV, + false, + false, + false, + false, + aDirectPnt, + aDirectTanU, + aDirectTanV); + + // Compare + EXPECT_NEAR(aCachePnt.X(), aDirectPnt.X(), THE_TOLERANCE) + << "DifferentDeg V>U D1 point X mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheTanU.X(), aDirectTanU.X(), THE_TOLERANCE) + << "DifferentDeg V>U D1 tanU X mismatch at u=" << u << ", v=" << v; + EXPECT_NEAR(aCacheTanV.X(), aDirectTanV.X(), THE_TOLERANCE) + << "DifferentDeg V>U D1 tanV X mismatch at u=" << u << ", v=" << v; + } + } +} diff --git a/src/FoundationClasses/TKMath/GTests/FILES.cmake b/src/FoundationClasses/TKMath/GTests/FILES.cmake index ed535ee023..e9588ec1b0 100644 --- a/src/FoundationClasses/TKMath/GTests/FILES.cmake +++ b/src/FoundationClasses/TKMath/GTests/FILES.cmake @@ -7,6 +7,8 @@ set(OCCT_TKMath_GTests_FILES Bnd_BoundSortBox_Test.cxx Bnd_Box_Test.cxx Bnd_OBB_Test.cxx + BSplCLib_Cache_Test.cxx + BSplSLib_Cache_Test.cxx BVH_BinnedBuilder_Test.cxx BVH_Box_Test.cxx BVH_BuildQueue_Test.cxx diff --git a/src/ModelingAlgorithms/TKBool/TopOpeBRepTool/TopOpeBRepTool_GEOMETRY.cxx b/src/ModelingAlgorithms/TKBool/TopOpeBRepTool/TopOpeBRepTool_GEOMETRY.cxx index 065194e28e..f7d8f618cb 100644 --- a/src/ModelingAlgorithms/TKBool/TopOpeBRepTool/TopOpeBRepTool_GEOMETRY.cxx +++ b/src/ModelingAlgorithms/TKBool/TopOpeBRepTool/TopOpeBRepTool_GEOMETRY.cxx @@ -18,6 +18,7 @@ #include #include +#include #include #include #include diff --git a/src/ModelingAlgorithms/TKShHealing/ShapeUpgrade/ShapeUpgrade_SplitSurface.cxx b/src/ModelingAlgorithms/TKShHealing/ShapeUpgrade/ShapeUpgrade_SplitSurface.cxx index eaa50e552d..ba443a70fc 100644 --- a/src/ModelingAlgorithms/TKShHealing/ShapeUpgrade/ShapeUpgrade_SplitSurface.cxx +++ b/src/ModelingAlgorithms/TKShHealing/ShapeUpgrade/ShapeUpgrade_SplitSurface.cxx @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/src/ModelingData/TKBRep/BRepAdaptor/BRepAdaptor_Curve2d.cxx b/src/ModelingData/TKBRep/BRepAdaptor/BRepAdaptor_Curve2d.cxx index d50b0b3cd7..ae27ac74d6 100644 --- a/src/ModelingData/TKBRep/BRepAdaptor/BRepAdaptor_Curve2d.cxx +++ b/src/ModelingData/TKBRep/BRepAdaptor/BRepAdaptor_Curve2d.cxx @@ -38,14 +38,32 @@ Handle(Adaptor2d_Curve2d) BRepAdaptor_Curve2d::ShallowCopy() const { Handle(BRepAdaptor_Curve2d) aCopy = new BRepAdaptor_Curve2d(); - aCopy->myCurve = myCurve; - aCopy->myTypeCurve = myTypeCurve; - aCopy->myFirst = myFirst; - aCopy->myLast = myLast; - aCopy->myBSplineCurve = myBSplineCurve; - if (!myNestedEvaluator.IsNull()) + aCopy->myCurve = myCurve; + aCopy->myTypeCurve = myTypeCurve; + aCopy->myFirst = myFirst; + aCopy->myLast = myLast; + + // Copy curve-specific data based on variant type + if (const auto* anOffsetData = std::get_if(&myCurveData)) + { + OffsetData aNewData; + if (!anOffsetData->BasisAdaptor.IsNull()) + { + aNewData.BasisAdaptor = + Handle(Geom2dAdaptor_Curve)::DownCast(anOffsetData->BasisAdaptor->ShallowCopy()); + } + aNewData.Offset = anOffsetData->Offset; + aCopy->myCurveData = std::move(aNewData); + } + else if (const auto* aBSplineData = std::get_if(&myCurveData)) + { + BSplineData aNewData; + aNewData.Curve = aBSplineData->Curve; + aCopy->myCurveData = std::move(aNewData); + } + else if (std::holds_alternative(myCurveData)) { - aCopy->myNestedEvaluator = myNestedEvaluator->ShallowCopy(); + aCopy->myCurveData = BezierData{}; } return aCopy; diff --git a/src/ModelingData/TKG2d/Adaptor2d/Adaptor2d_OffsetCurve.cxx b/src/ModelingData/TKG2d/Adaptor2d/Adaptor2d_OffsetCurve.cxx index d7b480a891..00f9bba739 100644 --- a/src/ModelingData/TKG2d/Adaptor2d/Adaptor2d_OffsetCurve.cxx +++ b/src/ModelingData/TKG2d/Adaptor2d/Adaptor2d_OffsetCurve.cxx @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include @@ -282,7 +282,7 @@ gp_Pnt2d Adaptor2d_OffsetCurve::Value(const Standard_Real U) const gp_Pnt2d aP; gp_Vec2d aV; myCurve->D1(U, aP, aV); - Geom2dEvaluator::CalculateD0(aP, aV, myOffset); + Geom2d_OffsetUtils::CalculateD0(aP, aV, myOffset); return aP; } else @@ -306,7 +306,7 @@ void Adaptor2d_OffsetCurve::D1(const Standard_Real U, gp_Pnt2d& P, gp_Vec2d& V) { gp_Vec2d aV2; myCurve->D2(U, P, V, aV2); - Geom2dEvaluator::CalculateD1(P, V, aV2, myOffset); + Geom2d_OffsetUtils::CalculateD1(P, V, aV2, myOffset); } else { @@ -322,7 +322,7 @@ void Adaptor2d_OffsetCurve::D2(const Standard_Real U, gp_Pnt2d& P, gp_Vec2d& V1, { gp_Vec2d aV3; myCurve->D3(U, P, V1, V2, aV3); - Geom2dEvaluator::CalculateD2(P, V1, V2, aV3, Standard_False, myOffset); + Geom2d_OffsetUtils::CalculateD2(P, V1, V2, aV3, Standard_False, myOffset); } else { @@ -342,7 +342,7 @@ void Adaptor2d_OffsetCurve::D3(const Standard_Real U, { gp_Vec2d aV4 = myCurve->DN(U, 4); myCurve->D3(U, P, V1, V2, V3); - Geom2dEvaluator::CalculateD3(P, V1, V2, V3, aV4, Standard_False, myOffset); + Geom2d_OffsetUtils::CalculateD3(P, V1, V2, V3, aV4, Standard_False, myOffset); } else { diff --git a/src/ModelingData/TKG2d/Geom2d/FILES.cmake b/src/ModelingData/TKG2d/Geom2d/FILES.cmake index a207fb8251..5a76611e53 100644 --- a/src/ModelingData/TKG2d/Geom2d/FILES.cmake +++ b/src/ModelingData/TKG2d/Geom2d/FILES.cmake @@ -31,6 +31,7 @@ set(OCCT_Geom2d_FILES Geom2d_Line.hxx Geom2d_OffsetCurve.cxx Geom2d_OffsetCurve.hxx + Geom2d_OffsetUtils.pxx Geom2d_Parabola.cxx Geom2d_Parabola.hxx Geom2d_Point.cxx diff --git a/src/ModelingData/TKG2d/Geom2d/Geom2d_OffsetCurve.cxx b/src/ModelingData/TKG2d/Geom2d/Geom2d_OffsetCurve.cxx index c7346953af..681a33414f 100644 --- a/src/ModelingData/TKG2d/Geom2d/Geom2d_OffsetCurve.cxx +++ b/src/ModelingData/TKG2d/Geom2d/Geom2d_OffsetCurve.cxx @@ -21,8 +21,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -36,20 +38,20 @@ IMPLEMENT_STANDARD_RTTIEXT(Geom2d_OffsetCurve, Geom2d_Curve) -static const Standard_Real MyAngularToleranceForG1 = Precision::Angular(); +static const double MyAngularToleranceForG1 = Precision::Angular(); -//================================================================================================= +//================================================================================================== Handle(Geom2d_Geometry) Geom2d_OffsetCurve::Copy() const { return new Geom2d_OffsetCurve(*this); } -//======================================================================= +//================================================================================================== // function : Geom2d_OffsetCurve // purpose : Basis curve cannot be an Offset curve or trimmed from // offset curve. -//======================================================================= +//================================================================================================== Geom2d_OffsetCurve::Geom2d_OffsetCurve(const Handle(Geom2d_Curve)& theCurve, const Standard_Real theOffset, @@ -59,18 +61,17 @@ Geom2d_OffsetCurve::Geom2d_OffsetCurve(const Handle(Geom2d_Curve)& theCurve, SetBasisCurve(theCurve, isTheNotCheckC0); } -//================================================================================================= +//================================================================================================== Geom2d_OffsetCurve::Geom2d_OffsetCurve(const Geom2d_OffsetCurve& theOther) : offsetValue(theOther.offsetValue), myBasisCurveContinuity(theOther.myBasisCurveContinuity) { - // Deep copy basis curve and evaluator without validation - basisCurve = Handle(Geom2d_Curve)::DownCast(theOther.basisCurve->Copy()); - myEvaluator = new Geom2dEvaluator_OffsetCurve(basisCurve, offsetValue); + // Deep copy basis curve without validation + basisCurve = Handle(Geom2d_Curve)::DownCast(theOther.basisCurve->Copy()); } -//================================================================================================= +//================================================================================================== void Geom2d_OffsetCurve::Reverse() { @@ -78,21 +79,21 @@ void Geom2d_OffsetCurve::Reverse() offsetValue = -offsetValue; } -//================================================================================================= +//================================================================================================== Standard_Real Geom2d_OffsetCurve::ReversedParameter(const Standard_Real U) const { return basisCurve->ReversedParameter(U); } -//================================================================================================= +//================================================================================================== void Geom2d_OffsetCurve::SetBasisCurve(const Handle(Geom2d_Curve)& C, const Standard_Boolean isNotCheckC0) { - const Standard_Real aUf = C->FirstParameter(), aUl = C->LastParameter(); + const double aUf = C->FirstParameter(), aUl = C->LastParameter(); Handle(Geom2d_Curve) aCheckingCurve = C; - Standard_Boolean isTrimmed = Standard_False; + bool isTrimmed = false; while (aCheckingCurve->IsKind(STANDARD_TYPE(Geom2d_TrimmedCurve)) || aCheckingCurve->IsKind(STANDARD_TYPE(Geom2d_OffsetCurve))) @@ -101,7 +102,7 @@ void Geom2d_OffsetCurve::SetBasisCurve(const Handle(Geom2d_Curve)& C, { Handle(Geom2d_TrimmedCurve) aTrimC = Handle(Geom2d_TrimmedCurve)::DownCast(aCheckingCurve); aCheckingCurve = aTrimC->BasisCurve(); - isTrimmed = Standard_True; + isTrimmed = true; } if (aCheckingCurve->IsKind(STANDARD_TYPE(Geom2d_OffsetCurve))) @@ -114,7 +115,7 @@ void Geom2d_OffsetCurve::SetBasisCurve(const Handle(Geom2d_Curve)& C, myBasisCurveContinuity = aCheckingCurve->Continuity(); - Standard_Boolean isC0 = !isNotCheckC0 && (myBasisCurveContinuity == GeomAbs_C0); + bool isC0 = !isNotCheckC0 && (myBasisCurveContinuity == GeomAbs_C0); // Basis curve must be at least C1 if (isC0 && aCheckingCurve->IsKind(STANDARD_TYPE(Geom2d_BSplineCurve))) @@ -125,12 +126,14 @@ void Geom2d_OffsetCurve::SetBasisCurve(const Handle(Geom2d_Curve)& C, // Checking if basis curve has more smooth (C1, G2 and above) is not done. // It can be done in case of need. myBasisCurveContinuity = GeomAbs_G1; - isC0 = Standard_False; + isC0 = false; } // Raise exception if still C0 if (isC0) + { throw Standard_ConstructionError("Offset on C0 curve"); + } } // if (isTrimmed) @@ -141,26 +144,23 @@ void Geom2d_OffsetCurve::SetBasisCurve(const Handle(Geom2d_Curve)& C, { basisCurve = aCheckingCurve; } - - myEvaluator = new Geom2dEvaluator_OffsetCurve(basisCurve, offsetValue); } -//================================================================================================= +//================================================================================================== void Geom2d_OffsetCurve::SetOffsetValue(const Standard_Real D) { offsetValue = D; - myEvaluator->SetOffsetValue(offsetValue); } -//================================================================================================= +//================================================================================================== Handle(Geom2d_Curve) Geom2d_OffsetCurve::BasisCurve() const { return basisCurve; } -//================================================================================================= +//================================================================================================== GeomAbs_Shape Geom2d_OffsetCurve::Continuity() const { @@ -193,31 +193,40 @@ GeomAbs_Shape Geom2d_OffsetCurve::Continuity() const return OffsetShape; } -//================================================================================================= +//================================================================================================== void Geom2d_OffsetCurve::D0(const Standard_Real theU, gp_Pnt2d& theP) const { - myEvaluator->D0(theU, theP); + if (!Geom2d_OffsetUtils::EvaluateD0(theU, basisCurve.get(), offsetValue, theP)) + { + throw Standard_NullValue("Geom2d_OffsetCurve::D0: Unable to calculate offset point"); + } } -//================================================================================================= +//================================================================================================== void Geom2d_OffsetCurve::D1(const Standard_Real theU, gp_Pnt2d& theP, gp_Vec2d& theV1) const { - myEvaluator->D1(theU, theP, theV1); + if (!Geom2d_OffsetUtils::EvaluateD1(theU, basisCurve.get(), offsetValue, theP, theV1)) + { + throw Standard_NullValue("Geom2d_OffsetCurve::D1: Unable to calculate offset D1"); + } } -//================================================================================================= +//================================================================================================== void Geom2d_OffsetCurve::D2(const Standard_Real theU, gp_Pnt2d& theP, gp_Vec2d& theV1, gp_Vec2d& theV2) const { - myEvaluator->D2(theU, theP, theV1, theV2); + if (!Geom2d_OffsetUtils::EvaluateD2(theU, basisCurve.get(), offsetValue, theP, theV1, theV2)) + { + throw Standard_NullValue("Geom2d_OffsetCurve::D2: Unable to calculate offset D2"); + } } -//================================================================================================= +//================================================================================================== void Geom2d_OffsetCurve::D3(const Standard_Real theU, gp_Pnt2d& theP, @@ -225,58 +234,59 @@ void Geom2d_OffsetCurve::D3(const Standard_Real theU, gp_Vec2d& theV2, gp_Vec2d& theV3) const { - myEvaluator->D3(theU, theP, theV1, theV2, theV3); + if (!Geom2d_OffsetUtils::EvaluateD3(theU, + basisCurve.get(), + offsetValue, + theP, + theV1, + theV2, + theV3)) + { + throw Standard_NullValue("Geom2d_OffsetCurve::D3: Unable to calculate offset D3"); + } } -//================================================================================================= +//================================================================================================== gp_Vec2d Geom2d_OffsetCurve::DN(const Standard_Real U, const Standard_Integer N) const { Standard_RangeError_Raise_if(N < 1, "Exception: Geom2d_OffsetCurve::DN(). N<1."); - gp_Vec2d VN, VBidon; - gp_Pnt2d PBidon; - switch (N) + gp_Vec2d aVN; + if (!Geom2d_OffsetUtils::EvaluateDN(U, basisCurve.get(), offsetValue, N, aVN)) { - case 1: - D1(U, PBidon, VN); - break; - case 2: - D2(U, PBidon, VBidon, VN); - break; - case 3: - D3(U, PBidon, VBidon, VBidon, VN); - break; - default: + if (N > 3) + { throw Standard_NotImplemented("Exception: Derivative order is greater than 3. " "Cannot compute of derivative."); + } + throw Standard_NullValue("Geom2d_OffsetCurve::DN: Unable to calculate offset DN"); } - - return VN; + return aVN; } -//================================================================================================= +//================================================================================================== Standard_Real Geom2d_OffsetCurve::FirstParameter() const { return basisCurve->FirstParameter(); } -//================================================================================================= +//================================================================================================== Standard_Real Geom2d_OffsetCurve::LastParameter() const { return basisCurve->LastParameter(); } -//================================================================================================= +//================================================================================================== Standard_Real Geom2d_OffsetCurve::Offset() const { return offsetValue; } -//================================================================================================= +//================================================================================================== Standard_Boolean Geom2d_OffsetCurve::IsClosed() const { @@ -286,7 +296,7 @@ Standard_Boolean Geom2d_OffsetCurve::IsClosed() const return (PF.Distance(PL) <= gp::Resolution()); } -//================================================================================================= +//================================================================================================== Standard_Boolean Geom2d_OffsetCurve::IsCN(const Standard_Integer N) const { @@ -294,31 +304,29 @@ Standard_Boolean Geom2d_OffsetCurve::IsCN(const Standard_Integer N) const return basisCurve->IsCN(N + 1); } -//================================================================================================= +//================================================================================================== Standard_Boolean Geom2d_OffsetCurve::IsPeriodic() const { return basisCurve->IsPeriodic(); } -//================================================================================================= +//================================================================================================== Standard_Real Geom2d_OffsetCurve::Period() const { return basisCurve->Period(); } -//================================================================================================= +//================================================================================================== void Geom2d_OffsetCurve::Transform(const gp_Trsf2d& T) { basisCurve->Transform(T); offsetValue *= std::abs(T.ScaleFactor()); - - myEvaluator->SetOffsetValue(offsetValue); } -//================================================================================================= +//================================================================================================== Standard_Real Geom2d_OffsetCurve::TransformedParameter(const Standard_Real U, const gp_Trsf2d& T) const @@ -326,21 +334,21 @@ Standard_Real Geom2d_OffsetCurve::TransformedParameter(const Standard_Real U, return basisCurve->TransformedParameter(U, T); } -//================================================================================================= +//================================================================================================== Standard_Real Geom2d_OffsetCurve::ParametricTransformation(const gp_Trsf2d& T) const { return basisCurve->ParametricTransformation(T); } -//================================================================================================= +//================================================================================================== GeomAbs_Shape Geom2d_OffsetCurve::GetBasisCurveContinuity() const { return myBasisCurveContinuity; } -//================================================================================================= +//================================================================================================== void Geom2d_OffsetCurve::DumpJson(Standard_OStream& theOStream, Standard_Integer theDepth) const { diff --git a/src/ModelingData/TKG2d/Geom2d/Geom2d_OffsetCurve.hxx b/src/ModelingData/TKG2d/Geom2d/Geom2d_OffsetCurve.hxx index 5e536246b9..6879d1dd76 100644 --- a/src/ModelingData/TKG2d/Geom2d/Geom2d_OffsetCurve.hxx +++ b/src/ModelingData/TKG2d/Geom2d/Geom2d_OffsetCurve.hxx @@ -23,7 +23,6 @@ #include #include #include -#include class gp_Pnt2d; class gp_Vec2d; @@ -293,12 +292,10 @@ public: DEFINE_STANDARD_RTTIEXT(Geom2d_OffsetCurve, Geom2d_Curve) -protected: private: - Handle(Geom2d_Curve) basisCurve; - Standard_Real offsetValue; - GeomAbs_Shape myBasisCurveContinuity; - Handle(Geom2dEvaluator_OffsetCurve) myEvaluator; + Handle(Geom2d_Curve) basisCurve; + double offsetValue; + GeomAbs_Shape myBasisCurveContinuity; }; #endif // _Geom2d_OffsetCurve_HeaderFile diff --git a/src/ModelingData/TKG2d/Geom2d/Geom2d_OffsetUtils.pxx b/src/ModelingData/TKG2d/Geom2d/Geom2d_OffsetUtils.pxx new file mode 100644 index 0000000000..a590d599a3 --- /dev/null +++ b/src/ModelingData/TKG2d/Geom2d/Geom2d_OffsetUtils.pxx @@ -0,0 +1,503 @@ +// Copyright (c) 2015-2025 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 _Geom2d_OffsetUtils_HeaderFile +#define _Geom2d_OffsetUtils_HeaderFile + +#include +#include +#include +#include +#include + +#include + +//! Internal helper namespace for 2D offset curve calculations. +//! Provides static inline functions to compute offset curve point and derivatives +//! from basis curve derivatives. +//! +//! These functions are used by Geom2d_OffsetCurve and Geom2dAdaptor_Curve. +//! +//! Mathematical basis: +//! P(u) = p(u) + Offset * N / ||N|| +//! where N = (p'(u).Y, -p'(u).X) is the normal direction (rotated tangent by 90 degrees) +namespace Geom2d_OffsetUtils +{ + +//! Calculates D0 (point) for 2D offset curve. +//! @param[in,out] theValue on input: basis curve point; on output: offset point +//! @param[in] theD1 first derivative of basis curve at the point +//! @param[in] theOffset offset distance value +//! @return true if successful, false if tangent vector has zero magnitude +inline bool CalculateD0(gp_Pnt2d& theValue, const gp_Vec2d& theD1, double theOffset) +{ + if (theD1.SquareMagnitude() <= gp::Resolution()) + { + return false; + } + + gp_Dir2d aNormal(theD1.Y(), -theD1.X()); + theValue.ChangeCoord().Add(aNormal.XY() * theOffset); + return true; +} + +//! Calculates D0 and D1 for 2D offset curve. +//! @param[in,out] theValue on input: basis curve point; on output: offset point +//! @param[in,out] theD1 on input: first derivative of basis; on output: offset curve D1 +//! @param[in] theD2 second derivative of basis curve +//! @param[in] theOffset offset distance value +//! @return true if successful, false if computation failed +inline bool CalculateD1(gp_Pnt2d& theValue, + gp_Vec2d& theD1, + const gp_Vec2d& theD2, + double theOffset) +{ + // P(u) = p(u) + Offset * Ndir / R + // with R = || p' ^ Z|| and Ndir = P' ^ Z + + // P'(u) = p'(u) + (Offset / R**2) * (DNdir/DU * R - Ndir * (DR/R)) + + gp_XY Ndir(theD1.Y(), -theD1.X()); + gp_XY DNdir(theD2.Y(), -theD2.X()); + double R2 = Ndir.SquareModulus(); + double R = std::sqrt(R2); + double R3 = R * R2; + double Dr = Ndir.Dot(DNdir); + if (R3 <= gp::Resolution()) + { + if (R2 <= gp::Resolution()) + { + return false; + } + // We try another computation but the stability is not very good. + DNdir.Multiply(R); + DNdir.Subtract(Ndir.Multiplied(Dr / R)); + DNdir.Multiply(theOffset / R2); + } + else + { + // Same computation as IICURV in EUCLID-IS because the stability is better + DNdir.Multiply(theOffset / R); + DNdir.Subtract(Ndir.Multiplied(theOffset * Dr / R3)); + } + + Ndir.Multiply(theOffset / R); + // P(u) + theValue.ChangeCoord().Add(Ndir); + // P'(u) + theD1.Add(gp_Vec2d(DNdir)); + return true; +} + +//! Calculates D0, D1, D2 for 2D offset curve. +//! @param[in,out] theValue on input: basis curve point; on output: offset point +//! @param[in,out] theD1 on input: first derivative of basis; on output: offset curve D1 +//! @param[in,out] theD2 on input: second derivative of basis; on output: offset curve D2 +//! @param[in] theD3 third derivative of basis curve +//! @param[in] theIsDirChange flag indicating direction change at singular point +//! @param[in] theOffset offset distance value +//! @return true if successful, false if computation failed +inline bool CalculateD2(gp_Pnt2d& theValue, + gp_Vec2d& theD1, + gp_Vec2d& theD2, + const gp_Vec2d& theD3, + bool theIsDirChange, + double theOffset) +{ + // P(u) = p(u) + Offset * Ndir / R + // with R = || p' ^ Z|| and Ndir = P' ^ Z + + // P'(u) = p'(u) + (Offset / R**2) * (DNdir/DU * R - Ndir * (DR/R)) + + // P"(u) = p"(u) + (Offset / R) * (D2Ndir/DU - DNdir * (2.0 * Dr/ R**2) + + // Ndir * ( (3.0 * Dr**2 / R**4) - (D2r / R**2))) + + gp_XY Ndir(theD1.Y(), -theD1.X()); + gp_XY DNdir(theD2.Y(), -theD2.X()); + gp_XY D2Ndir(theD3.Y(), -theD3.X()); + double R2 = Ndir.SquareModulus(); + double R = std::sqrt(R2); + double R3 = R2 * R; + double R4 = R2 * R2; + double R5 = R3 * R2; + double Dr = Ndir.Dot(DNdir); + double D2r = Ndir.Dot(D2Ndir) + DNdir.Dot(DNdir); + if (R5 <= gp::Resolution()) + { + if (R4 <= gp::Resolution()) + { + return false; + } + // We try another computation but the stability is not very good dixit ISG. + // V2 = P" (U) : + D2Ndir.Subtract(DNdir.Multiplied(2.0 * Dr / R2)); + D2Ndir.Add(Ndir.Multiplied(((3.0 * Dr * Dr) / R4) - (D2r / R2))); + D2Ndir.Multiply(theOffset / R); + + // V1 = P' (U) : + DNdir.Multiply(R); + DNdir.Subtract(Ndir.Multiplied(Dr / R)); + DNdir.Multiply(theOffset / R2); + } + else + { + // Same computation as IICURV in EUCLID-IS because the stability is better. + // V2 = P" (U) : + D2Ndir.Multiply(theOffset / R); + D2Ndir.Subtract(DNdir.Multiplied(2.0 * theOffset * Dr / R3)); + D2Ndir.Add(Ndir.Multiplied(theOffset * (((3.0 * Dr * Dr) / R5) - (D2r / R3)))); + + // V1 = P' (U) + DNdir.Multiply(theOffset / R); + DNdir.Subtract(Ndir.Multiplied(theOffset * Dr / R3)); + } + + Ndir.Multiply(theOffset / R); + // P(u) + theValue.ChangeCoord().Add(Ndir); + // P'(u) : + theD1.Add(gp_Vec2d(DNdir)); + // P"(u) : + if (theIsDirChange) + { + theD2.Reverse(); + } + theD2.Add(gp_Vec2d(D2Ndir)); + return true; +} + +//! Calculates D0, D1, D2, D3 for 2D offset curve. +//! @param[in,out] theValue on input: basis curve point; on output: offset point +//! @param[in,out] theD1 on input: first derivative of basis; on output: offset curve D1 +//! @param[in,out] theD2 on input: second derivative of basis; on output: offset curve D2 +//! @param[in,out] theD3 on input: third derivative of basis; on output: offset curve D3 +//! @param[in] theD4 fourth derivative of basis curve +//! @param[in] theIsDirChange flag indicating direction change at singular point +//! @param[in] theOffset offset distance value +//! @return true if successful, false if computation failed +inline bool CalculateD3(gp_Pnt2d& theValue, + gp_Vec2d& theD1, + gp_Vec2d& theD2, + gp_Vec2d& theD3, + const gp_Vec2d& theD4, + bool theIsDirChange, + double theOffset) +{ + // P(u) = p(u) + Offset * Ndir / R + // with R = || p' ^ Z|| and Ndir = P' ^ Z + + // P'(u) = p'(u) + (Offset / R**2) * (DNdir/DU * R - Ndir * (DR/R)) + + // P"(u) = p"(u) + (Offset / R) * (D2Ndir/DU - DNdir * (2.0 * Dr/ R**2) + + // Ndir * ( (3.0 * Dr**2 / R**4) - (D2r / R**2))) + + // P"'(u) = p"'(u) + (Offset / R) * (D3Ndir - (3.0 * Dr/R**2 ) * D2Ndir - + // (3.0 * D2r / R2) * DNdir) + (3.0 * Dr * Dr / R4) * DNdir - + // (D3r/R2) * Ndir + (6.0 * Dr * Dr / R4) * Ndir + + // (6.0 * Dr * D2r / R4) * Ndir - (15.0 * Dr* Dr* Dr /R6) * Ndir + + gp_XY Ndir(theD1.Y(), -theD1.X()); + gp_XY DNdir(theD2.Y(), -theD2.X()); + gp_XY D2Ndir(theD3.Y(), -theD3.X()); + gp_XY D3Ndir(theD4.Y(), -theD4.X()); + double R2 = Ndir.SquareModulus(); + double R = std::sqrt(R2); + double R3 = R2 * R; + double R4 = R2 * R2; + double R5 = R3 * R2; + double R6 = R3 * R3; + double R7 = R5 * R2; + double Dr = Ndir.Dot(DNdir); + double D2r = Ndir.Dot(D2Ndir) + DNdir.Dot(DNdir); + double D3r = Ndir.Dot(D3Ndir) + 3.0 * DNdir.Dot(D2Ndir); + + if (R7 <= gp::Resolution()) + { + if (R6 <= gp::Resolution()) + { + return false; + } + // We try another computation but the stability is not very good dixit ISG. + // V3 = P"' (U) : + D3Ndir.Subtract(D2Ndir.Multiplied(3.0 * Dr / R2)); + D3Ndir.Subtract(DNdir.Multiplied(3.0 * ((D2r / R2) + (Dr * Dr / R4)))); + D3Ndir.Add( + Ndir.Multiplied(6.0 * Dr * Dr / R4 + 6.0 * Dr * D2r / R4 - 15.0 * Dr * Dr * Dr / R6 - D3r)); + D3Ndir.Multiply(theOffset / R); + // V2 = P" (U) : + R4 = R2 * R2; + D2Ndir.Subtract(DNdir.Multiplied(2.0 * Dr / R2)); + D2Ndir.Subtract(Ndir.Multiplied((3.0 * Dr * Dr / R4) - (D2r / R2))); + D2Ndir.Multiply(theOffset / R); + // V1 = P' (U) : + DNdir.Multiply(R); + DNdir.Subtract(Ndir.Multiplied(Dr / R)); + DNdir.Multiply(theOffset / R2); + } + else + { + // Same computation as IICURV in EUCLID-IS because the stability is better. + // V3 = P"' (U) : + D3Ndir.Multiply(theOffset / R); + D3Ndir.Subtract(D2Ndir.Multiplied(3.0 * theOffset * Dr / R3)); + D3Ndir.Subtract(DNdir.Multiplied(((3.0 * theOffset) * ((D2r / R3) + (Dr * Dr) / R5)))); + D3Ndir.Add(Ndir.Multiplied( + (theOffset * (6.0 * Dr * Dr / R5 + 6.0 * Dr * D2r / R5 - 15.0 * Dr * Dr * Dr / R7 - D3r)))); + // V2 = P" (U) : + D2Ndir.Multiply(theOffset / R); + D2Ndir.Subtract(DNdir.Multiplied(2.0 * theOffset * Dr / R3)); + D2Ndir.Subtract(Ndir.Multiplied(theOffset * (((3.0 * Dr * Dr) / R5) - (D2r / R3)))); + // V1 = P' (U) : + DNdir.Multiply(theOffset / R); + DNdir.Subtract(Ndir.Multiplied(theOffset * Dr / R3)); + } + + Ndir.Multiply(theOffset / R); + // P(u) + theValue.ChangeCoord().Add(Ndir); + // P'(u) : + theD1.Add(gp_Vec2d(DNdir)); + // P"(u) + theD2.Add(gp_Vec2d(D2Ndir)); + // P"'(u) + if (theIsDirChange) + { + theD3.Reverse(); + } + theD3.Add(gp_Vec2d(D3Ndir)); + return true; +} + +//! Adjusts derivatives at singular points where the first derivative is nearly zero. +//! Uses Taylor series approximation to find a valid tangent direction. +//! @tparam CurveType type supporting D0, DN methods (Geom2d_Curve or adaptor) +//! @param[in] theCurve basis curve for derivative evaluation +//! @param[in] theMaxDerivative maximum derivative order to compute (3 or 4) +//! @param[in] theU parameter value +//! @param[in,out] theD1 first derivative (will be adjusted) +//! @param[in,out] theD2 second derivative (will be adjusted) +//! @param[in,out] theD3 third derivative (will be adjusted) +//! @param[in,out] theD4 fourth derivative (will be adjusted if theMaxDerivative >= 4) +//! @return true if direction change detected +template +bool AdjustDerivative(const CurveType& theCurve, + int theMaxDerivative, + double theU, + gp_Vec2d& theD1, + gp_Vec2d& theD2, + gp_Vec2d& theD3, + gp_Vec2d& theD4) +{ + static const double aTol = gp::Resolution(); + static const double aMinStep = 1e-7; + static const int aMaxDerivOrder = 3; + + bool isDirectionChange = false; + const double anUinfium = theCurve.FirstParameter(); + const double anUsupremum = theCurve.LastParameter(); + + static const double DivisionFactor = 1.e-3; + double du; + if ((anUsupremum >= RealLast()) || (anUinfium <= RealFirst())) + { + du = 0.0; + } + else + { + du = anUsupremum - anUinfium; + } + + const double aDelta = std::max(du * DivisionFactor, aMinStep); + + // Derivative is approximated by Taylor-series + int anIndex = 1; // Derivative order + gp_Vec2d V; + + do + { + V = theCurve.DN(theU, ++anIndex); + } while ((V.SquareMagnitude() <= aTol) && anIndex < aMaxDerivOrder); + + double u; + + if (theU - anUinfium < aDelta) + { + u = theU + aDelta; + } + else + { + u = theU - aDelta; + } + + gp_Pnt2d P1, P2; + theCurve.D0(std::min(theU, u), P1); + theCurve.D0(std::max(theU, u), P2); + + gp_Vec2d V1(P1, P2); + isDirectionChange = V.Dot(V1) < 0.0; + const double aSign = isDirectionChange ? -1.0 : 1.0; + + theD1 = V * aSign; + gp_Vec2d* aDeriv[3] = {&theD2, &theD3, &theD4}; + for (int i = 1; i < theMaxDerivative; i++) + { + *(aDeriv[i - 1]) = theCurve.DN(theU, anIndex + i) * aSign; + } + + return isDirectionChange; +} + +//! Template function for D0 evaluation of 2D offset curve. +//! Gets D1 from basis curve and computes offset point. +//! +//! @tparam BasisCurveType type of basis curve (must have D1 method) +//! @param[in] theU parameter value +//! @param[in] theBasisCurve basis curve +//! @param[in] theOffset offset distance +//! @param[out] theValue computed offset point +//! @return true if evaluation succeeded, false if normal is undefined +template +bool EvaluateD0(double theU, + const BasisCurveType& theBasisCurve, + double theOffset, + gp_Pnt2d& theValue) +{ + gp_Vec2d aD1; + theBasisCurve->D1(theU, theValue, aD1); + return CalculateD0(theValue, aD1, theOffset); +} + +//! Template function for D1 evaluation of 2D offset curve. +//! Gets D2 from basis curve and computes offset point and first derivative. +//! +//! @tparam BasisCurveType type of basis curve (must have D2 method) +//! @param[in] theU parameter value +//! @param[in] theBasisCurve basis curve +//! @param[in] theOffset offset distance +//! @param[out] theValue computed offset point +//! @param[out] theD1 computed first derivative +//! @return true if evaluation succeeded, false if computation failed +template +bool EvaluateD1(double theU, + const BasisCurveType& theBasisCurve, + double theOffset, + gp_Pnt2d& theValue, + gp_Vec2d& theD1) +{ + gp_Vec2d aD2; + theBasisCurve->D2(theU, theValue, theD1, aD2); + return CalculateD1(theValue, theD1, aD2, theOffset); +} + +//! Template function for D2 evaluation of 2D offset curve. +//! Gets D3 from basis curve and computes offset point and derivatives. +//! Handles singular points where first derivative is nearly zero. +//! +//! @tparam BasisCurveType type of basis curve (must have D3 and DN methods) +//! @param[in] theU parameter value +//! @param[in] theBasisCurve basis curve +//! @param[in] theOffset offset distance +//! @param[out] theValue computed offset point +//! @param[out] theD1 computed first derivative +//! @param[out] theD2 computed second derivative +//! @return true if evaluation succeeded, false if computation failed +template +bool EvaluateD2(double theU, + const BasisCurveType& theBasisCurve, + double theOffset, + gp_Pnt2d& theValue, + gp_Vec2d& theD1, + gp_Vec2d& theD2) +{ + gp_Vec2d aD3; + theBasisCurve->D3(theU, theValue, theD1, theD2, aD3); + + bool isDirectionChange = false; + if (theD1.SquareMagnitude() <= gp::Resolution()) + { + gp_Vec2d aDummyD4; + isDirectionChange = AdjustDerivative(*theBasisCurve, 3, theU, theD1, theD2, aD3, aDummyD4); + } + + return CalculateD2(theValue, theD1, theD2, aD3, isDirectionChange, theOffset); +} + +//! Template function for D3 evaluation of 2D offset curve. +//! Gets D3 and D4 from basis curve and computes offset point and derivatives. +//! Handles singular points where first derivative is nearly zero. +//! +//! @tparam BasisCurveType type of basis curve (must have D3 and DN methods) +//! @param[in] theU parameter value +//! @param[in] theBasisCurve basis curve +//! @param[in] theOffset offset distance +//! @param[out] theValue computed offset point +//! @param[out] theD1 computed first derivative +//! @param[out] theD2 computed second derivative +//! @param[out] theD3 computed third derivative +//! @return true if evaluation succeeded, false if computation failed +template +bool EvaluateD3(double theU, + const BasisCurveType& theBasisCurve, + double theOffset, + gp_Pnt2d& theValue, + gp_Vec2d& theD1, + gp_Vec2d& theD2, + gp_Vec2d& theD3) +{ + theBasisCurve->D3(theU, theValue, theD1, theD2, theD3); + gp_Vec2d aD4 = theBasisCurve->DN(theU, 4); + + bool isDirectionChange = false; + if (theD1.SquareMagnitude() <= gp::Resolution()) + { + isDirectionChange = AdjustDerivative(*theBasisCurve, 4, theU, theD1, theD2, theD3, aD4); + } + + return CalculateD3(theValue, theD1, theD2, theD3, aD4, isDirectionChange, theOffset); +} + +//! Template function for DN evaluation of 2D offset curve. +//! Handles derivatives up to order 3 using D1/D2/D3 methods. +//! +//! @tparam BasisCurveType type of basis curve (must have D1, D2, D3 methods) +//! @param[in] theU parameter value +//! @param[in] theBasisCurve basis curve +//! @param[in] theOffset offset distance +//! @param[in] theN derivative order (must be >= 1) +//! @param[out] theDN computed N-th derivative +//! @return true if evaluation succeeded (N <= 3), false if N > 3 or computation failed +template +bool EvaluateDN(double theU, + const BasisCurveType& theBasisCurve, + double theOffset, + int theN, + gp_Vec2d& theDN) +{ + gp_Pnt2d aPnt; + gp_Vec2d aDummy; + switch (theN) + { + case 1: + return EvaluateD1(theU, theBasisCurve, theOffset, aPnt, theDN); + case 2: + return EvaluateD2(theU, theBasisCurve, theOffset, aPnt, aDummy, theDN); + case 3: + return EvaluateD3(theU, theBasisCurve, theOffset, aPnt, aDummy, aDummy, theDN); + default: + return false; // N > 3 not supported + } +} + +} // namespace Geom2d_OffsetUtils + +#endif // _Geom2d_OffsetUtils_HeaderFile diff --git a/src/ModelingData/TKG2d/Geom2dAdaptor/Geom2dAdaptor_Curve.cxx b/src/ModelingData/TKG2d/Geom2dAdaptor/Geom2dAdaptor_Curve.cxx index 18c55bcd38..edeebe2d37 100644 --- a/src/ModelingData/TKG2d/Geom2dAdaptor/Geom2dAdaptor_Curve.cxx +++ b/src/ModelingData/TKG2d/Geom2dAdaptor/Geom2dAdaptor_Curve.cxx @@ -34,11 +34,12 @@ #include #include #include +#include #include #include #include -#include #include +#include #include #include #include @@ -50,6 +51,7 @@ #include #include #include +#include #include #include @@ -64,14 +66,32 @@ Handle(Adaptor2d_Curve2d) Geom2dAdaptor_Curve::ShallowCopy() const { Handle(Geom2dAdaptor_Curve) aCopy = new Geom2dAdaptor_Curve(); - aCopy->myCurve = myCurve; - aCopy->myTypeCurve = myTypeCurve; - aCopy->myFirst = myFirst; - aCopy->myLast = myLast; - aCopy->myBSplineCurve = myBSplineCurve; - if (!myNestedEvaluator.IsNull()) + aCopy->myCurve = myCurve; + aCopy->myTypeCurve = myTypeCurve; + aCopy->myFirst = myFirst; + aCopy->myLast = myLast; + + // Copy curve-specific data based on variant type + if (const auto* anOffsetData = std::get_if(&myCurveData)) + { + OffsetData aNewData; + if (!anOffsetData->BasisAdaptor.IsNull()) + { + aNewData.BasisAdaptor = + Handle(Geom2dAdaptor_Curve)::DownCast(anOffsetData->BasisAdaptor->ShallowCopy()); + } + aNewData.Offset = anOffsetData->Offset; + aCopy->myCurveData = std::move(aNewData); + } + else if (const auto* aBSplineData = std::get_if(&myCurveData)) + { + BSplineData aNewData; + aNewData.Curve = aBSplineData->Curve; + aCopy->myCurveData = std::move(aNewData); + } + else if (std::holds_alternative(myCurveData)) { - aCopy->myNestedEvaluator = myNestedEvaluator->ShallowCopy(); + aCopy->myCurveData = BezierData{}; } return aCopy; @@ -90,28 +110,29 @@ GeomAbs_Shape Geom2dAdaptor_Curve::LocalContinuity(const Standard_Real U1, const Standard_Real U2) const { Standard_NoSuchObject_Raise_if(myTypeCurve != GeomAbs_BSplineCurve, " "); - Standard_Integer Nb = myBSplineCurve->NbKnots(); - Standard_Integer Index1 = 0; - Standard_Integer Index2 = 0; + const auto& aBSpline = std::get(myCurveData).Curve; + Standard_Integer Nb = aBSpline->NbKnots(); + Standard_Integer Index1 = 0; + Standard_Integer Index2 = 0; Standard_Real newFirst, newLast; TColStd_Array1OfReal TK(1, Nb); TColStd_Array1OfInteger TM(1, Nb); - myBSplineCurve->Knots(TK); - myBSplineCurve->Multiplicities(TM); - BSplCLib::LocateParameter(myBSplineCurve->Degree(), + aBSpline->Knots(TK); + aBSpline->Multiplicities(TM); + BSplCLib::LocateParameter(aBSpline->Degree(), TK, TM, U1, - myBSplineCurve->IsPeriodic(), + aBSpline->IsPeriodic(), 1, Nb, Index1, newFirst); - BSplCLib::LocateParameter(myBSplineCurve->Degree(), + BSplCLib::LocateParameter(aBSpline->Degree(), TK, TM, U2, - myBSplineCurve->IsPeriodic(), + aBSpline->IsPeriodic(), 1, Nb, Index2, @@ -125,10 +146,10 @@ GeomAbs_Shape Geom2dAdaptor_Curve::LocalContinuity(const Standard_Real U1, Index2--; Standard_Integer MultMax; // attention aux courbes peridiques. - if (myBSplineCurve->IsPeriodic() && Index1 == Nb) + if (aBSpline->IsPeriodic() && Index1 == Nb) Index1 = 1; - if ((Index2 - Index1 <= 0) && (!myBSplineCurve->IsPeriodic())) + if ((Index2 - Index1 <= 0) && (!aBSpline->IsPeriodic())) { MultMax = 100; // CN entre 2 Noeuds consecutifs } @@ -140,7 +161,7 @@ GeomAbs_Shape Geom2dAdaptor_Curve::LocalContinuity(const Standard_Real U1, if (TM(i) > MultMax) MultMax = TM(i); } - MultMax = myBSplineCurve->Degree() - MultMax; + MultMax = aBSpline->Degree() - MultMax; } if (MultMax <= 0) { @@ -201,9 +222,7 @@ void Geom2dAdaptor_Curve::Reset() { myTypeCurve = GeomAbs_OtherCurve; myCurve.Nullify(); - myCurveCache.Nullify(); - myNestedEvaluator.Nullify(); - myBSplineCurve.Nullify(); + myCurveData = std::monostate{}; myFirst = myLast = 0.0; } @@ -215,13 +234,11 @@ void Geom2dAdaptor_Curve::load(const Handle(Geom2d_Curve)& C, { myFirst = UFirst; myLast = ULast; - myCurveCache.Nullify(); if (myCurve != C) { - myCurve = C; - myNestedEvaluator.Nullify(); - myBSplineCurve.Nullify(); + myCurve = C; + myCurveData = std::monostate{}; Handle(Standard_Type) TheType = C->DynamicType(); if (TheType == STANDARD_TYPE(Geom2d_TrimmedCurve)) @@ -251,26 +268,44 @@ void Geom2dAdaptor_Curve::load(const Handle(Geom2d_Curve)& C, else if (TheType == STANDARD_TYPE(Geom2d_BezierCurve)) { myTypeCurve = GeomAbs_BezierCurve; + myCurveData = BezierData{}; } else if (TheType == STANDARD_TYPE(Geom2d_BSplineCurve)) { - myTypeCurve = GeomAbs_BSplineCurve; - myBSplineCurve = Handle(Geom2d_BSplineCurve)::DownCast(myCurve); + myTypeCurve = GeomAbs_BSplineCurve; + BSplineData aBSplineData; + aBSplineData.Curve = Handle(Geom2d_BSplineCurve)::DownCast(myCurve); + myCurveData = std::move(aBSplineData); } else if (TheType == STANDARD_TYPE(Geom2d_OffsetCurve)) { myTypeCurve = GeomAbs_OffsetCurve; Handle(Geom2d_OffsetCurve) anOffsetCurve = Handle(Geom2d_OffsetCurve)::DownCast(myCurve); - // Create nested adaptor for base curve - Handle(Geom2d_Curve) aBaseCurve = anOffsetCurve->BasisCurve(); - Handle(Geom2dAdaptor_Curve) aBaseAdaptor = new Geom2dAdaptor_Curve(aBaseCurve); - myNestedEvaluator = new Geom2dEvaluator_OffsetCurve(aBaseAdaptor, anOffsetCurve->Offset()); + // Create nested adaptor for base curve and store offset data + Handle(Geom2d_Curve) aBaseCurve = anOffsetCurve->BasisCurve(); + + OffsetData anOffsetData; + anOffsetData.BasisAdaptor = new Geom2dAdaptor_Curve(aBaseCurve, UFirst, ULast); + anOffsetData.Offset = anOffsetCurve->Offset(); + myCurveData = std::move(anOffsetData); } else { myTypeCurve = GeomAbs_OtherCurve; } } + else + { + // Same curve but potentially different parameters - invalidate cache + if (auto* aBSplineData = std::get_if(&myCurveData)) + { + aBSplineData->Cache.Nullify(); + } + else if (auto* aBezierData = std::get_if(&myCurveData)) + { + aBezierData->Cache.Nullify(); + } + } } // -- @@ -324,12 +359,13 @@ Standard_Integer Geom2dAdaptor_Curve::NbIntervals(const GeomAbs_Shape S) const { if (myTypeCurve == GeomAbs_BSplineCurve) { - if ((!myBSplineCurve->IsPeriodic() && S <= Continuity()) || S == GeomAbs_C0) + const auto& aBSpline = std::get(myCurveData).Curve; + if ((!aBSpline->IsPeriodic() && S <= Continuity()) || S == GeomAbs_C0) { return 1; } - Standard_Integer aDegree = myBSplineCurve->Degree(); + Standard_Integer aDegree = aBSpline->Degree(); Standard_Integer aCont; switch (S) @@ -352,10 +388,10 @@ Standard_Integer Geom2dAdaptor_Curve::NbIntervals(const GeomAbs_Shape S) const Standard_Real anEps = std::min(Resolution(Precision::Confusion()), Precision::PConfusion()); - return BSplCLib::Intervals(myBSplineCurve->Knots(), - myBSplineCurve->Multiplicities(), + return BSplCLib::Intervals(aBSpline->Knots(), + aBSpline->Multiplicities(), aDegree, - myBSplineCurve->IsPeriodic(), + aBSpline->IsPeriodic(), aCont, myFirst, myLast, @@ -404,14 +440,15 @@ void Geom2dAdaptor_Curve::Intervals(TColStd_Array1OfReal& T, const GeomAbs_Shape { if (myTypeCurve == GeomAbs_BSplineCurve) { - if ((!myBSplineCurve->IsPeriodic() && S <= Continuity()) || S == GeomAbs_C0) + const auto& aBSpline = std::get(myCurveData).Curve; + if ((!aBSpline->IsPeriodic() && S <= Continuity()) || S == GeomAbs_C0) { T(T.Lower()) = myFirst; T(T.Lower() + 1) = myLast; return; } - Standard_Integer aDegree = myBSplineCurve->Degree(); + Standard_Integer aDegree = aBSpline->Degree(); Standard_Integer aCont; switch (S) @@ -434,10 +471,10 @@ void Geom2dAdaptor_Curve::Intervals(TColStd_Array1OfReal& T, const GeomAbs_Shape Standard_Real anEps = std::min(Resolution(Precision::Confusion()), Precision::PConfusion()); - BSplCLib::Intervals(myBSplineCurve->Knots(), - myBSplineCurve->Multiplicities(), + BSplCLib::Intervals(aBSpline->Knots(), + aBSpline->Multiplicities(), aDegree, - myBSplineCurve->IsPeriodic(), + aBSpline->IsPeriodic(), aCont, myFirst, myLast, @@ -530,30 +567,33 @@ void Geom2dAdaptor_Curve::RebuildCache(const Standard_Real theParameter) const if (myTypeCurve == GeomAbs_BezierCurve) { // Create cache for Bezier - Handle(Geom2d_BezierCurve) aBezier = Handle(Geom2d_BezierCurve)::DownCast(myCurve); - Standard_Integer aDeg = aBezier->Degree(); + auto& aBezierData = std::get(myCurveData); + Handle(Geom2d_BezierCurve) aBezier = Handle(Geom2d_BezierCurve)::DownCast(myCurve); + Standard_Integer aDeg = aBezier->Degree(); TColStd_Array1OfReal aFlatKnots(BSplCLib::FlatBezierKnots(aDeg), 1, 2 * (aDeg + 1)); - if (myCurveCache.IsNull()) - myCurveCache = new BSplCLib_Cache(aDeg, - aBezier->IsPeriodic(), - aFlatKnots, - aBezier->Poles(), - aBezier->Weights()); - myCurveCache->BuildCache(theParameter, aFlatKnots, aBezier->Poles(), aBezier->Weights()); + if (aBezierData.Cache.IsNull()) + aBezierData.Cache = new BSplCLib_Cache(aDeg, + aBezier->IsPeriodic(), + aFlatKnots, + aBezier->Poles(), + aBezier->Weights()); + aBezierData.Cache->BuildCache(theParameter, aFlatKnots, aBezier->Poles(), aBezier->Weights()); } else if (myTypeCurve == GeomAbs_BSplineCurve) { // Create cache for B-spline - if (myCurveCache.IsNull()) - myCurveCache = new BSplCLib_Cache(myBSplineCurve->Degree(), - myBSplineCurve->IsPeriodic(), - myBSplineCurve->KnotSequence(), - myBSplineCurve->Poles(), - myBSplineCurve->Weights()); - myCurveCache->BuildCache(theParameter, - myBSplineCurve->KnotSequence(), - myBSplineCurve->Poles(), - myBSplineCurve->Weights()); + auto& aBSplineData = std::get(myCurveData); + const auto& aBSpline = aBSplineData.Curve; + if (aBSplineData.Cache.IsNull()) + aBSplineData.Cache = new BSplCLib_Cache(aBSpline->Degree(), + aBSpline->IsPeriodic(), + aBSpline->KnotSequence(), + aBSpline->Poles(), + aBSpline->Weights()); + aBSplineData.Cache->BuildCache(theParameter, + aBSpline->KnotSequence(), + aBSpline->Poles(), + aBSpline->Weights()); } } @@ -563,11 +603,14 @@ Standard_Boolean Geom2dAdaptor_Curve::IsBoundary(const Standard_Real theU, Standard_Integer& theSpanStart, Standard_Integer& theSpanFinish) const { - if (!myBSplineCurve.IsNull() && (theU == myFirst || theU == myLast)) + const auto* aBSplineData = std::get_if(&myCurveData); + if (aBSplineData != nullptr && !aBSplineData->Curve.IsNull() + && (theU == myFirst || theU == myLast)) { + const auto& aBSpline = aBSplineData->Curve; if (theU == myFirst) { - myBSplineCurve->LocateU(myFirst, PosTol, theSpanStart, theSpanFinish); + aBSpline->LocateU(myFirst, PosTol, theSpanStart, theSpanFinish); if (theSpanStart < 1) theSpanStart = 1; if (theSpanStart >= theSpanFinish) @@ -575,9 +618,9 @@ Standard_Boolean Geom2dAdaptor_Curve::IsBoundary(const Standard_Real theU, } else if (theU == myLast) { - myBSplineCurve->LocateU(myLast, PosTol, theSpanStart, theSpanFinish); - if (theSpanFinish > myBSplineCurve->NbKnots()) - theSpanFinish = myBSplineCurve->NbKnots(); + aBSpline->LocateU(myLast, PosTol, theSpanStart, theSpanFinish); + if (theSpanFinish > aBSpline->NbKnots()) + theSpanFinish = aBSpline->NbKnots(); if (theSpanStart >= theSpanFinish) theSpanStart = theSpanFinish - 1; } @@ -601,26 +644,43 @@ void Geom2dAdaptor_Curve::D0(const Standard_Real U, gp_Pnt2d& P) const { switch (myTypeCurve) { - case GeomAbs_BezierCurve: + case GeomAbs_BezierCurve: { + auto& aBezierData = std::get(myCurveData); + // use cached data + if (aBezierData.Cache.IsNull() || !aBezierData.Cache->IsCacheValid(U)) + RebuildCache(U); + aBezierData.Cache->D0(U, P); + break; + } + case GeomAbs_BSplineCurve: { + auto& aBSplineData = std::get(myCurveData); Standard_Integer aStart = 0, aFinish = 0; if (IsBoundary(U, aStart, aFinish)) { - myBSplineCurve->LocalD0(U, aStart, aFinish, P); + aBSplineData.Curve->LocalD0(U, aStart, aFinish, P); } else { // use cached data - if (myCurveCache.IsNull() || !myCurveCache->IsCacheValid(U)) + if (aBSplineData.Cache.IsNull() || !aBSplineData.Cache->IsCacheValid(U)) RebuildCache(U); - myCurveCache->D0(U, P); + aBSplineData.Cache->D0(U, P); } break; } - case GeomAbs_OffsetCurve: - myNestedEvaluator->D0(U, P); + case GeomAbs_OffsetCurve: { + const auto& anOffsetData = std::get(myCurveData); + if (!Geom2d_OffsetUtils::EvaluateD0(U, + anOffsetData.BasisAdaptor.get(), + anOffsetData.Offset, + P)) + { + throw Standard_NullValue("Geom2dAdaptor_Curve::D0: Unable to calculate offset point"); + } break; + } default: myCurve->D0(U, P); @@ -633,26 +693,44 @@ void Geom2dAdaptor_Curve::D1(const Standard_Real U, gp_Pnt2d& P, gp_Vec2d& V) co { switch (myTypeCurve) { - case GeomAbs_BezierCurve: + case GeomAbs_BezierCurve: { + auto& aBezierData = std::get(myCurveData); + // use cached data + if (aBezierData.Cache.IsNull() || !aBezierData.Cache->IsCacheValid(U)) + RebuildCache(U); + aBezierData.Cache->D1(U, P, V); + break; + } + case GeomAbs_BSplineCurve: { + auto& aBSplineData = std::get(myCurveData); Standard_Integer aStart = 0, aFinish = 0; if (IsBoundary(U, aStart, aFinish)) { - myBSplineCurve->LocalD1(U, aStart, aFinish, P, V); + aBSplineData.Curve->LocalD1(U, aStart, aFinish, P, V); } else { // use cached data - if (myCurveCache.IsNull() || !myCurveCache->IsCacheValid(U)) + if (aBSplineData.Cache.IsNull() || !aBSplineData.Cache->IsCacheValid(U)) RebuildCache(U); - myCurveCache->D1(U, P, V); + aBSplineData.Cache->D1(U, P, V); } break; } - case GeomAbs_OffsetCurve: - myNestedEvaluator->D1(U, P, V); + case GeomAbs_OffsetCurve: { + const auto& anOffsetData = std::get(myCurveData); + if (!Geom2d_OffsetUtils::EvaluateD1(U, + anOffsetData.BasisAdaptor.get(), + anOffsetData.Offset, + P, + V)) + { + throw Standard_NullValue("Geom2dAdaptor_Curve::D1: Unable to calculate offset D1"); + } break; + } default: myCurve->D1(U, P, V); @@ -665,26 +743,45 @@ void Geom2dAdaptor_Curve::D2(const Standard_Real U, gp_Pnt2d& P, gp_Vec2d& V1, g { switch (myTypeCurve) { - case GeomAbs_BezierCurve: + case GeomAbs_BezierCurve: { + auto& aBezierData = std::get(myCurveData); + // use cached data + if (aBezierData.Cache.IsNull() || !aBezierData.Cache->IsCacheValid(U)) + RebuildCache(U); + aBezierData.Cache->D2(U, P, V1, V2); + break; + } + case GeomAbs_BSplineCurve: { + auto& aBSplineData = std::get(myCurveData); Standard_Integer aStart = 0, aFinish = 0; if (IsBoundary(U, aStart, aFinish)) { - myBSplineCurve->LocalD2(U, aStart, aFinish, P, V1, V2); + aBSplineData.Curve->LocalD2(U, aStart, aFinish, P, V1, V2); } else { // use cached data - if (myCurveCache.IsNull() || !myCurveCache->IsCacheValid(U)) + if (aBSplineData.Cache.IsNull() || !aBSplineData.Cache->IsCacheValid(U)) RebuildCache(U); - myCurveCache->D2(U, P, V1, V2); + aBSplineData.Cache->D2(U, P, V1, V2); } break; } - case GeomAbs_OffsetCurve: - myNestedEvaluator->D2(U, P, V1, V2); + case GeomAbs_OffsetCurve: { + const auto& anOffsetData = std::get(myCurveData); + if (!Geom2d_OffsetUtils::EvaluateD2(U, + anOffsetData.BasisAdaptor.get(), + anOffsetData.Offset, + P, + V1, + V2)) + { + throw Standard_NullValue("Geom2dAdaptor_Curve::D2: Unable to calculate offset D2"); + } break; + } default: myCurve->D2(U, P, V1, V2); @@ -701,26 +798,46 @@ void Geom2dAdaptor_Curve::D3(const Standard_Real U, { switch (myTypeCurve) { - case GeomAbs_BezierCurve: + case GeomAbs_BezierCurve: { + auto& aBezierData = std::get(myCurveData); + // use cached data + if (aBezierData.Cache.IsNull() || !aBezierData.Cache->IsCacheValid(U)) + RebuildCache(U); + aBezierData.Cache->D3(U, P, V1, V2, V3); + break; + } + case GeomAbs_BSplineCurve: { + auto& aBSplineData = std::get(myCurveData); Standard_Integer aStart = 0, aFinish = 0; if (IsBoundary(U, aStart, aFinish)) { - myBSplineCurve->LocalD3(U, aStart, aFinish, P, V1, V2, V3); + aBSplineData.Curve->LocalD3(U, aStart, aFinish, P, V1, V2, V3); } else { // use cached data - if (myCurveCache.IsNull() || !myCurveCache->IsCacheValid(U)) + if (aBSplineData.Cache.IsNull() || !aBSplineData.Cache->IsCacheValid(U)) RebuildCache(U); - myCurveCache->D3(U, P, V1, V2, V3); + aBSplineData.Cache->D3(U, P, V1, V2, V3); } break; } - case GeomAbs_OffsetCurve: - myNestedEvaluator->D3(U, P, V1, V2, V3); + case GeomAbs_OffsetCurve: { + const auto& anOffsetData = std::get(myCurveData); + if (!Geom2d_OffsetUtils::EvaluateD3(U, + anOffsetData.BasisAdaptor.get(), + anOffsetData.Offset, + P, + V1, + V2, + V3)) + { + throw Standard_NullValue("Geom2dAdaptor_Curve::D3: Unable to calculate offset D3"); + } break; + } default: myCurve->D3(U, P, V1, V2, V3); @@ -734,20 +851,37 @@ gp_Vec2d Geom2dAdaptor_Curve::DN(const Standard_Real U, const Standard_Integer N switch (myTypeCurve) { case GeomAbs_BezierCurve: + return myCurve->DN(U, N); + case GeomAbs_BSplineCurve: { Standard_Integer aStart = 0, aFinish = 0; if (IsBoundary(U, aStart, aFinish)) { - myBSplineCurve->LocalDN(U, aStart, aFinish, N); + return std::get(myCurveData).Curve->LocalDN(U, aStart, aFinish, N); } - else - return myCurve->DN(U, N); - break; + return myCurve->DN(U, N); } - case GeomAbs_OffsetCurve: - return myNestedEvaluator->DN(U, N); - break; + case GeomAbs_OffsetCurve: { + Standard_RangeError_Raise_if(N < 1, "Geom2dAdaptor_Curve::DN(): N < 1"); + + const auto& anOffsetData = std::get(myCurveData); + gp_Vec2d aDN; + if (!Geom2d_OffsetUtils::EvaluateDN(U, + anOffsetData.BasisAdaptor.get(), + anOffsetData.Offset, + N, + aDN)) + { + if (N > 3) + { + throw Standard_NotImplemented( + "Geom2dAdaptor_Curve::DN: Derivative order > 3 not supported"); + } + throw Standard_NullValue("Geom2dAdaptor_Curve::DN: Unable to calculate offset DN"); + } + return aDN; + } default: // to eliminate gcc warning break; @@ -845,7 +979,7 @@ Standard_Integer Geom2dAdaptor_Curve::Degree() const if (myTypeCurve == GeomAbs_BezierCurve) return Handle(Geom2d_BezierCurve)::DownCast(myCurve)->Degree(); else if (myTypeCurve == GeomAbs_BSplineCurve) - return myBSplineCurve->Degree(); + return std::get(myCurveData).Curve->Degree(); else throw Standard_NoSuchObject(); } @@ -857,7 +991,7 @@ Standard_Boolean Geom2dAdaptor_Curve::IsRational() const switch (myTypeCurve) { case GeomAbs_BSplineCurve: - return myBSplineCurve->IsRational(); + return std::get(myCurveData).Curve->IsRational(); case GeomAbs_BezierCurve: return Handle(Geom2d_BezierCurve)::DownCast(myCurve)->IsRational(); default: @@ -872,7 +1006,7 @@ Standard_Integer Geom2dAdaptor_Curve::NbPoles() const if (myTypeCurve == GeomAbs_BezierCurve) return Handle(Geom2d_BezierCurve)::DownCast(myCurve)->NbPoles(); else if (myTypeCurve == GeomAbs_BSplineCurve) - return myBSplineCurve->NbPoles(); + return std::get(myCurveData).Curve->NbPoles(); else throw Standard_NoSuchObject(); } @@ -883,7 +1017,7 @@ Standard_Integer Geom2dAdaptor_Curve::NbKnots() const { if (myTypeCurve != GeomAbs_BSplineCurve) throw Standard_NoSuchObject("Geom2dAdaptor_Curve::NbKnots"); - return myBSplineCurve->NbKnots(); + return std::get(myCurveData).Curve->NbKnots(); } //================================================================================================= @@ -897,7 +1031,11 @@ Handle(Geom2d_BezierCurve) Geom2dAdaptor_Curve::Bezier() const Handle(Geom2d_BSplineCurve) Geom2dAdaptor_Curve::BSpline() const { - return myBSplineCurve; + if (const auto* aBSplineData = std::get_if(&myCurveData)) + { + return aBSplineData->Curve; + } + return Handle(Geom2d_BSplineCurve)(); } static Standard_Integer nbPoints(const Handle(Geom2d_Curve)& theCurve) diff --git a/src/ModelingData/TKG2d/Geom2dAdaptor/Geom2dAdaptor_Curve.hxx b/src/ModelingData/TKG2d/Geom2dAdaptor/Geom2dAdaptor_Curve.hxx index 0dba174a8b..8d6c2e9964 100644 --- a/src/ModelingData/TKG2d/Geom2dAdaptor/Geom2dAdaptor_Curve.hxx +++ b/src/ModelingData/TKG2d/Geom2dAdaptor/Geom2dAdaptor_Curve.hxx @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -28,6 +27,8 @@ #include #include +#include + class gp_Vec2d; class gp_Lin2d; class gp_Circ2d; @@ -36,6 +37,9 @@ class gp_Hypr2d; class gp_Parab2d; class Geom2d_BezierCurve; class Geom2d_BSplineCurve; +class Geom2dAdaptor_Curve; + +DEFINE_STANDARD_HANDLE(Geom2dAdaptor_Curve, Adaptor2d_Curve2d) //! An interface between the services provided by any //! curve from the package Geom2d and those required @@ -47,6 +51,30 @@ class Geom2d_BSplineCurve; class Geom2dAdaptor_Curve : public Adaptor2d_Curve2d { DEFINE_STANDARD_RTTIEXT(Geom2dAdaptor_Curve, Adaptor2d_Curve2d) +public: + //! Internal structure for 2D offset curve evaluation data. + struct OffsetData + { + Handle(Geom2dAdaptor_Curve) BasisAdaptor; //!< Adaptor for basis curve + double Offset = 0.0; //!< Offset distance + }; + + //! Internal structure for Bezier curve evaluation data. + struct BezierData + { + mutable Handle(BSplCLib_Cache) Cache; //!< Cached data for evaluation + }; + + //! Internal structure for BSpline curve evaluation data. + struct BSplineData + { + Handle(Geom2d_BSplineCurve) Curve; //!< BSpline curve to prevent downcasts + mutable Handle(BSplCLib_Cache) Cache; //!< Cached data for evaluation + }; + + //! Variant type for 2D curve-specific evaluation data. + using CurveDataVariant = std::variant; + public: Standard_EXPORT Geom2dAdaptor_Curve(); @@ -214,12 +242,7 @@ protected: GeomAbs_CurveType myTypeCurve; Standard_Real myFirst; Standard_Real myLast; - - Handle(Geom2d_BSplineCurve) myBSplineCurve; ///< B-spline representation to prevent castings - mutable Handle(BSplCLib_Cache) myCurveCache; ///< Cached data for B-spline or Bezier curve - Handle(Geom2dEvaluator_Curve) myNestedEvaluator; ///< Calculates value of offset curve + CurveDataVariant myCurveData; ///< Curve-specific evaluation data (BSpline, Bezier, offset) }; -DEFINE_STANDARD_HANDLE(Geom2dAdaptor_Curve, Adaptor2d_Curve2d) - #endif // _Geom2dAdaptor_Curve_HeaderFile diff --git a/src/ModelingData/TKG2d/Geom2dEvaluator/FILES.cmake b/src/ModelingData/TKG2d/Geom2dEvaluator/FILES.cmake deleted file mode 100644 index 87d08f6969..0000000000 --- a/src/ModelingData/TKG2d/Geom2dEvaluator/FILES.cmake +++ /dev/null @@ -1,10 +0,0 @@ -# Source files for Geom2dEvaluator package -set(OCCT_Geom2dEvaluator_FILES_LOCATION "${CMAKE_CURRENT_LIST_DIR}") - -set(OCCT_Geom2dEvaluator_FILES - Geom2dEvaluator_Curve.hxx - Geom2dEvaluator_OffsetCurve.cxx - Geom2dEvaluator_OffsetCurve.hxx - Geom2dEvaluator.hxx - Geom2dEvaluator.cxx -) diff --git a/src/ModelingData/TKG2d/Geom2dEvaluator/Geom2dEvaluator.cxx b/src/ModelingData/TKG2d/Geom2dEvaluator/Geom2dEvaluator.cxx deleted file mode 100644 index 753cc44eea..0000000000 --- a/src/ModelingData/TKG2d/Geom2dEvaluator/Geom2dEvaluator.cxx +++ /dev/null @@ -1,230 +0,0 @@ -// Created on: 2015-09-21 -// Copyright (c) 2015 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. - -#include -#include -#include -#include -#include - -//================================================================================================= - -void Geom2dEvaluator::CalculateD0(gp_Pnt2d& theValue, - const gp_Vec2d& theD1, - const Standard_Real theOffset) -{ - if (theD1.SquareMagnitude() <= gp::Resolution()) - throw Standard_NullValue("Geom2dEvaluator: Undefined normal vector " - "because tangent vector has zero-magnitude!"); - - gp_Dir2d aNormal(theD1.Y(), -theD1.X()); - theValue.ChangeCoord().Add(aNormal.XY() * theOffset); -} - -//================================================================================================= - -void Geom2dEvaluator::CalculateD1(gp_Pnt2d& theValue, - gp_Vec2d& theD1, - const gp_Vec2d& theD2, - const Standard_Real theOffset) -{ - // P(u) = p(u) + Offset * Ndir / R - // with R = || p' ^ Z|| and Ndir = P' ^ Z - - // P'(u) = p'(u) + (Offset / R**2) * (DNdir/DU * R - Ndir * (DR/R)) - - gp_XY Ndir(theD1.Y(), -theD1.X()); - gp_XY DNdir(theD2.Y(), -theD2.X()); - Standard_Real R2 = Ndir.SquareModulus(); - Standard_Real R = std::sqrt(R2); - Standard_Real R3 = R * R2; - Standard_Real Dr = Ndir.Dot(DNdir); - if (R3 <= gp::Resolution()) - { - if (R2 <= gp::Resolution()) - throw Standard_NullValue("Geom2dEvaluator_OffsetCurve: Null derivative"); - // We try another computation but the stability is not very good. - DNdir.Multiply(R); - DNdir.Subtract(Ndir.Multiplied(Dr / R)); - DNdir.Multiply(theOffset / R2); - } - else - { - // Same computation as IICURV in EUCLID-IS because the stability is better - DNdir.Multiply(theOffset / R); - DNdir.Subtract(Ndir.Multiplied(theOffset * Dr / R3)); - } - - Ndir.Multiply(theOffset / R); - // P(u) - theValue.ChangeCoord().Add(Ndir); - // P'(u) - theD1.Add(gp_Vec2d(DNdir)); -} - -//================================================================================================= - -void Geom2dEvaluator::CalculateD2(gp_Pnt2d& theValue, - gp_Vec2d& theD1, - gp_Vec2d& theD2, - const gp_Vec2d& theD3, - const Standard_Boolean theIsDirChange, - const Standard_Real theOffset) -{ - // P(u) = p(u) + Offset * Ndir / R - // with R = || p' ^ Z|| and Ndir = P' ^ Z - - // P'(u) = p'(u) + (Offset / R**2) * (DNdir/DU * R - Ndir * (DR/R)) - - // P"(u) = p"(u) + (Offset / R) * (D2Ndir/DU - DNdir * (2.0 * Dr/ R**2) + - // Ndir * ( (3.0 * Dr**2 / R**4) - (D2r / R**2))) - - gp_XY Ndir(theD1.Y(), -theD1.X()); - gp_XY DNdir(theD2.Y(), -theD2.X()); - gp_XY D2Ndir(theD3.Y(), -theD3.X()); - Standard_Real R2 = Ndir.SquareModulus(); - Standard_Real R = std::sqrt(R2); - Standard_Real R3 = R2 * R; - Standard_Real R4 = R2 * R2; - Standard_Real R5 = R3 * R2; - Standard_Real Dr = Ndir.Dot(DNdir); - Standard_Real D2r = Ndir.Dot(D2Ndir) + DNdir.Dot(DNdir); - if (R5 <= gp::Resolution()) - { - if (R4 <= gp::Resolution()) - throw Standard_NullValue("Geom2dEvaluator: Null derivative"); - // We try another computation but the stability is not very good dixit ISG. - // V2 = P" (U) : - D2Ndir.Subtract(DNdir.Multiplied(2.0 * Dr / R2)); - D2Ndir.Add(Ndir.Multiplied(((3.0 * Dr * Dr) / R4) - (D2r / R2))); - D2Ndir.Multiply(theOffset / R); - - // V1 = P' (U) : - DNdir.Multiply(R); - DNdir.Subtract(Ndir.Multiplied(Dr / R)); - DNdir.Multiply(theOffset / R2); - } - else - { - // Same computation as IICURV in EUCLID-IS because the stability is better. - // V2 = P" (U) : - D2Ndir.Multiply(theOffset / R); - D2Ndir.Subtract(DNdir.Multiplied(2.0 * theOffset * Dr / R3)); - D2Ndir.Add(Ndir.Multiplied(theOffset * (((3.0 * Dr * Dr) / R5) - (D2r / R3)))); - - // V1 = P' (U) - DNdir.Multiply(theOffset / R); - DNdir.Subtract(Ndir.Multiplied(theOffset * Dr / R3)); - } - - Ndir.Multiply(theOffset / R); - // P(u) - theValue.ChangeCoord().Add(Ndir); - // P'(u) : - theD1.Add(gp_Vec2d(DNdir)); - // P"(u) : - if (theIsDirChange) - theD2.Reverse(); - theD2.Add(gp_Vec2d(D2Ndir)); -} - -//================================================================================================= - -void Geom2dEvaluator::CalculateD3(gp_Pnt2d& theValue, - gp_Vec2d& theD1, - gp_Vec2d& theD2, - gp_Vec2d& theD3, - const gp_Vec2d& theD4, - const Standard_Boolean theIsDirChange, - const Standard_Real theOffset) -{ - // P(u) = p(u) + Offset * Ndir / R - // with R = || p' ^ Z|| and Ndir = P' ^ Z - - // P'(u) = p'(u) + (Offset / R**2) * (DNdir/DU * R - Ndir * (DR/R)) - - // P"(u) = p"(u) + (Offset / R) * (D2Ndir/DU - DNdir * (2.0 * Dr/ R**2) + - // Ndir * ( (3.0 * Dr**2 / R**4) - (D2r / R**2))) - - // P"'(u) = p"'(u) + (Offset / R) * (D3Ndir - (3.0 * Dr/R**2 ) * D2Ndir - - // (3.0 * D2r / R2) * DNdir) + (3.0 * Dr * Dr / R4) * DNdir - - // (D3r/R2) * Ndir + (6.0 * Dr * Dr / R4) * Ndir + - // (6.0 * Dr * D2r / R4) * Ndir - (15.0 * Dr* Dr* Dr /R6) * Ndir - - gp_XY Ndir(theD1.Y(), -theD1.X()); - gp_XY DNdir(theD2.Y(), -theD2.X()); - gp_XY D2Ndir(theD3.Y(), -theD3.X()); - gp_XY D3Ndir(theD4.Y(), -theD4.X()); - Standard_Real R2 = Ndir.SquareModulus(); - Standard_Real R = std::sqrt(R2); - Standard_Real R3 = R2 * R; - Standard_Real R4 = R2 * R2; - Standard_Real R5 = R3 * R2; - Standard_Real R6 = R3 * R3; - Standard_Real R7 = R5 * R2; - Standard_Real Dr = Ndir.Dot(DNdir); - Standard_Real D2r = Ndir.Dot(D2Ndir) + DNdir.Dot(DNdir); - Standard_Real D3r = Ndir.Dot(D3Ndir) + 3.0 * DNdir.Dot(D2Ndir); - - if (R7 <= gp::Resolution()) - { - if (R6 <= gp::Resolution()) - throw Standard_NullValue("Geom2dEvaluator: Null derivative"); - // We try another computation but the stability is not very good dixit ISG. - // V3 = P"' (U) : - D3Ndir.Subtract(D2Ndir.Multiplied(3.0 * theOffset * Dr / R2)); - D3Ndir.Subtract((DNdir.Multiplied((3.0 * theOffset) * ((D2r / R2) + (Dr * Dr) / R4)))); - D3Ndir.Add(Ndir.Multiplied( - (theOffset * (6.0 * Dr * Dr / R4 + 6.0 * Dr * D2r / R4 - 15.0 * Dr * Dr * Dr / R6 - D3r)))); - D3Ndir.Multiply(theOffset / R); - // V2 = P" (U) : - R4 = R2 * R2; - D2Ndir.Subtract(DNdir.Multiplied(2.0 * Dr / R2)); - D2Ndir.Subtract(Ndir.Multiplied(((3.0 * Dr * Dr) / R4) - (D2r / R2))); - D2Ndir.Multiply(theOffset / R); - // V1 = P' (U) : - DNdir.Multiply(R); - DNdir.Subtract(Ndir.Multiplied(Dr / R)); - DNdir.Multiply(theOffset / R2); - } - else - { - // Same computation as IICURV in EUCLID-IS because the stability is better. - // V3 = P"' (U) : - D3Ndir.Multiply(theOffset / R); - D3Ndir.Subtract(D2Ndir.Multiplied(3.0 * theOffset * Dr / R3)); - D3Ndir.Subtract(DNdir.Multiplied(((3.0 * theOffset) * ((D2r / R3) + (Dr * Dr) / R5)))); - D3Ndir.Add(Ndir.Multiplied( - (theOffset * (6.0 * Dr * Dr / R5 + 6.0 * Dr * D2r / R5 - 15.0 * Dr * Dr * Dr / R7 - D3r)))); - // V2 = P" (U) : - D2Ndir.Multiply(theOffset / R); - D2Ndir.Subtract(DNdir.Multiplied(2.0 * theOffset * Dr / R3)); - D2Ndir.Subtract(Ndir.Multiplied(theOffset * (((3.0 * Dr * Dr) / R5) - (D2r / R3)))); - // V1 = P' (U) : - DNdir.Multiply(theOffset / R); - DNdir.Subtract(Ndir.Multiplied(theOffset * Dr / R3)); - } - - Ndir.Multiply(theOffset / R); - // P(u) - theValue.ChangeCoord().Add(Ndir); - // P'(u) : - theD1.Add(gp_Vec2d(DNdir)); - // P"(u) - theD2.Add(gp_Vec2d(D2Ndir)); - // P"'(u) - if (theIsDirChange) - theD3.Reverse(); - theD3.Add(gp_Vec2d(D2Ndir)); -} diff --git a/src/ModelingData/TKG2d/Geom2dEvaluator/Geom2dEvaluator.hxx b/src/ModelingData/TKG2d/Geom2dEvaluator/Geom2dEvaluator.hxx deleted file mode 100644 index a794468a79..0000000000 --- a/src/ModelingData/TKG2d/Geom2dEvaluator/Geom2dEvaluator.hxx +++ /dev/null @@ -1,76 +0,0 @@ -// Created on: 1992-08-28 -// Created by: Remi LEQUETTE -// Copyright (c) 1992-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 _Geom2dEvaluator_HeaderFile -#define _Geom2dEvaluator_HeaderFile - -#include -#include - -class gp_Pnt2d; -class gp_Vec2d; - -//! The Geom2dEvaluator package provides utilities for -//! calculating value and derivatives of offset curve -//! using corresponding values of base curve. - -class Geom2dEvaluator -{ -public: - DEFINE_STANDARD_ALLOC - - //! Recalculate D1 values of base curve into D0 value of offset curve - Standard_EXPORT static void CalculateD0(gp_Pnt2d& theValue, - const gp_Vec2d& theD1, - const Standard_Real theOffset); - - //! Recalculate D2 values of base curve into D1 values of offset curve - Standard_EXPORT static void CalculateD1(gp_Pnt2d& theValue, - gp_Vec2d& theD1, - const gp_Vec2d& theD2, - const Standard_Real theOffset); - - //! Recalculate D3 values of base curve into D2 values of offset curve - Standard_EXPORT static void CalculateD2(gp_Pnt2d& theValue, - gp_Vec2d& theD1, - gp_Vec2d& theD2, - const gp_Vec2d& theD3, - const Standard_Boolean theIsDirChange, - const Standard_Real theOffset); - - //! Recalculate D3 values of base curve into D3 values of offset curve - Standard_EXPORT static void CalculateD3(gp_Pnt2d& theValue, - gp_Vec2d& theD1, - gp_Vec2d& theD2, - gp_Vec2d& theD3, - const gp_Vec2d& theD4, - const Standard_Boolean theIsDirChange, - const Standard_Real theOffset); - - //! Recalculate derivatives in the singular point - //! Returns true if the direction of derivatives is changed - Standard_EXPORT static Standard_Boolean AdjustDerivative(const Standard_Integer theMaxDerivative, - const Standard_Real theU, - gp_Vec2d& theD1, - gp_Vec2d& theD2, - gp_Vec2d& theD3, - gp_Vec2d& theD4); - -protected: -private: -}; - -#endif // _Geom2dEvaluator_HeaderFile diff --git a/src/ModelingData/TKG2d/Geom2dEvaluator/Geom2dEvaluator_Curve.hxx b/src/ModelingData/TKG2d/Geom2dEvaluator/Geom2dEvaluator_Curve.hxx deleted file mode 100644 index 186266b128..0000000000 --- a/src/ModelingData/TKG2d/Geom2dEvaluator/Geom2dEvaluator_Curve.hxx +++ /dev/null @@ -1,56 +0,0 @@ -// Created on: 2015-09-21 -// Copyright (c) 2015 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 _Geom2dEvaluator_Curve_HeaderFile -#define _Geom2dEvaluator_Curve_HeaderFile - -#include -#include - -class gp_Pnt2d; -class gp_Vec2d; - -//! Interface for calculation of values and derivatives for different kinds of curves in 2D. -//! Works both with adaptors and curves. -class Geom2dEvaluator_Curve : public Standard_Transient -{ -public: - Geom2dEvaluator_Curve() {} - - //! Value of 2D curve - virtual void D0(const Standard_Real theU, gp_Pnt2d& theValue) const = 0; - //! Value and first derivatives of curve - virtual void D1(const Standard_Real theU, gp_Pnt2d& theValue, gp_Vec2d& theD1) const = 0; - //! Value, first and second derivatives of curve - virtual void D2(const Standard_Real theU, - gp_Pnt2d& theValue, - gp_Vec2d& theD1, - gp_Vec2d& theD2) const = 0; - //! Value, first, second and third derivatives of curve - virtual void D3(const Standard_Real theU, - gp_Pnt2d& theValue, - gp_Vec2d& theD1, - gp_Vec2d& theD2, - gp_Vec2d& theD3) const = 0; - //! Calculates N-th derivatives of curve, where N = theDerU. Raises if N < 1 - virtual gp_Vec2d DN(const Standard_Real theU, const Standard_Integer theDerU) const = 0; - - virtual Handle(Geom2dEvaluator_Curve) ShallowCopy() const = 0; - - DEFINE_STANDARD_RTTI_INLINE(Geom2dEvaluator_Curve, Standard_Transient) -}; - -DEFINE_STANDARD_HANDLE(Geom2dEvaluator_Curve, Standard_Transient) - -#endif // _Geom2dEvaluator_Curve_HeaderFile diff --git a/src/ModelingData/TKG2d/Geom2dEvaluator/Geom2dEvaluator_OffsetCurve.cxx b/src/ModelingData/TKG2d/Geom2dEvaluator/Geom2dEvaluator_OffsetCurve.cxx deleted file mode 100644 index 8db3a7f080..0000000000 --- a/src/ModelingData/TKG2d/Geom2dEvaluator/Geom2dEvaluator_OffsetCurve.cxx +++ /dev/null @@ -1,262 +0,0 @@ -// Created on: 2015-09-21 -// Copyright (c) 2015 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. - -#include -#include -#include -#include - -IMPLEMENT_STANDARD_RTTIEXT(Geom2dEvaluator_OffsetCurve, Geom2dEvaluator_Curve) - -Geom2dEvaluator_OffsetCurve::Geom2dEvaluator_OffsetCurve(const Handle(Geom2d_Curve)& theBase, - const Standard_Real theOffset) - : Geom2dEvaluator_Curve(), - myBaseCurve(theBase), - myOffset(theOffset) -{ -} - -Geom2dEvaluator_OffsetCurve::Geom2dEvaluator_OffsetCurve(const Handle(Geom2dAdaptor_Curve)& theBase, - const Standard_Real theOffset) - : Geom2dEvaluator_Curve(), - myBaseAdaptor(theBase), - myOffset(theOffset) -{ -} - -void Geom2dEvaluator_OffsetCurve::D0(const Standard_Real theU, gp_Pnt2d& theValue) const -{ - gp_Vec2d aD1; - BaseD1(theU, theValue, aD1); - Geom2dEvaluator::CalculateD0(theValue, aD1, myOffset); -} - -void Geom2dEvaluator_OffsetCurve::D1(const Standard_Real theU, - gp_Pnt2d& theValue, - gp_Vec2d& theD1) const -{ - gp_Vec2d aD2; - BaseD2(theU, theValue, theD1, aD2); - Geom2dEvaluator::CalculateD1(theValue, theD1, aD2, myOffset); -} - -void Geom2dEvaluator_OffsetCurve::D2(const Standard_Real theU, - gp_Pnt2d& theValue, - gp_Vec2d& theD1, - gp_Vec2d& theD2) const -{ - gp_Vec2d aD3; - BaseD3(theU, theValue, theD1, theD2, aD3); - - Standard_Boolean isDirectionChange = Standard_False; - if (theD1.SquareMagnitude() <= gp::Resolution()) - { - gp_Vec2d aDummyD4; - isDirectionChange = AdjustDerivative(3, theU, theD1, theD2, aD3, aDummyD4); - } - - Geom2dEvaluator::CalculateD2(theValue, theD1, theD2, aD3, isDirectionChange, myOffset); -} - -void Geom2dEvaluator_OffsetCurve::D3(const Standard_Real theU, - gp_Pnt2d& theValue, - gp_Vec2d& theD1, - gp_Vec2d& theD2, - gp_Vec2d& theD3) const -{ - gp_Vec2d aD4; - BaseD4(theU, theValue, theD1, theD2, theD3, aD4); - - Standard_Boolean isDirectionChange = Standard_False; - if (theD1.SquareMagnitude() <= gp::Resolution()) - isDirectionChange = AdjustDerivative(4, theU, theD1, theD2, theD3, aD4); - - Geom2dEvaluator::CalculateD3(theValue, theD1, theD2, theD3, aD4, isDirectionChange, myOffset); -} - -gp_Vec2d Geom2dEvaluator_OffsetCurve::DN(const Standard_Real theU, - const Standard_Integer theDeriv) const -{ - Standard_RangeError_Raise_if(theDeriv < 1, "Geom2dEvaluator_OffsetCurve::DN(): theDeriv < 1"); - - gp_Pnt2d aPnt; - gp_Vec2d aDummy, aDN; - switch (theDeriv) - { - case 1: - D1(theU, aPnt, aDN); - break; - case 2: - D2(theU, aPnt, aDummy, aDN); - break; - case 3: - D3(theU, aPnt, aDummy, aDummy, aDN); - break; - default: - aDN = BaseDN(theU, theDeriv); - } - return aDN; -} - -Handle(Geom2dEvaluator_Curve) Geom2dEvaluator_OffsetCurve::ShallowCopy() const -{ - Handle(Geom2dEvaluator_OffsetCurve) aCopy; - if (!myBaseAdaptor.IsNull()) - { - aCopy = new Geom2dEvaluator_OffsetCurve( - Handle(Geom2dAdaptor_Curve)::DownCast(myBaseAdaptor->ShallowCopy()), - myOffset); - } - else - { - aCopy = new Geom2dEvaluator_OffsetCurve(myBaseCurve, myOffset); - } - - return aCopy; -} - -void Geom2dEvaluator_OffsetCurve::BaseD0(const Standard_Real theU, gp_Pnt2d& theValue) const -{ - if (!myBaseAdaptor.IsNull()) - myBaseAdaptor->D0(theU, theValue); - else - myBaseCurve->D0(theU, theValue); -} - -void Geom2dEvaluator_OffsetCurve::BaseD1(const Standard_Real theU, - gp_Pnt2d& theValue, - gp_Vec2d& theD1) const -{ - if (!myBaseAdaptor.IsNull()) - myBaseAdaptor->D1(theU, theValue, theD1); - else - myBaseCurve->D1(theU, theValue, theD1); -} - -void Geom2dEvaluator_OffsetCurve::BaseD2(const Standard_Real theU, - gp_Pnt2d& theValue, - gp_Vec2d& theD1, - gp_Vec2d& theD2) const -{ - if (!myBaseAdaptor.IsNull()) - myBaseAdaptor->D2(theU, theValue, theD1, theD2); - else - myBaseCurve->D2(theU, theValue, theD1, theD2); -} - -void Geom2dEvaluator_OffsetCurve::BaseD3(const Standard_Real theU, - gp_Pnt2d& theValue, - gp_Vec2d& theD1, - gp_Vec2d& theD2, - gp_Vec2d& theD3) const -{ - if (!myBaseAdaptor.IsNull()) - myBaseAdaptor->D3(theU, theValue, theD1, theD2, theD3); - else - myBaseCurve->D3(theU, theValue, theD1, theD2, theD3); -} - -void Geom2dEvaluator_OffsetCurve::BaseD4(const Standard_Real theU, - gp_Pnt2d& theValue, - gp_Vec2d& theD1, - gp_Vec2d& theD2, - gp_Vec2d& theD3, - gp_Vec2d& theD4) const -{ - if (!myBaseAdaptor.IsNull()) - { - myBaseAdaptor->D3(theU, theValue, theD1, theD2, theD3); - theD4 = myBaseAdaptor->DN(theU, 4); - } - else - { - myBaseCurve->D3(theU, theValue, theD1, theD2, theD3); - theD4 = myBaseCurve->DN(theU, 4); - } -} - -gp_Vec2d Geom2dEvaluator_OffsetCurve::BaseDN(const Standard_Real theU, - const Standard_Integer theDeriv) const -{ - if (!myBaseAdaptor.IsNull()) - return myBaseAdaptor->DN(theU, theDeriv); - return myBaseCurve->DN(theU, theDeriv); -} - -Standard_Boolean Geom2dEvaluator_OffsetCurve::AdjustDerivative( - const Standard_Integer theMaxDerivative, - const Standard_Real theU, - gp_Vec2d& theD1, - gp_Vec2d& theD2, - gp_Vec2d& theD3, - gp_Vec2d& theD4) const -{ - static const Standard_Real aTol = gp::Resolution(); - static const Standard_Real aMinStep = 1e-7; - static const Standard_Integer aMaxDerivOrder = 3; - - Standard_Boolean isDirectionChange = Standard_False; - Standard_Real anUinfium; - Standard_Real anUsupremum; - if (!myBaseAdaptor.IsNull()) - { - anUinfium = myBaseAdaptor->FirstParameter(); - anUsupremum = myBaseAdaptor->LastParameter(); - } - else - { - anUinfium = myBaseCurve->FirstParameter(); - anUsupremum = myBaseCurve->LastParameter(); - } - - static const Standard_Real DivisionFactor = 1.e-3; - Standard_Real du; - if ((anUsupremum >= RealLast()) || (anUinfium <= RealFirst())) - du = 0.0; - else - du = anUsupremum - anUinfium; - - const Standard_Real aDelta = std::max(du * DivisionFactor, aMinStep); - - // Derivative is approximated by Taylor-series - Standard_Integer anIndex = 1; // Derivative order - gp_Vec2d V; - - do - { - V = BaseDN(theU, ++anIndex); - } while ((V.SquareMagnitude() <= aTol) && anIndex < aMaxDerivOrder); - - Standard_Real u; - - if (theU - anUinfium < aDelta) - u = theU + aDelta; - else - u = theU - aDelta; - - gp_Pnt2d P1, P2; - BaseD0(std::min(theU, u), P1); - BaseD0(std::max(theU, u), P2); - - gp_Vec2d V1(P1, P2); - isDirectionChange = V.Dot(V1) < 0.0; - Standard_Real aSign = isDirectionChange ? -1.0 : 1.0; - - theD1 = V * aSign; - gp_Vec2d* aDeriv[3] = {&theD2, &theD3, &theD4}; - for (Standard_Integer i = 1; i < theMaxDerivative; i++) - *(aDeriv[i - 1]) = BaseDN(theU, anIndex + i) * aSign; - - return isDirectionChange; -} diff --git a/src/ModelingData/TKG2d/Geom2dEvaluator/Geom2dEvaluator_OffsetCurve.hxx b/src/ModelingData/TKG2d/Geom2dEvaluator/Geom2dEvaluator_OffsetCurve.hxx deleted file mode 100644 index b6db1cd07b..0000000000 --- a/src/ModelingData/TKG2d/Geom2dEvaluator/Geom2dEvaluator_OffsetCurve.hxx +++ /dev/null @@ -1,101 +0,0 @@ -// Created on: 2015-09-21 -// Copyright (c) 2015 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 _Geom2dEvaluator_OffsetCurve_HeaderFile -#define _Geom2dEvaluator_OffsetCurve_HeaderFile - -#include -#include - -//! Allows to calculate values and derivatives for offset curves in 2D -class Geom2dEvaluator_OffsetCurve : public Geom2dEvaluator_Curve -{ -public: - //! Initialize evaluator by curve - Standard_EXPORT Geom2dEvaluator_OffsetCurve(const Handle(Geom2d_Curve)& theBase, - const Standard_Real theOffset); - //! Initialize evaluator by curve adaptor - Standard_EXPORT Geom2dEvaluator_OffsetCurve(const Handle(Geom2dAdaptor_Curve)& theBase, - const Standard_Real theOffset); - - //! Change the offset value - void SetOffsetValue(Standard_Real theOffset) { myOffset = theOffset; } - - //! Value of curve - Standard_EXPORT void D0(const Standard_Real theU, gp_Pnt2d& theValue) const Standard_OVERRIDE; - //! Value and first derivatives of curve - Standard_EXPORT void D1(const Standard_Real theU, - gp_Pnt2d& theValue, - gp_Vec2d& theD1) const Standard_OVERRIDE; - //! Value, first and second derivatives of curve - Standard_EXPORT void D2(const Standard_Real theU, - gp_Pnt2d& theValue, - gp_Vec2d& theD1, - gp_Vec2d& theD2) const Standard_OVERRIDE; - //! Value, first, second and third derivatives of curve - Standard_EXPORT void D3(const Standard_Real theU, - gp_Pnt2d& theValue, - gp_Vec2d& theD1, - gp_Vec2d& theD2, - gp_Vec2d& theD3) const Standard_OVERRIDE; - //! Calculates N-th derivatives of curve, where N = theDeriv. Raises if N < 1 - Standard_EXPORT gp_Vec2d DN(const Standard_Real theU, - const Standard_Integer theDeriv) const Standard_OVERRIDE; - - Standard_EXPORT Handle(Geom2dEvaluator_Curve) ShallowCopy() const Standard_OVERRIDE; - - DEFINE_STANDARD_RTTIEXT(Geom2dEvaluator_OffsetCurve, Geom2dEvaluator_Curve) - -private: - //! Calculate value of base curve/adaptor - void BaseD0(const Standard_Real theU, gp_Pnt2d& theValue) const; - //! Calculate value and first derivatives of base curve/adaptor - void BaseD1(const Standard_Real theU, gp_Pnt2d& theValue, gp_Vec2d& theD1) const; - //! Calculate value, first and second derivatives of base curve/adaptor - void BaseD2(const Standard_Real theU, gp_Pnt2d& theValue, gp_Vec2d& theD1, gp_Vec2d& theD2) const; - //! Calculate value, first, second and third derivatives of base curve/adaptor - void BaseD3(const Standard_Real theU, - gp_Pnt2d& theValue, - gp_Vec2d& theD1, - gp_Vec2d& theD2, - gp_Vec2d& theD3) const; - //! Calculate value and derivatives till 4th of base curve/adaptor - void BaseD4(const Standard_Real theU, - gp_Pnt2d& theValue, - gp_Vec2d& theD1, - gp_Vec2d& theD2, - gp_Vec2d& theD3, - gp_Vec2d& theD4) const; - //! Calculate N-th derivative of base curve/adaptor - gp_Vec2d BaseDN(const Standard_Real theU, const Standard_Integer theDeriv) const; - - // Recalculate derivatives in the singular point - // Returns true if the direction of derivatives is changed - Standard_Boolean AdjustDerivative(const Standard_Integer theMaxDerivative, - const Standard_Real theU, - gp_Vec2d& theD1, - gp_Vec2d& theD2, - gp_Vec2d& theD3, - gp_Vec2d& theD4) const; - -private: - Handle(Geom2d_Curve) myBaseCurve; - Handle(Geom2dAdaptor_Curve) myBaseAdaptor; - - Standard_Real myOffset; ///< offset value -}; - -DEFINE_STANDARD_HANDLE(Geom2dEvaluator_OffsetCurve, Geom2dEvaluator_Curve) - -#endif // _Geom2dEvaluator_OffsetCurve_HeaderFile diff --git a/src/ModelingData/TKG2d/PACKAGES.cmake b/src/ModelingData/TKG2d/PACKAGES.cmake index 53148bd634..feea4dbfd0 100644 --- a/src/ModelingData/TKG2d/PACKAGES.cmake +++ b/src/ModelingData/TKG2d/PACKAGES.cmake @@ -6,6 +6,5 @@ set(OCCT_TKG2d_LIST_OF_PACKAGES Adaptor2d Geom2dLProp Geom2dAdaptor - Geom2dEvaluator Geom2dHash ) diff --git a/src/ModelingData/TKG3d/Adaptor3d/Adaptor3d_HSurfaceTool.cxx b/src/ModelingData/TKG3d/Adaptor3d/Adaptor3d_HSurfaceTool.cxx index 4e414b9436..4f4b67381d 100644 --- a/src/ModelingData/TKG3d/Adaptor3d/Adaptor3d_HSurfaceTool.cxx +++ b/src/ModelingData/TKG3d/Adaptor3d/Adaptor3d_HSurfaceTool.cxx @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/src/ModelingData/TKG3d/Geom/FILES.cmake b/src/ModelingData/TKG3d/Geom/FILES.cmake index 044def655d..c088e17c6a 100644 --- a/src/ModelingData/TKG3d/Geom/FILES.cmake +++ b/src/ModelingData/TKG3d/Geom/FILES.cmake @@ -40,6 +40,7 @@ set(OCCT_Geom_FILES Geom_ElementarySurface.hxx Geom_Ellipse.cxx Geom_Ellipse.hxx + Geom_ExtrusionUtils.pxx Geom_Geometry.cxx Geom_Geometry.hxx Geom_HSequenceOfBSplineSurface.hxx @@ -51,8 +52,10 @@ set(OCCT_Geom_FILES Geom_OffsetCurve.hxx Geom_OffsetSurface.cxx Geom_OffsetSurface.hxx + Geom_OffsetCurveUtils.pxx + Geom_OffsetSurfaceUtils.pxx Geom_OsculatingSurface.cxx - Geom_OsculatingSurface.hxx + Geom_OsculatingSurface.pxx Geom_Parabola.cxx Geom_Parabola.hxx Geom_Plane.cxx @@ -61,6 +64,7 @@ set(OCCT_Geom_FILES Geom_Point.hxx Geom_RectangularTrimmedSurface.cxx Geom_RectangularTrimmedSurface.hxx + Geom_RevolutionUtils.pxx Geom_SequenceOfBSplineSurface.hxx Geom_SphericalSurface.cxx Geom_SphericalSurface.hxx diff --git a/src/ModelingData/TKG3d/Geom/Geom_BSplineCurve.hxx b/src/ModelingData/TKG3d/Geom/Geom_BSplineCurve.hxx index 6066120447..e6ebf7d067 100644 --- a/src/ModelingData/TKG3d/Geom/Geom_BSplineCurve.hxx +++ b/src/ModelingData/TKG3d/Geom/Geom_BSplineCurve.hxx @@ -808,6 +808,16 @@ public: //! Returns the weights of the B-spline curve; Standard_EXPORT const TColStd_Array1OfReal* Weights() const; + //! Returns handle to the array of poles for efficient grid evaluation. + const Handle(TColgp_HArray1OfPnt)& HArrayPoles() const { return poles; } + + //! Returns handle to the array of weights for efficient grid evaluation. + //! May be null for non-rational curves. + const Handle(TColStd_HArray1OfReal)& HArrayWeights() const { return weights; } + + //! Returns handle to the flat knots array for efficient grid evaluation. + const Handle(TColStd_HArray1OfReal)& HArrayFlatKnots() const { return flatknots; } + //! Applies the transformation T to this BSpline curve. Standard_EXPORT void Transform(const gp_Trsf& T) Standard_OVERRIDE; diff --git a/src/ModelingData/TKG3d/Geom/Geom_BSplineSurface.hxx b/src/ModelingData/TKG3d/Geom/Geom_BSplineSurface.hxx index 268f48e7c6..2b7b3f9bf3 100644 --- a/src/ModelingData/TKG3d/Geom/Geom_BSplineSurface.hxx +++ b/src/ModelingData/TKG3d/Geom/Geom_BSplineSurface.hxx @@ -1106,6 +1106,19 @@ public: //! value and derivatives computation Standard_EXPORT const TColStd_Array2OfReal* Weights() const; + //! Returns handle to the poles array (direct access to internal storage). + const Handle(TColgp_HArray2OfPnt)& HArrayPoles() const { return poles; } + + //! Returns handle to the weights array. + //! Returns null handle for non-rational surfaces. + const Handle(TColStd_HArray2OfReal)& HArrayWeights() const { return weights; } + + //! Returns handle to the U flat knots sequence. + const Handle(TColStd_HArray1OfReal)& HArrayUFlatKnots() const { return ufknots; } + + //! Returns handle to the V flat knots sequence. + const Handle(TColStd_HArray1OfReal)& HArrayVFlatKnots() const { return vfknots; } + Standard_EXPORT void D0(const Standard_Real U, const Standard_Real V, gp_Pnt& P) const Standard_OVERRIDE; diff --git a/src/ModelingData/TKG3d/Geom/Geom_ExtrusionUtils.pxx b/src/ModelingData/TKG3d/Geom/Geom_ExtrusionUtils.pxx new file mode 100644 index 0000000000..3b9c73ca20 --- /dev/null +++ b/src/ModelingData/TKG3d/Geom/Geom_ExtrusionUtils.pxx @@ -0,0 +1,172 @@ +// Copyright (c) 2025 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 _Geom_ExtrusionUtils_HeaderFile +#define _Geom_ExtrusionUtils_HeaderFile + +#include +#include +#include + +//! @file Geom_ExtrusionUtils.pxx +//! @brief Shared utility functions for extrusion surface evaluation. +//! +//! This file provides template functions for evaluating points and derivatives +//! on linear extrusion surfaces. The functions are templated to work with both +//! Geom_Curve (for Geom_SurfaceOfLinearExtrusion) and Adaptor3d_Curve +//! (for GeomAdaptor_SurfaceOfLinearExtrusion). +//! +//! Extrusion surface: P(U,V) = C(U) + V * Direction + +namespace Geom_ExtrusionUtils +{ + +//! Evaluates point on extrusion surface. +//! @tparam CurveType Type supporting D0(param, point) method +//! @param theU Parameter along the basis curve +//! @param theV Parameter along the extrusion direction +//! @param theBasis Basis curve +//! @param theDir Extrusion direction XYZ (must be normalized) +//! @param theP [out] Evaluated point +template +inline void D0(const double theU, + const double theV, + const CurveType& theBasis, + const gp_XYZ& theDir, + gp_Pnt& theP) +{ + theBasis.D0(theU, theP); + theP.SetXYZ(theP.XYZ() + theV * theDir); +} + +//! Evaluates point and first derivatives on extrusion surface. +//! @tparam CurveType Type supporting D1(param, point, vec) method +//! @param theU Parameter along the basis curve +//! @param theV Parameter along the extrusion direction +//! @param theBasis Basis curve +//! @param theDir Extrusion direction XYZ (must be normalized) +//! @param theP [out] Evaluated point +//! @param theD1U [out] First derivative with respect to U +//! @param theD1V [out] First derivative with respect to V +template +inline void D1(const double theU, + const double theV, + const CurveType& theBasis, + const gp_XYZ& theDir, + gp_Pnt& theP, + gp_Vec& theD1U, + gp_Vec& theD1V) +{ + theBasis.D1(theU, theP, theD1U); + theP.SetXYZ(theP.XYZ() + theV * theDir); + theD1V.SetXYZ(theDir); +} + +//! Evaluates point, first and second derivatives on extrusion surface. +//! @tparam CurveType Type supporting D2(param, point, vec, vec) method +//! @param theU Parameter along the basis curve +//! @param theV Parameter along the extrusion direction +//! @param theBasis Basis curve +//! @param theDir Extrusion direction XYZ (must be normalized) +//! @param theP [out] Evaluated point +//! @param theD1U [out] First derivative with respect to U +//! @param theD1V [out] First derivative with respect to V +//! @param theD2U [out] Second derivative with respect to U +//! @param theD2V [out] Second derivative with respect to V +//! @param theD2UV [out] Mixed second derivative +template +inline void D2(const double theU, + const double theV, + const CurveType& theBasis, + const gp_XYZ& theDir, + gp_Pnt& theP, + gp_Vec& theD1U, + gp_Vec& theD1V, + gp_Vec& theD2U, + gp_Vec& theD2V, + gp_Vec& theD2UV) +{ + theBasis.D2(theU, theP, theD1U, theD2U); + theP.SetXYZ(theP.XYZ() + theV * theDir); + theD1V.SetXYZ(theDir); + theD2V.SetCoord(0.0, 0.0, 0.0); + theD2UV.SetCoord(0.0, 0.0, 0.0); +} + +//! Evaluates point, first, second and third derivatives on extrusion surface. +//! @tparam CurveType Type supporting D3(param, point, vec, vec, vec) method +//! @param theU Parameter along the basis curve +//! @param theV Parameter along the extrusion direction +//! @param theBasis Basis curve +//! @param theDir Extrusion direction XYZ (must be normalized) +//! @param theP [out] Evaluated point +//! @param theD1U [out] First derivative with respect to U +//! @param theD1V [out] First derivative with respect to V +//! @param theD2U [out] Second derivative with respect to U +//! @param theD2V [out] Second derivative with respect to V +//! @param theD2UV [out] Mixed second derivative +//! @param theD3U [out] Third derivative with respect to U +//! @param theD3V [out] Third derivative with respect to V +//! @param theD3UUV [out] Mixed third derivative (UUV) +//! @param theD3UVV [out] Mixed third derivative (UVV) +template +inline void D3(const double theU, + const double theV, + const CurveType& theBasis, + const gp_XYZ& theDir, + gp_Pnt& theP, + gp_Vec& theD1U, + gp_Vec& theD1V, + gp_Vec& theD2U, + gp_Vec& theD2V, + gp_Vec& theD2UV, + gp_Vec& theD3U, + gp_Vec& theD3V, + gp_Vec& theD3UUV, + gp_Vec& theD3UVV) +{ + theBasis.D3(theU, theP, theD1U, theD2U, theD3U); + theP.SetXYZ(theP.XYZ() + theV * theDir); + theD1V.SetXYZ(theDir); + theD2V.SetCoord(0.0, 0.0, 0.0); + theD2UV.SetCoord(0.0, 0.0, 0.0); + theD3V.SetCoord(0.0, 0.0, 0.0); + theD3UUV.SetCoord(0.0, 0.0, 0.0); + theD3UVV.SetCoord(0.0, 0.0, 0.0); +} + +//! Evaluates N-th derivative on extrusion surface. +//! @tparam CurveType Type supporting DN(param, order) method +//! @param theU Parameter along the basis curve +//! @param theBasis Basis curve +//! @param theDir Extrusion direction XYZ (must be normalized) +//! @param theDerU Derivative order with respect to U +//! @param theDerV Derivative order with respect to V +//! @return The derivative vector +template +inline gp_Vec DN(const double theU, + const CurveType& theBasis, + const gp_XYZ& theDir, + const int theDerU, + const int theDerV) +{ + if (theDerV == 0) + return theBasis.DN(theU, theDerU); + else if (theDerU == 0 && theDerV == 1) + return gp_Vec(theDir); + return gp_Vec(0.0, 0.0, 0.0); +} + +} // namespace Geom_ExtrusionUtils + +#endif // _Geom_ExtrusionUtils_HeaderFile diff --git a/src/ModelingData/TKG3d/Geom/Geom_OffsetCurve.cxx b/src/ModelingData/TKG3d/Geom/Geom_OffsetCurve.cxx index 471372e1f9..ff1ff728d8 100644 --- a/src/ModelingData/TKG3d/Geom/Geom_OffsetCurve.cxx +++ b/src/ModelingData/TKG3d/Geom/Geom_OffsetCurve.cxx @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -38,32 +40,31 @@ IMPLEMENT_STANDARD_RTTIEXT(Geom_OffsetCurve, Geom_Curve) -static const Standard_Real MyAngularToleranceForG1 = Precision::Angular(); +static const double MyAngularToleranceForG1 = Precision::Angular(); -//================================================================================================= +//================================================================================================== Handle(Geom_Geometry) Geom_OffsetCurve::Copy() const { return new Geom_OffsetCurve(*this); } -//======================================================================= +//================================================================================================== // function : Geom_OffsetCurve // purpose : Basis curve cannot be an Offset curve or trimmed from // offset curve. -//======================================================================= +//================================================================================================== Geom_OffsetCurve::Geom_OffsetCurve(const Geom_OffsetCurve& theOther) : basisCurve(Handle(Geom_Curve)::DownCast(theOther.basisCurve->Copy())), direction(theOther.direction), offsetValue(theOther.offsetValue), - myBasisCurveContinuity(theOther.myBasisCurveContinuity), - myEvaluator(new GeomEvaluator_OffsetCurve(basisCurve, offsetValue, direction)) + myBasisCurveContinuity(theOther.myBasisCurveContinuity) { // Deep copy without validation - source curve is already validated } -//======================================================================= +//================================================================================================== Geom_OffsetCurve::Geom_OffsetCurve(const Handle(Geom_Curve)& theCurve, const Standard_Real theOffset, @@ -75,67 +76,64 @@ Geom_OffsetCurve::Geom_OffsetCurve(const Handle(Geom_Curve)& theCurve, SetBasisCurve(theCurve, isTheNotCheckC0); } -//================================================================================================= +//================================================================================================== void Geom_OffsetCurve::Reverse() { basisCurve->Reverse(); offsetValue = -offsetValue; - myEvaluator->SetOffsetValue(offsetValue); } -//================================================================================================= +//================================================================================================== Standard_Real Geom_OffsetCurve::ReversedParameter(const Standard_Real U) const { return basisCurve->ReversedParameter(U); } -//================================================================================================= +//================================================================================================== const gp_Dir& Geom_OffsetCurve::Direction() const { return direction; } -//================================================================================================= +//================================================================================================== void Geom_OffsetCurve::SetDirection(const gp_Dir& V) { direction = V; - myEvaluator->SetOffsetDirection(direction); } -//================================================================================================= +//================================================================================================== void Geom_OffsetCurve::SetOffsetValue(const Standard_Real D) { offsetValue = D; - myEvaluator->SetOffsetValue(offsetValue); } -//================================================================================================= +//================================================================================================== Standard_Boolean Geom_OffsetCurve::IsPeriodic() const { return basisCurve->IsPeriodic(); } -//================================================================================================= +//================================================================================================== Standard_Real Geom_OffsetCurve::Period() const { return basisCurve->Period(); } -//================================================================================================= +//================================================================================================== void Geom_OffsetCurve::SetBasisCurve(const Handle(Geom_Curve)& C, const Standard_Boolean isNotCheckC0) { - const Standard_Real aUf = C->FirstParameter(), aUl = C->LastParameter(); - Handle(Geom_Curve) aCheckingCurve = Handle(Geom_Curve)::DownCast(C->Copy()); - Standard_Boolean isTrimmed = Standard_False; + const double aUf = C->FirstParameter(), aUl = C->LastParameter(); + Handle(Geom_Curve) aCheckingCurve = Handle(Geom_Curve)::DownCast(C->Copy()); + bool isTrimmed = false; while (aCheckingCurve->IsKind(STANDARD_TYPE(Geom_TrimmedCurve)) || aCheckingCurve->IsKind(STANDARD_TYPE(Geom_OffsetCurve))) @@ -144,17 +142,17 @@ void Geom_OffsetCurve::SetBasisCurve(const Handle(Geom_Curve)& C, { Handle(Geom_TrimmedCurve) aTrimC = Handle(Geom_TrimmedCurve)::DownCast(aCheckingCurve); aCheckingCurve = aTrimC->BasisCurve(); - isTrimmed = Standard_True; + isTrimmed = true; } if (aCheckingCurve->IsKind(STANDARD_TYPE(Geom_OffsetCurve))) { Handle(Geom_OffsetCurve) aOC = Handle(Geom_OffsetCurve)::DownCast(aCheckingCurve); aCheckingCurve = aOC->BasisCurve(); - Standard_Real PrevOff = aOC->Offset(); - gp_Vec V1(aOC->Direction()); - gp_Vec V2(direction); - gp_Vec Vdir(PrevOff * V1 + offsetValue * V2); + double PrevOff = aOC->Offset(); + gp_Vec V1(aOC->Direction()); + gp_Vec V2(direction); + gp_Vec Vdir(PrevOff * V1 + offsetValue * V2); if (offsetValue >= 0.) { @@ -171,7 +169,7 @@ void Geom_OffsetCurve::SetBasisCurve(const Handle(Geom_Curve)& C, myBasisCurveContinuity = aCheckingCurve->Continuity(); - Standard_Boolean isC0 = !isNotCheckC0 && (myBasisCurveContinuity == GeomAbs_C0); + bool isC0 = !isNotCheckC0 && (myBasisCurveContinuity == GeomAbs_C0); // Basis curve must be at least C1 if (isC0 && aCheckingCurve->IsKind(STANDARD_TYPE(Geom_BSplineCurve))) @@ -182,12 +180,14 @@ void Geom_OffsetCurve::SetBasisCurve(const Handle(Geom_Curve)& C, // Checking if basis curve has more smooth (C1, G2 and above) is not done. // It can be done in case of need. myBasisCurveContinuity = GeomAbs_G1; - isC0 = Standard_False; + isC0 = false; } // Raise exception if still C0 if (isC0) + { throw Standard_ConstructionError("Offset on C0 curve"); + } } // if (isTrimmed) @@ -198,22 +198,19 @@ void Geom_OffsetCurve::SetBasisCurve(const Handle(Geom_Curve)& C, { basisCurve = aCheckingCurve; } - - myEvaluator = new GeomEvaluator_OffsetCurve(basisCurve, offsetValue, direction); } -//================================================================================================= +//================================================================================================== Handle(Geom_Curve) Geom_OffsetCurve::BasisCurve() const { return basisCurve; } -//================================================================================================= +//================================================================================================== GeomAbs_Shape Geom_OffsetCurve::Continuity() const { - GeomAbs_Shape OffsetShape = GeomAbs_C0; switch (myBasisCurveContinuity) { @@ -242,28 +239,46 @@ GeomAbs_Shape Geom_OffsetCurve::Continuity() const return OffsetShape; } -//================================================================================================= +//================================================================================================== -void Geom_OffsetCurve::D0(const Standard_Real U, gp_Pnt& P) const +void Geom_OffsetCurve::D0(const Standard_Real theU, gp_Pnt& theP) const { - myEvaluator->D0(U, P); + if (!Geom_OffsetCurveUtils::EvaluateD0(theU, basisCurve.get(), direction, offsetValue, theP)) + { + throw Standard_NullValue("Geom_OffsetCurve::D0: Unable to calculate offset point"); + } } -//================================================================================================= +//================================================================================================== -void Geom_OffsetCurve::D1(const Standard_Real U, gp_Pnt& P, gp_Vec& V1) const +void Geom_OffsetCurve::D1(const Standard_Real theU, gp_Pnt& theP, gp_Vec& theV1) const { - myEvaluator->D1(U, P, V1); + if (!Geom_OffsetCurveUtils::EvaluateD1(theU, basisCurve.get(), direction, offsetValue, theP, theV1)) + { + throw Standard_NullValue("Geom_OffsetCurve::D1: Unable to calculate offset D1"); + } } -//================================================================================================= +//================================================================================================== -void Geom_OffsetCurve::D2(const Standard_Real U, gp_Pnt& P, gp_Vec& V1, gp_Vec& V2) const +void Geom_OffsetCurve::D2(const Standard_Real theU, + gp_Pnt& theP, + gp_Vec& theV1, + gp_Vec& theV2) const { - myEvaluator->D2(U, P, V1, V2); + if (!Geom_OffsetCurveUtils::EvaluateD2(theU, + basisCurve.get(), + direction, + offsetValue, + theP, + theV1, + theV2)) + { + throw Standard_NullValue("Geom_OffsetCurve::D2: Unable to calculate offset D2"); + } } -//================================================================================================= +//================================================================================================== void Geom_OffsetCurve::D3(const Standard_Real theU, gp_Pnt& theP, @@ -271,61 +286,60 @@ void Geom_OffsetCurve::D3(const Standard_Real theU, gp_Vec& theV2, gp_Vec& theV3) const { - myEvaluator->D3(theU, theP, theV1, theV2, theV3); + if (!Geom_OffsetCurveUtils::EvaluateD3(theU, + basisCurve.get(), + direction, + offsetValue, + theP, + theV1, + theV2, + theV3)) + { + throw Standard_NullValue("Geom_OffsetCurve::D3: Unable to calculate offset D3"); + } } -//================================================================================================= +//================================================================================================== gp_Vec Geom_OffsetCurve::DN(const Standard_Real U, const Standard_Integer N) const { - Standard_RangeError_Raise_if(N < 1, - "Exception: " - "Geom_OffsetCurve::DN(...). N<1."); + Standard_RangeError_Raise_if(N < 1, "Exception: Geom_OffsetCurve::DN(...). N<1."); - gp_Vec VN, Vtemp; - gp_Pnt Ptemp; - switch (N) + gp_Vec aVN; + if (!Geom_OffsetCurveUtils::EvaluateDN(U, basisCurve.get(), direction, offsetValue, N, aVN)) { - case 1: - D1(U, Ptemp, VN); - break; - case 2: - D2(U, Ptemp, Vtemp, VN); - break; - case 3: - D3(U, Ptemp, Vtemp, Vtemp, VN); - break; - default: - throw Standard_NotImplemented( - "Exception: " - "Derivative order is greater than 3. Cannot compute of derivative."); + if (N > 3) + { + throw Standard_NotImplemented("Exception: Derivative order is greater than 3. " + "Cannot compute of derivative."); + } + throw Standard_NullValue("Geom_OffsetCurve::DN: Unable to calculate offset DN"); } - - return VN; + return aVN; } -//================================================================================================= +//================================================================================================== Standard_Real Geom_OffsetCurve::FirstParameter() const { return basisCurve->FirstParameter(); } -//================================================================================================= +//================================================================================================== Standard_Real Geom_OffsetCurve::LastParameter() const { return basisCurve->LastParameter(); } -//================================================================================================= +//================================================================================================== Standard_Real Geom_OffsetCurve::Offset() const { return offsetValue; } -//================================================================================================= +//================================================================================================== Standard_Boolean Geom_OffsetCurve::IsClosed() const { @@ -335,49 +349,45 @@ Standard_Boolean Geom_OffsetCurve::IsClosed() const return (PF.Distance(PL) <= gp::Resolution()); } -//================================================================================================= +//================================================================================================== Standard_Boolean Geom_OffsetCurve::IsCN(const Standard_Integer N) const { - Standard_RangeError_Raise_if(N < 0, " "); return basisCurve->IsCN(N + 1); } -//================================================================================================= +//================================================================================================== void Geom_OffsetCurve::Transform(const gp_Trsf& T) { basisCurve->Transform(T); direction.Transform(T); offsetValue *= T.ScaleFactor(); - - myEvaluator->SetOffsetValue(offsetValue); - myEvaluator->SetOffsetDirection(direction); } -//================================================================================================= +//================================================================================================== Standard_Real Geom_OffsetCurve::TransformedParameter(const Standard_Real U, const gp_Trsf& T) const { return basisCurve->TransformedParameter(U, T); } -//================================================================================================= +//================================================================================================== Standard_Real Geom_OffsetCurve::ParametricTransformation(const gp_Trsf& T) const { return basisCurve->ParametricTransformation(T); } -//================================================================================================= +//================================================================================================== GeomAbs_Shape Geom_OffsetCurve::GetBasisCurveContinuity() const { return myBasisCurveContinuity; } -//================================================================================================= +//================================================================================================== void Geom_OffsetCurve::DumpJson(Standard_OStream& theOStream, Standard_Integer theDepth) const { diff --git a/src/ModelingData/TKG3d/Geom/Geom_OffsetCurve.hxx b/src/ModelingData/TKG3d/Geom/Geom_OffsetCurve.hxx index d1358822d1..3e39a96dd6 100644 --- a/src/ModelingData/TKG3d/Geom/Geom_OffsetCurve.hxx +++ b/src/ModelingData/TKG3d/Geom/Geom_OffsetCurve.hxx @@ -24,7 +24,6 @@ #include #include #include -#include class gp_Pnt; class gp_Vec; @@ -291,13 +290,11 @@ public: DEFINE_STANDARD_RTTIEXT(Geom_OffsetCurve, Geom_Curve) -protected: private: - Handle(Geom_Curve) basisCurve; - gp_Dir direction; - Standard_Real offsetValue; - GeomAbs_Shape myBasisCurveContinuity; - Handle(GeomEvaluator_OffsetCurve) myEvaluator; + Handle(Geom_Curve) basisCurve; + gp_Dir direction; + double offsetValue; + GeomAbs_Shape myBasisCurveContinuity; }; #endif // _Geom_OffsetCurve_HeaderFile diff --git a/src/ModelingData/TKG3d/Geom/Geom_OffsetCurveUtils.pxx b/src/ModelingData/TKG3d/Geom/Geom_OffsetCurveUtils.pxx new file mode 100644 index 0000000000..e0e6c7e142 --- /dev/null +++ b/src/ModelingData/TKG3d/Geom/Geom_OffsetCurveUtils.pxx @@ -0,0 +1,532 @@ +// Copyright (c) 2015-2025 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 _Geom_OffsetCurveUtils_HeaderFile +#define _Geom_OffsetCurveUtils_HeaderFile + +#include +#include +#include +#include +#include +#include + +#include + +//! Internal helper namespace for 3D offset curve calculations. +//! Provides static inline functions to compute offset curve point and derivatives +//! from basis curve derivatives. +//! +//! These functions are used by Geom_OffsetCurve and GeomAdaptor_Curve. +//! +//! Mathematical basis: +//! P(u) = p(u) + Offset * N / ||N|| +//! where N = p'(u) ^ Direction is the local normal direction +namespace Geom_OffsetCurveUtils +{ + +//! Calculates D0 (point) for 3D offset curve. +//! @param[in,out] theValue on input: basis curve point; on output: offset point +//! @param[in] theD1 first derivative of basis curve at the point +//! @param[in] theDir offset reference direction +//! @param[in] theOffset offset distance value +//! @return true if successful, false if normal vector has zero magnitude +inline bool CalculateD0(gp_Pnt& theValue, + const gp_Vec& theD1, + const gp_Dir& theDir, + double theOffset) +{ + gp_XYZ Ndir = (theD1.XYZ()).Crossed(theDir.XYZ()); + double R = Ndir.Modulus(); + if (R <= gp::Resolution()) + { + return false; + } + + Ndir.Multiply(theOffset / R); + theValue.ChangeCoord().Add(Ndir); + return true; +} + +//! Calculates D0 and D1 for 3D offset curve. +//! @param[in,out] theValue on input: basis curve point; on output: offset point +//! @param[in,out] theD1 on input: first derivative of basis; on output: offset curve D1 +//! @param[in] theD2 second derivative of basis curve +//! @param[in] theDir offset reference direction +//! @param[in] theOffset offset distance value +//! @return true if successful, false if computation failed +inline bool CalculateD1(gp_Pnt& theValue, + gp_Vec& theD1, + const gp_Vec& theD2, + const gp_Dir& theDir, + double theOffset) +{ + // P(u) = p(u) + Offset * Ndir / R + // with R = || p' ^ V|| and Ndir = P' ^ direction (local normal direction) + + // P'(u) = p'(u) + (Offset / R**2) * (DNdir/DU * R - Ndir * (DR/R)) + + gp_XYZ Ndir = (theD1.XYZ()).Crossed(theDir.XYZ()); + gp_XYZ DNdir = (theD2.XYZ()).Crossed(theDir.XYZ()); + double R2 = Ndir.SquareModulus(); + double R = std::sqrt(R2); + double R3 = R * R2; + double Dr = Ndir.Dot(DNdir); + if (R3 <= gp::Resolution()) + { + if (R2 <= gp::Resolution()) + { + return false; + } + // We try another computation but the stability is not very good. + DNdir.Multiply(R); + DNdir.Subtract(Ndir.Multiplied(Dr / R)); + DNdir.Multiply(theOffset / R2); + } + else + { + // Same computation as IICURV in EUCLID-IS because the stability is better + DNdir.Multiply(theOffset / R); + DNdir.Subtract(Ndir.Multiplied(theOffset * Dr / R3)); + } + + Ndir.Multiply(theOffset / R); + // P(u) + theValue.ChangeCoord().Add(Ndir); + // P'(u) + theD1.Add(gp_Vec(DNdir)); + return true; +} + +//! Calculates D0, D1, D2 for 3D offset curve. +//! @param[in,out] theValue on input: basis curve point; on output: offset point +//! @param[in,out] theD1 on input: first derivative of basis; on output: offset curve D1 +//! @param[in,out] theD2 on input: second derivative of basis; on output: offset curve D2 +//! @param[in] theD3 third derivative of basis curve +//! @param[in] theDir offset reference direction +//! @param[in] theOffset offset distance value +//! @param[in] theIsDirChange flag indicating direction change at singular point +//! @return true if successful, false if computation failed +inline bool CalculateD2(gp_Pnt& theValue, + gp_Vec& theD1, + gp_Vec& theD2, + const gp_Vec& theD3, + const gp_Dir& theDir, + double theOffset, + bool theIsDirChange) +{ + // P(u) = p(u) + Offset * Ndir / R + // with R = || p' ^ V|| and Ndir = P' ^ direction (local normal direction) + + // P'(u) = p'(u) + (Offset / R**2) * (DNdir/DU * R - Ndir * (DR/R)) + + // P"(u) = p"(u) + (Offset / R) * (D2Ndir/DU - DNdir * (2.0 * Dr/ R**2) + + // Ndir * ( (3.0 * Dr**2 / R**4) - (D2r / R**2))) + + gp_XYZ Ndir = (theD1.XYZ()).Crossed(theDir.XYZ()); + gp_XYZ DNdir = (theD2.XYZ()).Crossed(theDir.XYZ()); + gp_XYZ D2Ndir = (theD3.XYZ()).Crossed(theDir.XYZ()); + double R2 = Ndir.SquareModulus(); + double R = std::sqrt(R2); + double R3 = R2 * R; + double R4 = R2 * R2; + double R5 = R3 * R2; + double Dr = Ndir.Dot(DNdir); + double D2r = Ndir.Dot(D2Ndir) + DNdir.Dot(DNdir); + + if (R5 <= gp::Resolution()) + { + if (R4 <= gp::Resolution()) + { + return false; + } + // We try another computation but the stability is not very good + // dixit ISG. + // V2 = P" (U) : + R4 = R2 * R2; + D2Ndir.Subtract(DNdir.Multiplied(2.0 * Dr / R2)); + D2Ndir.Add(Ndir.Multiplied(((3.0 * Dr * Dr) / R4) - (D2r / R2))); + D2Ndir.Multiply(theOffset / R); + + // V1 = P' (U) : + DNdir.Multiply(R); + DNdir.Subtract(Ndir.Multiplied(Dr / R)); + DNdir.Multiply(theOffset / R2); + } + else + { + // Same computation as IICURV in EUCLID-IS because the stability is better. + // V2 = P" (U) : + D2Ndir.Multiply(theOffset / R); + D2Ndir.Subtract(DNdir.Multiplied(2.0 * theOffset * Dr / R3)); + D2Ndir.Add(Ndir.Multiplied(theOffset * (((3.0 * Dr * Dr) / R5) - (D2r / R3)))); + + // V1 = P' (U) : + DNdir.Multiply(theOffset / R); + DNdir.Subtract(Ndir.Multiplied(theOffset * Dr / R3)); + } + + Ndir.Multiply(theOffset / R); + // P(u) + theValue.ChangeCoord().Add(Ndir); + // P'(u) : + theD1.Add(gp_Vec(DNdir)); + // P"(u) : + if (theIsDirChange) + { + theD2.Reverse(); + } + theD2.Add(gp_Vec(D2Ndir)); + return true; +} + +//! Calculates D0, D1, D2, D3 for 3D offset curve. +//! @param[in,out] theValue on input: basis curve point; on output: offset point +//! @param[in,out] theD1 on input: first derivative of basis; on output: offset curve D1 +//! @param[in,out] theD2 on input: second derivative of basis; on output: offset curve D2 +//! @param[in,out] theD3 on input: third derivative of basis; on output: offset curve D3 +//! @param[in] theD4 fourth derivative of basis curve +//! @param[in] theDir offset reference direction +//! @param[in] theOffset offset distance value +//! @param[in] theIsDirChange flag indicating direction change at singular point +//! @return true if successful, false if computation failed +inline bool CalculateD3(gp_Pnt& theValue, + gp_Vec& theD1, + gp_Vec& theD2, + gp_Vec& theD3, + const gp_Vec& theD4, + const gp_Dir& theDir, + double theOffset, + bool theIsDirChange) +{ + // P(u) = p(u) + Offset * Ndir / R + // with R = || p' ^ V|| and Ndir = P' ^ direction (local normal direction) + + // P'(u) = p'(u) + (Offset / R**2) * (DNdir/DU * R - Ndir * (DR/R)) + + // P"(u) = p"(u) + (Offset / R) * (D2Ndir/DU - DNdir * (2.0 * Dr/ R**2) + + // Ndir * ( (3.0 * Dr**2 / R**4) - (D2r / R**2))) + + // P"'(u) = p"'(u) + (Offset / R) * (D3Ndir - (3.0 * Dr/R**2) * D2Ndir - + // (3.0 * D2r / R2) * DNdir + (3.0 * Dr * Dr / R4) * DNdir - + // (D3r/R2) * Ndir + (6.0 * Dr * Dr / R4) * Ndir + + // (6.0 * Dr * D2r / R4) * Ndir - (15.0 * Dr* Dr* Dr /R6) * Ndir + + gp_XYZ Ndir = (theD1.XYZ()).Crossed(theDir.XYZ()); + gp_XYZ DNdir = (theD2.XYZ()).Crossed(theDir.XYZ()); + gp_XYZ D2Ndir = (theD3.XYZ()).Crossed(theDir.XYZ()); + gp_XYZ D3Ndir = (theD4.XYZ()).Crossed(theDir.XYZ()); + const double R2 = Ndir.SquareModulus(); + const double R = std::sqrt(R2); + const double R3 = R2 * R; + double R4 = R2 * R2; + const double R5 = R3 * R2; + const double R6 = R3 * R3; + const double R7 = R5 * R2; + const double Dr = Ndir.Dot(DNdir); + const double D2r = Ndir.Dot(D2Ndir) + DNdir.Dot(DNdir); + const double D3r = Ndir.Dot(D3Ndir) + 3.0 * DNdir.Dot(D2Ndir); + if (R7 <= gp::Resolution()) + { + if (R6 <= gp::Resolution()) + { + return false; + } + // V3 = P"' (U) : + D3Ndir.Subtract(D2Ndir.Multiplied(3.0 * Dr / R2)); + D3Ndir.Subtract(DNdir.Multiplied(3.0 * ((D2r / R2) + (Dr * Dr / R4)))); + D3Ndir.Add( + Ndir.Multiplied(6.0 * Dr * Dr / R4 + 6.0 * Dr * D2r / R4 - 15.0 * Dr * Dr * Dr / R6 - D3r)); + D3Ndir.Multiply(theOffset / R); + // V2 = P" (U) : + R4 = R2 * R2; + D2Ndir.Subtract(DNdir.Multiplied(2.0 * Dr / R2)); + D2Ndir.Subtract(Ndir.Multiplied((3.0 * Dr * Dr / R4) - (D2r / R2))); + D2Ndir.Multiply(theOffset / R); + // V1 = P' (U) : + DNdir.Multiply(R); + DNdir.Subtract(Ndir.Multiplied(Dr / R)); + DNdir.Multiply(theOffset / R2); + } + else + { + // V3 = P"' (U) : + D3Ndir.Divide(R); + D3Ndir.Subtract(D2Ndir.Multiplied(3.0 * Dr / R3)); + D3Ndir.Subtract(DNdir.Multiplied((3.0 * ((D2r / R3) + (Dr * Dr) / R5)))); + D3Ndir.Add( + Ndir.Multiplied(6.0 * Dr * Dr / R5 + 6.0 * Dr * D2r / R5 - 15.0 * Dr * Dr * Dr / R7 - D3r)); + D3Ndir.Multiply(theOffset); + // V2 = P" (U) : + D2Ndir.Divide(R); + D2Ndir.Subtract(DNdir.Multiplied(2.0 * Dr / R3)); + D2Ndir.Subtract(Ndir.Multiplied((3.0 * Dr * Dr / R5) - (D2r / R3))); + D2Ndir.Multiply(theOffset); + // V1 = P' (U) : + DNdir.Multiply(theOffset / R); + DNdir.Subtract(Ndir.Multiplied(theOffset * Dr / R3)); + } + + Ndir.Multiply(theOffset / R); + // P(u) + theValue.ChangeCoord().Add(Ndir); + // P'(u) : + theD1.Add(gp_Vec(DNdir)); + // P"(u) + theD2.Add(gp_Vec(D2Ndir)); + // P"'(u) + if (theIsDirChange) + { + theD3.Reverse(); + } + theD3.Add(gp_Vec(D3Ndir)); + return true; +} + +//! Adjusts derivatives at singular points where the first derivative is nearly zero. +//! Uses Taylor series approximation to find a valid tangent direction. +//! @tparam CurveType type supporting D0, DN methods (Geom_Curve or adaptor) +//! @param[in] theCurve basis curve for derivative evaluation +//! @param[in] theMaxDerivative maximum derivative order to compute (3 or 4) +//! @param[in] theU parameter value +//! @param[in,out] theD1 first derivative (will be adjusted) +//! @param[in,out] theD2 second derivative (will be adjusted) +//! @param[in,out] theD3 third derivative (will be adjusted) +//! @param[in,out] theD4 fourth derivative (will be adjusted if theMaxDerivative >= 4) +//! @return true if direction change detected +template +bool AdjustDerivative(const CurveType& theCurve, + int theMaxDerivative, + double theU, + gp_Vec& theD1, + gp_Vec& theD2, + gp_Vec& theD3, + gp_Vec& theD4) +{ + static const double aTol = gp::Resolution(); + static const double aMinStep = 1e-7; + static const int aMaxDerivOrder = 3; + + bool isDirectionChange = false; + const double anUinfium = theCurve.FirstParameter(); + const double anUsupremum = theCurve.LastParameter(); + + static const double DivisionFactor = 1.e-3; + double du; + if ((anUsupremum >= RealLast()) || (anUinfium <= RealFirst())) + { + du = 0.0; + } + else + { + du = anUsupremum - anUinfium; + } + + const double aDelta = std::max(du * DivisionFactor, aMinStep); + + // Derivative is approximated by Taylor-series + int anIndex = 1; // Derivative order + gp_Vec V; + + do + { + V = theCurve.DN(theU, ++anIndex); + } while ((V.SquareMagnitude() <= aTol) && anIndex < aMaxDerivOrder); + + double u; + + if (theU - anUinfium < aDelta) + { + u = theU + aDelta; + } + else + { + u = theU - aDelta; + } + + gp_Pnt P1, P2; + theCurve.D0(std::min(theU, u), P1); + theCurve.D0(std::max(theU, u), P2); + + gp_Vec V1(P1, P2); + isDirectionChange = V.Dot(V1) < 0.0; + const double aSign = isDirectionChange ? -1.0 : 1.0; + + theD1 = V * aSign; + gp_Vec* aDeriv[3] = {&theD2, &theD3, &theD4}; + for (int i = 1; i < theMaxDerivative; i++) + { + *(aDeriv[i - 1]) = theCurve.DN(theU, anIndex + i) * aSign; + } + + return isDirectionChange; +} + +//! Template function for D0 evaluation of offset curve. +//! Gets D1 from basis curve and computes offset point. +//! +//! @tparam BasisCurveType type of basis curve (must have D1 method) +//! @param[in] theU parameter value +//! @param[in] theBasisCurve basis curve +//! @param[in] theDir offset reference direction +//! @param[in] theOffset offset distance +//! @param[out] theValue computed offset point +//! @return true if evaluation succeeded, false if normal is undefined +template +bool EvaluateD0(double theU, + const BasisCurveType& theBasisCurve, + const gp_Dir& theDir, + double theOffset, + gp_Pnt& theValue) +{ + gp_Vec aD1; + theBasisCurve->D1(theU, theValue, aD1); + return CalculateD0(theValue, aD1, theDir, theOffset); +} + +//! Template function for D1 evaluation of offset curve. +//! Gets D2 from basis curve and computes offset point and first derivative. +//! +//! @tparam BasisCurveType type of basis curve (must have D2 method) +//! @param[in] theU parameter value +//! @param[in] theBasisCurve basis curve +//! @param[in] theDir offset reference direction +//! @param[in] theOffset offset distance +//! @param[out] theValue computed offset point +//! @param[out] theD1 computed first derivative +//! @return true if evaluation succeeded, false if computation failed +template +bool EvaluateD1(double theU, + const BasisCurveType& theBasisCurve, + const gp_Dir& theDir, + double theOffset, + gp_Pnt& theValue, + gp_Vec& theD1) +{ + gp_Vec aD2; + theBasisCurve->D2(theU, theValue, theD1, aD2); + return CalculateD1(theValue, theD1, aD2, theDir, theOffset); +} + +//! Template function for D2 evaluation of offset curve. +//! Gets D3 from basis curve and computes offset point and derivatives. +//! Handles singular points where first derivative is nearly zero. +//! +//! @tparam BasisCurveType type of basis curve (must have D3 and DN methods) +//! @param[in] theU parameter value +//! @param[in] theBasisCurve basis curve +//! @param[in] theDir offset reference direction +//! @param[in] theOffset offset distance +//! @param[out] theValue computed offset point +//! @param[out] theD1 computed first derivative +//! @param[out] theD2 computed second derivative +//! @return true if evaluation succeeded, false if computation failed +template +bool EvaluateD2(double theU, + const BasisCurveType& theBasisCurve, + const gp_Dir& theDir, + double theOffset, + gp_Pnt& theValue, + gp_Vec& theD1, + gp_Vec& theD2) +{ + gp_Vec aD3; + theBasisCurve->D3(theU, theValue, theD1, theD2, aD3); + + bool isDirectionChange = false; + if (theD1.SquareMagnitude() <= gp::Resolution()) + { + gp_Vec aDummyD4; + isDirectionChange = AdjustDerivative(*theBasisCurve, 3, theU, theD1, theD2, aD3, aDummyD4); + } + + return CalculateD2(theValue, theD1, theD2, aD3, theDir, theOffset, isDirectionChange); +} + +//! Template function for D3 evaluation of offset curve. +//! Gets D3 and D4 from basis curve and computes offset point and derivatives. +//! Handles singular points where first derivative is nearly zero. +//! +//! @tparam BasisCurveType type of basis curve (must have D3 and DN methods) +//! @param[in] theU parameter value +//! @param[in] theBasisCurve basis curve +//! @param[in] theDir offset reference direction +//! @param[in] theOffset offset distance +//! @param[out] theValue computed offset point +//! @param[out] theD1 computed first derivative +//! @param[out] theD2 computed second derivative +//! @param[out] theD3 computed third derivative +//! @return true if evaluation succeeded, false if computation failed +template +bool EvaluateD3(double theU, + const BasisCurveType& theBasisCurve, + const gp_Dir& theDir, + double theOffset, + gp_Pnt& theValue, + gp_Vec& theD1, + gp_Vec& theD2, + gp_Vec& theD3) +{ + theBasisCurve->D3(theU, theValue, theD1, theD2, theD3); + gp_Vec aD4 = theBasisCurve->DN(theU, 4); + + bool isDirectionChange = false; + if (theD1.SquareMagnitude() <= gp::Resolution()) + { + isDirectionChange = AdjustDerivative(*theBasisCurve, 4, theU, theD1, theD2, theD3, aD4); + } + + return CalculateD3(theValue, theD1, theD2, theD3, aD4, theDir, theOffset, isDirectionChange); +} + +//! Template function for DN evaluation of offset curve. +//! Handles derivatives up to order 3 using D1/D2/D3 methods. +//! For derivatives > 3, returns the basis curve derivative directly +//! (offset contribution is negligible for high-order derivatives). +//! +//! @tparam BasisCurveType type of basis curve (must have D1, D2, D3, DN methods) +//! @param[in] theU parameter value +//! @param[in] theBasisCurve basis curve +//! @param[in] theDir offset reference direction +//! @param[in] theOffset offset distance +//! @param[in] theN derivative order (must be >= 1) +//! @param[out] theDN computed N-th derivative +//! @return true if evaluation succeeded, false if computation failed +template +bool EvaluateDN(double theU, + const BasisCurveType& theBasisCurve, + const gp_Dir& theDir, + double theOffset, + int theN, + gp_Vec& theDN) +{ + gp_Pnt aPnt; + gp_Vec aDummy; + switch (theN) + { + case 1: + return EvaluateD1(theU, theBasisCurve, theDir, theOffset, aPnt, theDN); + case 2: + return EvaluateD2(theU, theBasisCurve, theDir, theOffset, aPnt, aDummy, theDN); + case 3: + return EvaluateD3(theU, theBasisCurve, theDir, theOffset, aPnt, aDummy, aDummy, theDN); + default: + // For derivatives > 3, return basis curve derivative (no offset contribution) + theDN = theBasisCurve->DN(theU, theN); + return true; + } +} + +} // namespace Geom_OffsetCurveUtils + +#endif // _Geom_OffsetCurveUtils_HeaderFile diff --git a/src/ModelingData/TKG3d/Geom/Geom_OffsetSurface.cxx b/src/ModelingData/TKG3d/Geom/Geom_OffsetSurface.cxx index 8ba66be5fc..f172fc7a10 100644 --- a/src/ModelingData/TKG3d/Geom/Geom_OffsetSurface.cxx +++ b/src/ModelingData/TKG3d/Geom/Geom_OffsetSurface.cxx @@ -21,8 +21,11 @@ // UReverse et Vreverse #include +#include +#include #include #include +#include #include #include #include @@ -32,6 +35,8 @@ #include #include #include +#include "Geom_OffsetSurfaceUtils.pxx" +#include "Geom_OsculatingSurface.pxx" #include #include #include @@ -41,10 +46,10 @@ #include #include #include -#include +#include #include #include -#include +#include #include #include #include @@ -53,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -85,14 +91,18 @@ Geom_OffsetSurface::Geom_OffsetSurface(const Geom_OffsetSurface& theOther) ? Handle(Geom_Surface)() : Handle(Geom_Surface)::DownCast(theOther.equivSurf->Copy())), offsetValue(theOther.offsetValue), - myOscSurf(theOther.myOscSurf), - myBasisSurfContinuity(theOther.myBasisSurfContinuity), - myEvaluator(new GeomEvaluator_OffsetSurface(basisSurf, offsetValue, myOscSurf)) + myOscSurf(theOther.myOscSurf ? std::make_unique(*theOther.myOscSurf) + : nullptr), + myBasisSurfContinuity(theOther.myBasisSurfContinuity) { // Deep copy without validation - source surface is already validated } -//======================================================================= +//================================================================================================= + +Geom_OffsetSurface::~Geom_OffsetSurface() = default; + +//================================================================================================= Geom_OffsetSurface::Geom_OffsetSurface(const Handle(Geom_Surface)& theSurf, const Standard_Real theOffset, @@ -223,12 +233,8 @@ void Geom_OffsetSurface::SetBasisSurface(const Handle(Geom_Surface)& S, // et aussi pour les singularite. Pour les surfaces osculatrices, on l'utilise pour // detecter si une iso est degeneree. constexpr Standard_Real Tol = Precision::Confusion(); // 0.0001; - myOscSurf = new Geom_OsculatingSurface(aCheckingSurf, Tol); + myOscSurf = std::make_unique(aCheckingSurf, Tol); } - - // Surface value calculator - if (equivSurf.IsNull()) - myEvaluator = new GeomEvaluator_OffsetSurface(basisSurf, offsetValue, myOscSurf); } //================================================================================================= @@ -237,13 +243,6 @@ void Geom_OffsetSurface::SetOffsetValue(const Standard_Real D) { offsetValue = D; equivSurf = Surface(); - if (equivSurf.IsNull()) - { - if (myEvaluator.IsNull()) - myEvaluator = new GeomEvaluator_OffsetSurface(basisSurf, offsetValue, myOscSurf); - else - myEvaluator->SetOffsetValue(offsetValue); - } } //================================================================================================= @@ -254,8 +253,6 @@ void Geom_OffsetSurface::UReverse() offsetValue = -offsetValue; if (!equivSurf.IsNull()) equivSurf->UReverse(); - else - myEvaluator->SetOffsetValue(offsetValue); } //================================================================================================= @@ -273,8 +270,6 @@ void Geom_OffsetSurface::VReverse() offsetValue = -offsetValue; if (!equivSurf.IsNull()) equivSurf->VReverse(); - else - myEvaluator->SetOffsetValue(offsetValue); } //================================================================================================= @@ -320,10 +315,17 @@ void Geom_OffsetSurface::D0(const Standard_Real U, const Standard_Real V, gp_Pnt if (myBasisSurfContinuity == GeomAbs_C0) throw Geom_UndefinedValue(); #endif - if (equivSurf.IsNull()) - myEvaluator->D0(U, V, P); - else + if (!equivSurf.IsNull()) + { equivSurf->D0(U, V, P); + return; + } + + if (!Geom_OffsetSurfaceUtils::EvaluateD0(U, V, basisSurf.get(), offsetValue, myOscSurf.get(), P)) + { + throw Geom_UndefinedValue( + "Geom_OffsetSurface::D0(): Unable to calculate value at singular point"); + } } //================================================================================================= @@ -338,10 +340,24 @@ void Geom_OffsetSurface::D1(const Standard_Real U, if (myBasisSurfContinuity == GeomAbs_C0 || myBasisSurfContinuity == GeomAbs_C1) throw Geom_UndefinedDerivative(); #endif - if (equivSurf.IsNull()) - myEvaluator->D1(U, V, P, D1U, D1V); - else + if (!equivSurf.IsNull()) + { equivSurf->D1(U, V, P, D1U, D1V); + return; + } + + if (!Geom_OffsetSurfaceUtils::EvaluateD1(U, + V, + basisSurf.get(), + offsetValue, + myOscSurf.get(), + P, + D1U, + D1V)) + { + throw Geom_UndefinedDerivative( + "Geom_OffsetSurface::D1(): Unable to calculate derivative at singular point"); + } } //================================================================================================= @@ -360,10 +376,27 @@ void Geom_OffsetSurface::D2(const Standard_Real U, || myBasisSurfContinuity == GeomAbs_C2) throw Geom_UndefinedDerivative(); #endif - if (equivSurf.IsNull()) - myEvaluator->D2(U, V, P, D1U, D1V, D2U, D2V, D2UV); - else + if (!equivSurf.IsNull()) + { equivSurf->D2(U, V, P, D1U, D1V, D2U, D2V, D2UV); + return; + } + + if (!Geom_OffsetSurfaceUtils::EvaluateD2(U, + V, + basisSurf.get(), + offsetValue, + myOscSurf.get(), + P, + D1U, + D1V, + D2U, + D2V, + D2UV)) + { + throw Geom_UndefinedDerivative( + "Geom_OffsetSurface::D2(): Unable to calculate derivative at singular point"); + } } //================================================================================================= @@ -387,10 +420,31 @@ void Geom_OffsetSurface::D3(const Standard_Real U, throw Geom_UndefinedDerivative(); } #endif - if (equivSurf.IsNull()) - myEvaluator->D3(U, V, P, D1U, D1V, D2U, D2V, D2UV, D3U, D3V, D3UUV, D3UVV); - else + if (!equivSurf.IsNull()) + { equivSurf->D3(U, V, P, D1U, D1V, D2U, D2V, D2UV, D3U, D3V, D3UUV, D3UVV); + return; + } + + if (!Geom_OffsetSurfaceUtils::EvaluateD3(U, + V, + basisSurf.get(), + offsetValue, + myOscSurf.get(), + P, + D1U, + D1V, + D2U, + D2V, + D2UV, + D3U, + D3V, + D3UUV, + D3UVV)) + { + throw Geom_UndefinedDerivative( + "Geom_OffsetSurface::D3(): Unable to calculate derivative at singular point"); + } } //================================================================================================= @@ -407,13 +461,25 @@ gp_Vec Geom_OffsetSurface::DN(const Standard_Real U, throw Geom_UndefinedDerivative(); } #endif - gp_Vec D(0, 0, 0); + if (!equivSurf.IsNull()) + { + return equivSurf->DN(U, V, Nu, Nv); + } - if (equivSurf.IsNull()) - D = myEvaluator->DN(U, V, Nu, Nv); - else - D = equivSurf->DN(U, V, Nu, Nv); - return D; + gp_Vec aResult; + if (!Geom_OffsetSurfaceUtils::EvaluateDN(U, + V, + Nu, + Nv, + basisSurf.get(), + offsetValue, + myOscSurf.get(), + aResult)) + { + throw Geom_UndefinedDerivative( + "Geom_OffsetSurface::DN(): Unable to calculate derivative at singular point"); + } + return aResult; } ////************************************************* @@ -763,10 +829,6 @@ void Geom_OffsetSurface::Transform(const gp_Trsf& T) basisSurf->Transform(T); offsetValue *= T.ScaleFactor(); equivSurf.Nullify(); - if (myEvaluator.IsNull()) - myEvaluator = new GeomEvaluator_OffsetSurface(basisSurf, offsetValue, myOscSurf); - else - myEvaluator->SetOffsetValue(offsetValue); } //================================================================================================= @@ -953,7 +1015,7 @@ Standard_Boolean Geom_OffsetSurface::UOsculatingSurface(const Standard_Real Standard_Boolean& t, Handle(Geom_BSplineSurface)& L) const { - return !myOscSurf.IsNull() && myOscSurf->UOscSurf(U, V, t, L); + return myOscSurf && myOscSurf->UOscSurf(U, V, t, L); } //================================================================================================= @@ -963,7 +1025,7 @@ Standard_Boolean Geom_OffsetSurface::VOsculatingSurface(const Standard_Real Standard_Boolean& t, Handle(Geom_BSplineSurface)& L) const { - return !myOscSurf.IsNull() && myOscSurf->VOscSurf(U, V, t, L); + return myOscSurf && myOscSurf->VOscSurf(U, V, t, L); } //================================================================================================= @@ -978,6 +1040,5 @@ void Geom_OffsetSurface::DumpJson(Standard_OStream& theOStream, Standard_Integer OCCT_DUMP_FIELD_VALUES_DUMPED(theOStream, theDepth, equivSurf.get()) OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, offsetValue) - OCCT_DUMP_FIELD_VALUES_DUMPED(theOStream, theDepth, myOscSurf.get()) OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, myBasisSurfContinuity) } diff --git a/src/ModelingData/TKG3d/Geom/Geom_OffsetSurface.hxx b/src/ModelingData/TKG3d/Geom/Geom_OffsetSurface.hxx index 2e40528637..7a2f2ed16d 100644 --- a/src/ModelingData/TKG3d/Geom/Geom_OffsetSurface.hxx +++ b/src/ModelingData/TKG3d/Geom/Geom_OffsetSurface.hxx @@ -21,13 +21,16 @@ #include #include +#include #include #include -#include + +#include + class Geom_Curve; +class Geom_OsculatingSurface; class gp_Pnt; class gp_Vec; -class Geom_BSplineSurface; class gp_Trsf; class gp_GTrsf2d; class Geom_Geometry; @@ -105,6 +108,9 @@ public: //! @param[in] theOther the offset surface to copy from Standard_EXPORT Geom_OffsetSurface(const Geom_OffsetSurface& theOther); + //! Destructor. + Standard_EXPORT ~Geom_OffsetSurface(); + //! Changes this offset surface by assigning D as the offset value. Standard_EXPORT void SetOffsetValue(const Standard_Real D); @@ -115,9 +121,6 @@ public: //! Note: The basis surface can be an offset surface. inline const Handle(Geom_Surface)& BasisSurface() const { return basisSurf; } - //! Returns osculating surface if base surface is B-spline or Bezier - inline const Handle(Geom_OsculatingSurface)& OsculatingSurface() const { return myOscSurf; } - //! Changes the orientation of this offset surface in the u //! parametric direction. The bounds of the surface //! are not changed but the given parametric direction is reversed. @@ -388,12 +391,11 @@ public: DEFINE_STANDARD_RTTIEXT(Geom_OffsetSurface, Geom_Surface) private: - Handle(Geom_Surface) basisSurf; - Handle(Geom_Surface) equivSurf; - Standard_Real offsetValue; - Handle(Geom_OsculatingSurface) myOscSurf; - GeomAbs_Shape myBasisSurfContinuity; - Handle(GeomEvaluator_OffsetSurface) myEvaluator; + Handle(Geom_Surface) basisSurf; + Handle(Geom_Surface) equivSurf; + Standard_Real offsetValue; + std::unique_ptr myOscSurf; + GeomAbs_Shape myBasisSurfContinuity; }; #endif // _Geom_OffsetSurface_HeaderFile diff --git a/src/ModelingData/TKG3d/Geom/Geom_OffsetSurfaceUtils.pxx b/src/ModelingData/TKG3d/Geom/Geom_OffsetSurfaceUtils.pxx new file mode 100644 index 0000000000..660984ef50 --- /dev/null +++ b/src/ModelingData/TKG3d/Geom/Geom_OffsetSurfaceUtils.pxx @@ -0,0 +1,1514 @@ +// Copyright (c) 2015-2025 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 _Geom_OffsetSurfaceUtils_HeaderFile +#define _Geom_OffsetSurfaceUtils_HeaderFile + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +//! Internal helper namespace for 3D offset surface calculations. +//! Provides static inline functions to compute offset surface point and derivatives +//! from basis surface derivatives. +//! +//! Includes both non-singular (simple cross product normal) and singular +//! (osculating surface) case handling. +//! +//! Mathematical basis: +//! P(u,v) = p(u,v) + Offset * N / ||N|| +//! where N = dP/du ^ dP/dv is the surface normal +namespace Geom_OffsetSurfaceUtils +{ + +//! Default tolerance for normal magnitude check +constexpr double THE_D1_MAGNITUDE_TOL = 1.e-9; + +//! Struct to hold osculating surface query results. +//! Used to abstract osculating surface handling between different classes. +struct OsculatingInfo +{ + bool AlongU = false; //!< True if osculating along U direction + bool AlongV = false; //!< True if osculating along V direction + bool IsOpposite = false; //!< True if normal direction should be reversed + + //! Returns the sign factor for offset calculation + double Sign() const { return ((AlongU || AlongV) && IsOpposite) ? -1.0 : 1.0; } + + //! Returns true if osculating surface is available + bool HasOsculating() const { return AlongU || AlongV; } +}; + +//! Checks if a vector has infinite coordinates. +//! @param[in] theVec vector to check +//! @return true if any coordinate is infinite +inline bool IsInfiniteCoord(const gp_Vec& theVec) +{ + return Precision::IsInfinite(theVec.X()) || Precision::IsInfinite(theVec.Y()) + || Precision::IsInfinite(theVec.Z()); +} + +//! Checks if surface normal is singular (has zero magnitude). +//! @param[in] theD1U first derivative with respect to U +//! @param[in] theD1V first derivative with respect to V +//! @param[in] theTol tolerance for magnitude check +//! @return true if normal magnitude is below tolerance (singular case) +inline bool IsSingular(const gp_Vec& theD1U, + const gp_Vec& theD1V, + double theTol = THE_D1_MAGNITUDE_TOL) +{ + // Normalize derivatives before normal calculation for stability + gp_Vec aD1U(theD1U); + gp_Vec aD1V(theD1V); + double aD1UNorm2 = aD1U.SquareMagnitude(); + double aD1VNorm2 = aD1V.SquareMagnitude(); + if (aD1UNorm2 > 1.0) + aD1U /= std::sqrt(aD1UNorm2); + if (aD1VNorm2 > 1.0) + aD1V /= std::sqrt(aD1VNorm2); + + gp_Vec aNorm = aD1U.Crossed(aD1V); + return aNorm.SquareMagnitude() <= theTol * theTol; +} + +//! Calculates normalized normal vector for non-singular case. +//! @param[in] theD1U first derivative with respect to U +//! @param[in] theD1V first derivative with respect to V +//! @param[out] theNormal computed normalized normal (valid only if return is true) +//! @param[in] theTol tolerance for magnitude check +//! @return true if normal computed successfully, false if singular +inline bool ComputeNormal(const gp_Vec& theD1U, + const gp_Vec& theD1V, + gp_Vec& theNormal, + double theTol = THE_D1_MAGNITUDE_TOL) +{ + // Normalize derivatives before normal calculation for stability + gp_Vec aD1U(theD1U); + gp_Vec aD1V(theD1V); + double aD1UNorm2 = aD1U.SquareMagnitude(); + double aD1VNorm2 = aD1V.SquareMagnitude(); + if (aD1UNorm2 > 1.0) + aD1U /= std::sqrt(aD1UNorm2); + if (aD1VNorm2 > 1.0) + aD1V /= std::sqrt(aD1VNorm2); + + theNormal = aD1U.Crossed(aD1V); + if (theNormal.SquareMagnitude() <= theTol * theTol) + { + return false; + } + theNormal.Normalize(); + return true; +} + +//! Computes dN/du for non-singular offset surface. +//! @param[in] theD1U first derivative with respect to U +//! @param[in] theD1V first derivative with respect to V +//! @param[in] theD2U second derivative d2P/du2 +//! @param[in] theD2UV mixed derivative d2P/dudv +//! @param[in] theNormal unit normal vector +//! @return derivative of normal with respect to U +inline gp_Vec ComputeDNormalU(const gp_Vec& theD1U, + const gp_Vec& theD1V, + const gp_Vec& theD2U, + const gp_Vec& theD2UV, + const gp_Vec& theNormal) +{ + double aScale = (theD1U ^ theD1V).Dot(theNormal); + + gp_Vec aN1U; + aN1U.SetX(theD2U.Y() * theD1V.Z() + theD1U.Y() * theD2UV.Z() - theD2U.Z() * theD1V.Y() + - theD1U.Z() * theD2UV.Y()); + aN1U.SetY(-(theD2U.X() * theD1V.Z() + theD1U.X() * theD2UV.Z() - theD2U.Z() * theD1V.X() + - theD1U.Z() * theD2UV.X())); + aN1U.SetZ(theD2U.X() * theD1V.Y() + theD1U.X() * theD2UV.Y() - theD2U.Y() * theD1V.X() + - theD1U.Y() * theD2UV.X()); + double aScaleU = aN1U.Dot(theNormal); + aN1U.Subtract(aScaleU * theNormal); + aN1U /= aScale; + + return aN1U; +} + +//! Computes dN/dv for non-singular offset surface. +//! @param[in] theD1U first derivative with respect to U +//! @param[in] theD1V first derivative with respect to V +//! @param[in] theD2V second derivative d2P/dv2 +//! @param[in] theD2UV mixed derivative d2P/dudv +//! @param[in] theNormal unit normal vector +//! @return derivative of normal with respect to V +inline gp_Vec ComputeDNormalV(const gp_Vec& theD1U, + const gp_Vec& theD1V, + const gp_Vec& theD2V, + const gp_Vec& theD2UV, + const gp_Vec& theNormal) +{ + double aScale = (theD1U ^ theD1V).Dot(theNormal); + + gp_Vec aN1V; + aN1V.SetX(theD2UV.Y() * theD1V.Z() + theD2V.Z() * theD1U.Y() - theD2UV.Z() * theD1V.Y() + - theD2V.Y() * theD1U.Z()); + aN1V.SetY(-(theD2UV.X() * theD1V.Z() + theD2V.Z() * theD1U.X() - theD2UV.Z() * theD1V.X() + - theD2V.X() * theD1U.Z())); + aN1V.SetZ(theD2UV.X() * theD1V.Y() + theD2V.Y() * theD1U.X() - theD2UV.Y() * theD1V.X() + - theD2V.X() * theD1U.Y()); + double aScaleV = aN1V.Dot(theNormal); + aN1V.Subtract(aScaleV * theNormal); + aN1V /= aScale; + + return aN1V; +} + +//! Calculates D0 (point) for offset surface in non-singular case. +//! @param[in,out] theValue on input: basis surface point; on output: offset point +//! @param[in] theD1U first derivative with respect to U +//! @param[in] theD1V first derivative with respect to V +//! @param[in] theOffset offset distance value +//! @param[in] theSign sign factor (1.0 or -1.0) for offset direction +//! @return false if singular (normal has zero magnitude), true otherwise +inline bool CalculateD0(gp_Pnt& theValue, + const gp_Vec& theD1U, + const gp_Vec& theD1V, + double theOffset, + double theSign = 1.0) +{ + gp_Vec aNorm; + if (!ComputeNormal(theD1U, theD1V, aNorm)) + { + return false; + } + theValue.SetXYZ(theValue.XYZ() + theOffset * theSign * aNorm.XYZ()); + return true; +} + +//! Calculates D0 and D1 for offset surface in non-singular case. +//! @param[in,out] theValue on input: basis surface point; on output: offset point +//! @param[in,out] theD1U on input: basis dP/du; on output: offset surface dP/du +//! @param[in,out] theD1V on input: basis dP/dv; on output: offset surface dP/dv +//! @param[in] theD2U second derivative d2P/du2 of basis surface +//! @param[in] theD2V second derivative d2P/dv2 of basis surface +//! @param[in] theD2UV mixed derivative d2P/dudv of basis surface +//! @param[in] theOffset offset distance value +//! @param[in] theSign sign factor (1.0 or -1.0) for offset direction +//! @return false if singular (normal has zero magnitude), true otherwise +inline bool CalculateD1(gp_Pnt& theValue, + gp_Vec& theD1U, + gp_Vec& theD1V, + const gp_Vec& theD2U, + const gp_Vec& theD2V, + const gp_Vec& theD2UV, + double theOffset, + double theSign = 1.0) +{ + gp_Vec aNorm; + if (!ComputeNormal(theD1U, theD1V, aNorm)) + { + return false; + } + + // Compute offset point + theValue.SetXYZ(theValue.XYZ() + theOffset * theSign * aNorm.XYZ()); + + // Compute normal derivatives + gp_Vec aN1U = ComputeDNormalU(theD1U, theD1V, theD2U, theD2UV, aNorm); + gp_Vec aN1V = ComputeDNormalV(theD1U, theD1V, theD2V, theD2UV, aNorm); + + theD1U += theOffset * theSign * aN1U; + theD1V += theOffset * theSign * aN1V; + + return true; +} + +//! Calculates D0, D1, D2 for offset surface in non-singular case. +//! @param[in,out] theValue on input: basis surface point; on output: offset point +//! @param[in,out] theD1U on input: basis dP/du; on output: offset surface dP/du +//! @param[in,out] theD1V on input: basis dP/dv; on output: offset surface dP/dv +//! @param[in] theD2U second derivative d2P/du2 of basis surface (not modified for D2) +//! @param[in] theD2V second derivative d2P/dv2 of basis surface (not modified for D2) +//! @param[in] theD2UV mixed derivative d2P/dudv of basis surface (not modified for D2) +//! @param[in] theOffset offset distance value +//! @param[in] theSign sign factor (1.0 or -1.0) for offset direction +//! @return false if singular (normal has zero magnitude), true otherwise +inline bool CalculateD2(gp_Pnt& theValue, + gp_Vec& theD1U, + gp_Vec& theD1V, + const gp_Vec& theD2U, + const gp_Vec& theD2V, + const gp_Vec& theD2UV, + double theOffset, + double theSign = 1.0) +{ + // For D2 in non-singular case, we use the same approach as D1 + // The actual D2 derivatives require more complex computation + return CalculateD1(theValue, theD1U, theD1V, theD2U, theD2V, theD2UV, theOffset, theSign); +} + +//! Template function for computing derivatives at singular points. +//! Works with any surface type that provides D1, D2, D3, DN methods. +//! @tparam BasisSurfType type of basis surface (Handle to surface or adaptor) +//! @tparam OscSurfType type of osculating surface (Handle to BSpline surface) +//! @param[in] theMaxOrder maximum derivative order +//! @param[in] theMinOrder minimum derivative order +//! @param[in] theU U parameter +//! @param[in] theV V parameter +//! @param[in] theBasisSurf basis surface +//! @param[in] theNU derivative order in U for output +//! @param[in] theNV derivative order in V for output +//! @param[in] theAlongU true if osculating along U +//! @param[in] theAlongV true if osculating along V +//! @param[in] theOscSurf osculating surface (may be null) +//! @param[out] theDerNUV array of normal derivatives +//! @param[in,out] theDerSurf array of surface derivatives +template +void ComputeDerivatives(int theMaxOrder, + int theMinOrder, + double theU, + double theV, + const BasisSurfType& theBasisSurf, + int theNU, + int theNV, + bool theAlongU, + bool theAlongV, + const OscSurfType& theOscSurf, + TColgp_Array2OfVec& theDerNUV, + TColgp_Array2OfVec& theDerSurf) +{ + gp_Pnt P; + gp_Vec DL1U, DL1V, DL2U, DL2V, DL2UV, DL3U, DL3UUV, DL3UVV, DL3V; + + if (theAlongU || theAlongV) + { + theMaxOrder = 0; + TColgp_Array2OfVec DerSurfL(0, theMaxOrder + theNU + 1, 0, theMaxOrder + theNV + 1); + switch (theMinOrder) + { + case 1: + theOscSurf->D1(theU, theV, P, DL1U, DL1V); + DerSurfL.SetValue(1, 0, DL1U); + DerSurfL.SetValue(0, 1, DL1V); + break; + case 2: + theOscSurf->D2(theU, theV, P, DL1U, DL1V, DL2U, DL2V, DL2UV); + DerSurfL.SetValue(1, 0, DL1U); + DerSurfL.SetValue(0, 1, DL1V); + DerSurfL.SetValue(1, 1, DL2UV); + DerSurfL.SetValue(2, 0, DL2U); + DerSurfL.SetValue(0, 2, DL2V); + break; + case 3: + theOscSurf->D3(theU, theV, P, DL1U, DL1V, DL2U, DL2V, DL2UV, DL3U, DL3V, DL3UUV, DL3UVV); + DerSurfL.SetValue(1, 0, DL1U); + DerSurfL.SetValue(0, 1, DL1V); + DerSurfL.SetValue(1, 1, DL2UV); + DerSurfL.SetValue(2, 0, DL2U); + DerSurfL.SetValue(0, 2, DL2V); + DerSurfL.SetValue(3, 0, DL3U); + DerSurfL.SetValue(2, 1, DL3UUV); + DerSurfL.SetValue(1, 2, DL3UVV); + DerSurfL.SetValue(0, 3, DL3V); + break; + default: + break; + } + + if (theNU <= theNV) + { + for (int i = 0; i <= theMaxOrder + 1 + theNU; i++) + for (int j = i; j <= theMaxOrder + theNV + 1; j++) + if (i + j > theMinOrder) + { + DerSurfL.SetValue(i, j, theOscSurf->DN(theU, theV, i, j)); + theDerSurf.SetValue(i, j, theBasisSurf->DN(theU, theV, i, j)); + if (i != j && j <= theNU + 1) + { + theDerSurf.SetValue(j, i, theBasisSurf->DN(theU, theV, j, i)); + DerSurfL.SetValue(j, i, theOscSurf->DN(theU, theV, j, i)); + } + } + } + else + { + for (int j = 0; j <= theMaxOrder + 1 + theNV; j++) + for (int i = j; i <= theMaxOrder + theNU + 1; i++) + if (i + j > theMinOrder) + { + DerSurfL.SetValue(i, j, theOscSurf->DN(theU, theV, i, j)); + theDerSurf.SetValue(i, j, theBasisSurf->DN(theU, theV, i, j)); + if (i != j && i <= theNV + 1) + { + theDerSurf.SetValue(j, i, theBasisSurf->DN(theU, theV, j, i)); + DerSurfL.SetValue(j, i, theOscSurf->DN(theU, theV, j, i)); + } + } + } + for (int i = 0; i <= theMaxOrder + theNU; i++) + for (int j = 0; j <= theMaxOrder + theNV; j++) + { + if (theAlongU) + theDerNUV.SetValue(i, j, CSLib::DNNUV(i, j, DerSurfL, theDerSurf)); + if (theAlongV) + theDerNUV.SetValue(i, j, CSLib::DNNUV(i, j, theDerSurf, DerSurfL)); + } + } + else + { + for (int i = 0; i <= theMaxOrder + theNU + 1; i++) + { + for (int j = i; j <= theMaxOrder + theNV + 1; j++) + { + if (i + j > theMinOrder) + { + theDerSurf.SetValue(i, j, theBasisSurf->DN(theU, theV, i, j)); + if (i != j && j <= theDerSurf.UpperRow() && i <= theDerSurf.UpperCol()) + { + theDerSurf.SetValue(j, i, theBasisSurf->DN(theU, theV, j, i)); + } + } + } + } + for (int i = 0; i <= theMaxOrder + theNU; i++) + for (int j = 0; j <= theMaxOrder + theNV; j++) + theDerNUV.SetValue(i, j, CSLib::DNNUV(i, j, theDerSurf)); + } +} + +//! Attempts to replace a zero derivative by stepping away and recomputing. +//! This handles CSLib_InfinityOfSolutions case where one derivative is zero. +//! +//! @tparam BasisSurfType type of basis surface (must have D1 method) +//! @param[in] theU U parameter +//! @param[in] theV V parameter +//! @param[in] theUMin minimum U bound +//! @param[in] theUMax maximum U bound +//! @param[in] theVMin minimum V bound +//! @param[in] theVMax maximum V bound +//! @param[in,out] theDU first derivative with respect to U +//! @param[in,out] theDV first derivative with respect to V +//! @param[in] theSquareTol squared tolerance for zero check +//! @param[in] theBasisSurf basis surface for computing derivatives +//! @return true if derivative was successfully replaced +template +bool ReplaceDerivative(double theU, + double theV, + double theUMin, + double theUMax, + double theVMin, + double theVMax, + gp_Vec& theDU, + gp_Vec& theDV, + double theSquareTol, + const BasisSurfType& theBasisSurf) +{ + bool isReplaceDU = theDU.SquareMagnitude() < theSquareTol; + bool isReplaceDV = theDV.SquareMagnitude() < theSquareTol; + bool isReplaced = false; + + // Only handle case where exactly one derivative is zero + if (isReplaceDU != isReplaceDV) + { + // Calculate step along non-zero derivative + double aStep; + if (isReplaceDV) + { + aStep = Precision::Confusion() * theDU.Magnitude(); + if (aStep > theUMax - theUMin) + aStep = (theUMax - theUMin) / 100.; + } + else + { + aStep = Precision::Confusion() * theDV.Magnitude(); + if (aStep > theVMax - theVMin) + aStep = (theVMax - theVMin) / 100.; + } + + gp_Pnt aP; + gp_Vec aDU, aDV; + + // Step away from current parametric coordinates and calculate derivatives once again. + // Replace zero derivative by the obtained. + for (double aStepSign = -1.0; aStepSign <= 1.0 && !isReplaced; aStepSign += 2.0) + { + double aU = theU; + double aV = theV; + + if (isReplaceDV) + { + aU = theU + aStepSign * aStep; + if (aU < theUMin || aU > theUMax) + continue; + } + else + { + aV = theV + aStepSign * aStep; + if (aV < theVMin || aV > theVMax) + continue; + } + + theBasisSurf->D1(aU, aV, aP, aDU, aDV); + + if (isReplaceDU && aDU.SquareMagnitude() > theSquareTol) + { + theDU = aDU; + isReplaced = true; + } + if (isReplaceDV && aDV.SquareMagnitude() > theSquareTol) + { + theDV = aDV; + isReplaced = true; + } + } + } + return isReplaced; +} + +//! Computes normal using CSLib with higher order derivatives for singular case. +//! Includes handling for CSLib_InfinityOfSolutions using ReplaceDerivative. +//! @tparam BasisSurfType type of basis surface +//! @param[in] theMaxOrder maximum derivative order for CSLib +//! @param[in] theU U parameter +//! @param[in] theV V parameter +//! @param[in] theUMin minimum U bound +//! @param[in] theUMax maximum U bound +//! @param[in] theVMin minimum V bound +//! @param[in] theVMax maximum V bound +//! @param[in] theD1U first derivative with respect to U +//! @param[in] theD1V first derivative with respect to V +//! @param[in] theBasisSurf basis surface for computing higher derivatives +//! @param[out] theNormal computed normal direction +//! @param[out] theStatus normal computation status +template +void ComputeSingularNormal(int theMaxOrder, + double theU, + double theV, + double theUMin, + double theUMax, + double theVMin, + double theVMax, + const gp_Vec& theD1U, + const gp_Vec& theD1V, + const BasisSurfType& theBasisSurf, + gp_Dir& theNormal, + CSLib_NormalStatus& theStatus) +{ + // First try simple normal + CSLib::Normal(theD1U, theD1V, THE_D1_MAGNITUDE_TOL, theStatus, theNormal); + + if (theStatus != CSLib_Defined && theMaxOrder > 0) + { + // Try with higher derivatives + TColgp_Array2OfVec aDerNUV(0, theMaxOrder, 0, theMaxOrder); + aDerNUV.SetValue(1, 0, theD1U); + aDerNUV.SetValue(0, 1, theD1V); + for (int i = 0; i <= theMaxOrder; ++i) + { + for (int j = 0; j <= theMaxOrder; ++j) + { + if (i + j > 1 && i + j <= theMaxOrder) + aDerNUV.SetValue(i, j, theBasisSurf->DN(theU, theV, i, j)); + } + } + int OrderU, OrderV; + CSLib::Normal(theMaxOrder, + aDerNUV, + THE_D1_MAGNITUDE_TOL, + theU, + theV, + theUMin, + theUMax, + theVMin, + theVMax, + theStatus, + theNormal, + OrderU, + OrderV); + + // Handle CSLib_InfinityOfSolutions by replacing zero derivative + if (theStatus == CSLib_InfinityOfSolutions) + { + gp_Vec aNewDU = theD1U; + gp_Vec aNewDV = theD1V; + if (ReplaceDerivative(theU, + theV, + theUMin, + theUMax, + theVMin, + theVMax, + aNewDU, + aNewDV, + THE_D1_MAGNITUDE_TOL * THE_D1_MAGNITUDE_TOL, + theBasisSurf)) + { + CSLib::Normal(aNewDU, aNewDV, THE_D1_MAGNITUDE_TOL, theStatus, theNormal); + } + } + } +} + +//! Attempts to shift the evaluation point towards the center of the parametric space. +//! This is used when normal calculation fails at singular points near boundaries. +//! The point is shifted iteratively, each time doubling the distance from the start point. +//! +//! @param[in] theUStart original U parameter (for direction calculation) +//! @param[in] theVStart original V parameter (for direction calculation) +//! @param[in,out] theU current U parameter, modified on success +//! @param[in,out] theV current V parameter, modified on success +//! @param[in] theUMin minimum U bound +//! @param[in] theUMax maximum U bound +//! @param[in] theVMin minimum V bound +//! @param[in] theVMax maximum V bound +//! @param[in] theIsUPeriodic true if surface is U-periodic +//! @param[in] theIsVPeriodic true if surface is V-periodic +//! @param[in] theD1U first derivative with respect to U (for singularity check) +//! @param[in] theD1V first derivative with respect to V (for singularity check) +//! @return true if shift was successful, false if center is reached +inline bool ShiftPoint(double theUStart, + double theVStart, + double& theU, + double& theV, + double theUMin, + double theUMax, + double theVMin, + double theVMax, + bool theIsUPeriodic, + bool theIsVPeriodic, + const gp_Vec& theD1U, + const gp_Vec& theD1V) +{ + // Check if either U or V is singular (normally one of them is) + bool isUSingular = (theD1U.SquareMagnitude() < THE_D1_MAGNITUDE_TOL * THE_D1_MAGNITUDE_TOL); + bool isVSingular = (theD1V.SquareMagnitude() < THE_D1_MAGNITUDE_TOL * THE_D1_MAGNITUDE_TOL); + + // Compute vector to shift from start point to center of the surface; + // if surface is periodic or singular in some direction, take shift in that direction zero + double aDirU = + (theIsUPeriodic || (isUSingular && !isVSingular) ? 0. : 0.5 * (theUMin + theUMax) - theUStart); + double aDirV = + (theIsVPeriodic || (isVSingular && !isUSingular) ? 0. : 0.5 * (theVMin + theVMax) - theVStart); + double aDist = std::sqrt(aDirU * aDirU + aDirV * aDirV); + + // Shift current point from its current position towards center, by value of twice + // current distance from it to start (but not less than Precision::PConfusion()); + // fail if center is overpassed. + double aDU = theU - theUStart; + double aDV = theV - theVStart; + double aStep = std::max(2. * std::sqrt(aDU * aDU + aDV * aDV), Precision::PConfusion()); + if (aStep >= aDist) + { + return false; + } + + aStep /= aDist; + theU += aDirU * aStep; + theV += aDirV * aStep; + + return true; +} + +//! Template function for D0 evaluation with retry mechanism for singular points. +//! When normal calculation fails, attempts to shift the point towards the center +//! and retry the calculation. +//! +//! @tparam BasisSurfType type of basis surface (must have D1 method) +//! @tparam OscSurfQueryType type providing osculating surface query +//! @param[in] theU U parameter +//! @param[in] theV V parameter +//! @param[in] theBasisSurf basis surface adaptor +//! @param[in] theOffset offset distance +//! @param[in] theOscQuery osculating surface query object (may be null) +//! @param[out] theValue computed offset point +//! @return true if calculation succeeded, false if failed at singular point +template +bool EvaluateD0(double theU, + double theV, + const BasisSurfType& theBasisSurf, + double theOffset, + const OscSurfQueryType& theOscQuery, + gp_Pnt& theValue) +{ + const double aUStart = theU; + const double aVStart = theV; + double aUMin, aUMax, aVMin, aVMax; + theBasisSurf->Bounds(aUMin, aUMax, aVMin, aVMax); + const bool isUPer = theBasisSurf->IsUPeriodic(); + const bool isVPer = theBasisSurf->IsVPeriodic(); + + for (;;) + { + gp_Vec aD1U, aD1V; + theBasisSurf->D1(theU, theV, theValue, aD1U, aD1V); + + if (IsInfiniteCoord(aD1U) || IsInfiniteCoord(aD1V)) + { + return false; + } + + // Get osculating surface info + OsculatingInfo aOscInfo; + Handle(Geom_BSplineSurface) aOscSurf; + if (theOscQuery) + { + aOscInfo.AlongU = theOscQuery->UOscSurf(theU, theV, aOscInfo.IsOpposite, aOscSurf); + aOscInfo.AlongV = theOscQuery->VOscSurf(theU, theV, aOscInfo.IsOpposite, aOscSurf); + } + + // Try non-singular case first + if (CalculateD0(theValue, aD1U, aD1V, theOffset, aOscInfo.Sign())) + { + return true; + } + + // Singular case - use higher order derivatives with osculating surface + constexpr int aMaxOrder = 3; + TColgp_Array2OfVec aDerNUV(0, aMaxOrder, 0, aMaxOrder); + TColgp_Array2OfVec aDerSurf(0, aMaxOrder + 1, 0, aMaxOrder + 1); + + aDerSurf.SetValue(1, 0, aD1U); + aDerSurf.SetValue(0, 1, aD1V); + + // Use ComputeDerivatives which handles osculating surface properly + if (aOscInfo.HasOsculating() && !aOscSurf.IsNull()) + { + ComputeDerivatives(aMaxOrder, + 1, + theU, + theV, + theBasisSurf, + 0, + 0, + aOscInfo.AlongU, + aOscInfo.AlongV, + aOscSurf, + aDerNUV, + aDerSurf); + } + else + { + Handle(Geom_BSplineSurface) aDummy; + ComputeDerivatives(aMaxOrder, + 1, + theU, + theV, + theBasisSurf, + 0, + 0, + false, + false, + aDummy, + aDerNUV, + aDerSurf); + } + + gp_Dir aNormal; + CSLib_NormalStatus aNStatus; + int OrderU, OrderV; + CSLib::Normal(aMaxOrder, + aDerNUV, + THE_D1_MAGNITUDE_TOL, + theU, + theV, + aUMin, + aUMax, + aVMin, + aVMax, + aNStatus, + aNormal, + OrderU, + OrderV); + + // Handle CSLib_InfinityOfSolutions by replacing zero derivative + if (aNStatus == CSLib_InfinityOfSolutions) + { + gp_Vec aNewDU = aD1U; + gp_Vec aNewDV = aD1V; + if (ReplaceDerivative(theU, + theV, + aUMin, + aUMax, + aVMin, + aVMax, + aNewDU, + aNewDV, + THE_D1_MAGNITUDE_TOL * THE_D1_MAGNITUDE_TOL, + theBasisSurf)) + { + CSLib::Normal(aNewDU, aNewDV, THE_D1_MAGNITUDE_TOL, aNStatus, aNormal); + } + } + + if (aNStatus == CSLib_Defined) + { + theValue.SetXYZ(theValue.XYZ() + theOffset * aOscInfo.Sign() * aNormal.XYZ()); + return true; + } + + // Try shifting point towards center - returns false when center is overpassed + if (!ShiftPoint(aUStart, + aVStart, + theU, + theV, + aUMin, + aUMax, + aVMin, + aVMax, + isUPer, + isVPer, + aD1U, + aD1V)) + { + return false; + } + } +} + +//! Template function for D1 evaluation with retry mechanism for singular points. +//! +//! @tparam BasisSurfType type of basis surface (must have D2 method) +//! @tparam OscSurfQueryType type providing osculating surface query +//! @param[in] theU U parameter +//! @param[in] theV V parameter +//! @param[in] theBasisSurf basis surface adaptor +//! @param[in] theOffset offset distance +//! @param[in] theOscQuery osculating surface query object (may be null) +//! @param[out] theValue computed offset point +//! @param[out] theD1U computed D1U derivative +//! @param[out] theD1V computed D1V derivative +//! @return true if calculation succeeded, false if failed at singular point +template +bool EvaluateD1(double theU, + double theV, + const BasisSurfType& theBasisSurf, + double theOffset, + const OscSurfQueryType& theOscQuery, + gp_Pnt& theValue, + gp_Vec& theD1U, + gp_Vec& theD1V) +{ + const double aUStart = theU; + const double aVStart = theV; + double aUMin, aUMax, aVMin, aVMax; + theBasisSurf->Bounds(aUMin, aUMax, aVMin, aVMax); + const bool isUPer = theBasisSurf->IsUPeriodic(); + const bool isVPer = theBasisSurf->IsVPeriodic(); + + for (;;) + { + gp_Vec aD2U, aD2V, aD2UV; + theBasisSurf->D2(theU, theV, theValue, theD1U, theD1V, aD2U, aD2V, aD2UV); + + if (IsInfiniteCoord(theD1U) || IsInfiniteCoord(theD1V)) + { + return false; + } + + // Try non-singular case first + OsculatingInfo aOscInfo; + Handle(Geom_BSplineSurface) aOscSurf; + if (theOscQuery) + { + aOscInfo.AlongU = theOscQuery->UOscSurf(theU, theV, aOscInfo.IsOpposite, aOscSurf); + aOscInfo.AlongV = theOscQuery->VOscSurf(theU, theV, aOscInfo.IsOpposite, aOscSurf); + } + + if (CalculateD1(theValue, theD1U, theD1V, aD2U, aD2V, aD2UV, theOffset, aOscInfo.Sign())) + { + return true; + } + + // Singular case - use higher order derivatives + constexpr int aMaxOrder = 3; + TColgp_Array2OfVec aDerNUV(0, aMaxOrder + 1, 0, aMaxOrder + 1); + TColgp_Array2OfVec aDerSurf(0, aMaxOrder + 2, 0, aMaxOrder + 2); + + aDerSurf.SetValue(1, 0, theD1U); + aDerSurf.SetValue(0, 1, theD1V); + aDerSurf.SetValue(1, 1, aD2UV); + aDerSurf.SetValue(2, 0, aD2U); + aDerSurf.SetValue(0, 2, aD2V); + + if (aOscInfo.HasOsculating() && !aOscSurf.IsNull()) + { + ComputeDerivatives(aMaxOrder, + 2, + theU, + theV, + theBasisSurf, + 1, + 1, + aOscInfo.AlongU, + aOscInfo.AlongV, + aOscSurf, + aDerNUV, + aDerSurf); + } + else + { + Handle(Geom_BSplineSurface) aDummy; + ComputeDerivatives(aMaxOrder, + 2, + theU, + theV, + theBasisSurf, + 1, + 1, + false, + false, + aDummy, + aDerNUV, + aDerSurf); + } + + gp_Dir aNormal; + CSLib_NormalStatus aNStatus; + int aOrderU, aOrderV; + CSLib::Normal(aMaxOrder, + aDerNUV, + THE_D1_MAGNITUDE_TOL, + theU, + theV, + aUMin, + aUMax, + aVMin, + aVMax, + aNStatus, + aNormal, + aOrderU, + aOrderV); + + // Handle CSLib_InfinityOfSolutions by replacing zero derivative + if (aNStatus == CSLib_InfinityOfSolutions) + { + gp_Vec aNewDU = theD1U; + gp_Vec aNewDV = theD1V; + if (ReplaceDerivative(theU, + theV, + aUMin, + aUMax, + aVMin, + aVMax, + aNewDU, + aNewDV, + THE_D1_MAGNITUDE_TOL * THE_D1_MAGNITUDE_TOL, + theBasisSurf)) + { + // Re-compute with replaced derivatives + aDerSurf.SetValue(1, 0, aNewDU); + aDerSurf.SetValue(0, 1, aNewDV); + if (aOscInfo.HasOsculating() && !aOscSurf.IsNull()) + { + ComputeDerivatives(aMaxOrder, + 2, + theU, + theV, + theBasisSurf, + 1, + 1, + aOscInfo.AlongU, + aOscInfo.AlongV, + aOscSurf, + aDerNUV, + aDerSurf); + } + else + { + Handle(Geom_BSplineSurface) aDummy; + ComputeDerivatives(aMaxOrder, + 2, + theU, + theV, + theBasisSurf, + 1, + 1, + false, + false, + aDummy, + aDerNUV, + aDerSurf); + } + CSLib::Normal(aMaxOrder, + aDerNUV, + THE_D1_MAGNITUDE_TOL, + theU, + theV, + aUMin, + aUMax, + aVMin, + aVMax, + aNStatus, + aNormal, + aOrderU, + aOrderV); + } + } + + if (aNStatus == CSLib_Defined) + { + // Compute offset point + theValue.SetXYZ(theValue.XYZ() + theOffset * aOscInfo.Sign() * aNormal.XYZ()); + // Compute D1 using CSLib + theD1U = CSLib::DNNormal(1, 0, aDerNUV, aOrderU, aOrderV); + theD1V = CSLib::DNNormal(0, 1, aDerNUV, aOrderU, aOrderV); + + theD1U.Multiply(theOffset * aOscInfo.Sign()); + theD1U.Add(aDerSurf(1, 0)); + theD1V.Multiply(theOffset * aOscInfo.Sign()); + theD1V.Add(aDerSurf(0, 1)); + return true; + } + + // Try shifting point towards center - returns false when center is overpassed + if (!ShiftPoint(aUStart, + aVStart, + theU, + theV, + aUMin, + aUMax, + aVMin, + aVMax, + isUPer, + isVPer, + theD1U, + theD1V)) + { + return false; + } + } +} + +//! Template function for D2 evaluation with retry mechanism for singular points. +//! Always uses CSLib path to match old evaluator behavior. +//! +//! @tparam BasisSurfType type of basis surface (must have D3 method) +//! @tparam OscSurfQueryType type providing osculating surface query +//! @param[in] theU U parameter +//! @param[in] theV V parameter +//! @param[in] theBasisSurf basis surface adaptor +//! @param[in] theOffset offset distance +//! @param[in] theOscQuery osculating surface query object (may be null) +//! @param[out] theValue computed offset point +//! @param[out] theD1U computed D1U derivative +//! @param[out] theD1V computed D1V derivative +//! @param[out] theD2U computed D2U derivative +//! @param[out] theD2V computed D2V derivative +//! @param[out] theD2UV computed D2UV derivative +//! @return true if calculation succeeded, false if failed at singular point +template +bool EvaluateD2(double theU, + double theV, + const BasisSurfType& theBasisSurf, + double theOffset, + const OscSurfQueryType& theOscQuery, + gp_Pnt& theValue, + gp_Vec& theD1U, + gp_Vec& theD1V, + gp_Vec& theD2U, + gp_Vec& theD2V, + gp_Vec& theD2UV) +{ + const double aUStart = theU; + const double aVStart = theV; + double aUMin, aUMax, aVMin, aVMax; + theBasisSurf->Bounds(aUMin, aUMax, aVMin, aVMax); + const bool isUPer = theBasisSurf->IsUPeriodic(); + const bool isVPer = theBasisSurf->IsVPeriodic(); + + for (;;) + { + gp_Vec aD3U, aD3V, aD3UUV, aD3UVV; + theBasisSurf->D3(theU, + theV, + theValue, + theD1U, + theD1V, + theD2U, + theD2V, + theD2UV, + aD3U, + aD3V, + aD3UUV, + aD3UVV); + + if (IsInfiniteCoord(theD1U) || IsInfiniteCoord(theD1V)) + { + return false; + } + + // Check if singular to determine MaxOrder + gp_Dir aNormal; + CSLib_NormalStatus aNStatus; + CSLib::Normal(theD1U, theD1V, THE_D1_MAGNITUDE_TOL, aNStatus, aNormal); + + const int aMaxOrder = (aNStatus == CSLib_Defined) ? 0 : 3; + int aOrderU, aOrderV; + + TColgp_Array2OfVec aDerNUV(0, aMaxOrder + 2, 0, aMaxOrder + 2); + TColgp_Array2OfVec aDerSurf(0, aMaxOrder + 3, 0, aMaxOrder + 3); + + aDerSurf.SetValue(1, 0, theD1U); + aDerSurf.SetValue(0, 1, theD1V); + aDerSurf.SetValue(1, 1, theD2UV); + aDerSurf.SetValue(2, 0, theD2U); + aDerSurf.SetValue(0, 2, theD2V); + aDerSurf.SetValue(3, 0, aD3U); + aDerSurf.SetValue(2, 1, aD3UUV); + aDerSurf.SetValue(1, 2, aD3UVV); + aDerSurf.SetValue(0, 3, aD3V); + + // Check osculating surface only in singular case + OsculatingInfo aOscInfo; + Handle(Geom_BSplineSurface) aOscSurf; + if ((aNStatus != CSLib_Defined) && theOscQuery) + { + aOscInfo.AlongU = theOscQuery->UOscSurf(theU, theV, aOscInfo.IsOpposite, aOscSurf); + aOscInfo.AlongV = theOscQuery->VOscSurf(theU, theV, aOscInfo.IsOpposite, aOscSurf); + } + + // Always use ComputeDerivatives (with MaxOrder=0 or 3) + if (aOscInfo.HasOsculating() && !aOscSurf.IsNull()) + { + ComputeDerivatives(aMaxOrder, + 3, + theU, + theV, + theBasisSurf, + 2, + 2, + aOscInfo.AlongU, + aOscInfo.AlongV, + aOscSurf, + aDerNUV, + aDerSurf); + } + else + { + Handle(Geom_BSplineSurface) aDummy; + ComputeDerivatives(aMaxOrder, + 3, + theU, + theV, + theBasisSurf, + 2, + 2, + false, + false, + aDummy, + aDerNUV, + aDerSurf); + } + + // Always call CSLib::Normal with full derivative arrays + CSLib::Normal(aMaxOrder, + aDerNUV, + THE_D1_MAGNITUDE_TOL, + theU, + theV, + aUMin, + aUMax, + aVMin, + aVMax, + aNStatus, + aNormal, + aOrderU, + aOrderV); + + if (aNStatus == CSLib_Defined) + { + const double aSign = theOffset * aOscInfo.Sign(); + + // Compute offset point + theValue.SetXYZ(theValue.XYZ() + aSign * aNormal.XYZ()); + + // Compute D1 using CSLib + theD1U = + aDerSurf(1, 0).Added(CSLib::DNNormal(1, 0, aDerNUV, aOrderU, aOrderV).Multiplied(aSign)); + theD1V = + aDerSurf(0, 1).Added(CSLib::DNNormal(0, 1, aDerNUV, aOrderU, aOrderV).Multiplied(aSign)); + + // For D2, re-fetch from basis surface (matching old evaluator behavior) + theD2U = theBasisSurf->DN(theU, theV, 2, 0); + theD2V = theBasisSurf->DN(theU, theV, 0, 2); + theD2UV = theBasisSurf->DN(theU, theV, 1, 1); + + // Add offset corrections + theD2U.Add(CSLib::DNNormal(2, 0, aDerNUV, aOrderU, aOrderV).Multiplied(aSign)); + theD2V.Add(CSLib::DNNormal(0, 2, aDerNUV, aOrderU, aOrderV).Multiplied(aSign)); + theD2UV.Add(CSLib::DNNormal(1, 1, aDerNUV, aOrderU, aOrderV).Multiplied(aSign)); + return true; + } + + // Try shifting point towards center - returns false when center is overpassed + if (!ShiftPoint(aUStart, + aVStart, + theU, + theV, + aUMin, + aUMax, + aVMin, + aVMax, + isUPer, + isVPer, + theD1U, + theD1V)) + { + return false; + } + } +} + +//! Template function for D3 evaluation with retry mechanism for singular points. +//! Always uses CSLib path to match old evaluator behavior. +//! +//! @tparam BasisSurfType type of basis surface (must have D3 method) +//! @tparam OscSurfQueryType type providing osculating surface query +//! @param[in] theU U parameter +//! @param[in] theV V parameter +//! @param[in] theBasisSurf basis surface adaptor +//! @param[in] theOffset offset distance +//! @param[in] theOscQuery osculating surface query object (may be null) +//! @param[out] theValue computed offset point +//! @param[out] theD1U computed D1U derivative +//! @param[out] theD1V computed D1V derivative +//! @param[out] theD2U computed D2U derivative +//! @param[out] theD2V computed D2V derivative +//! @param[out] theD2UV computed D2UV derivative +//! @param[out] theD3U computed D3U derivative +//! @param[out] theD3V computed D3V derivative +//! @param[out] theD3UUV computed D3UUV derivative +//! @param[out] theD3UVV computed D3UVV derivative +//! @return true if calculation succeeded, false if failed at singular point +template +bool EvaluateD3(double theU, + double theV, + const BasisSurfType& theBasisSurf, + double theOffset, + const OscSurfQueryType& theOscQuery, + gp_Pnt& theValue, + gp_Vec& theD1U, + gp_Vec& theD1V, + gp_Vec& theD2U, + gp_Vec& theD2V, + gp_Vec& theD2UV, + gp_Vec& theD3U, + gp_Vec& theD3V, + gp_Vec& theD3UUV, + gp_Vec& theD3UVV) +{ + const double aUStart = theU; + const double aVStart = theV; + double aUMin, aUMax, aVMin, aVMax; + theBasisSurf->Bounds(aUMin, aUMax, aVMin, aVMax); + const bool isUPer = theBasisSurf->IsUPeriodic(); + const bool isVPer = theBasisSurf->IsVPeriodic(); + + for (;;) + { + theBasisSurf->D3(theU, + theV, + theValue, + theD1U, + theD1V, + theD2U, + theD2V, + theD2UV, + theD3U, + theD3V, + theD3UUV, + theD3UVV); + + if (IsInfiniteCoord(theD1U) || IsInfiniteCoord(theD1V)) + { + return false; + } + + // Check if singular to determine MaxOrder + gp_Dir aNormal; + CSLib_NormalStatus aNStatus; + CSLib::Normal(theD1U, theD1V, THE_D1_MAGNITUDE_TOL, aNStatus, aNormal); + + const int aMaxOrder = (aNStatus == CSLib_Defined) ? 0 : 3; + int aOrderU, aOrderV; + + TColgp_Array2OfVec aDerNUV(0, aMaxOrder + 3, 0, aMaxOrder + 3); + TColgp_Array2OfVec aDerSurf(0, aMaxOrder + 4, 0, aMaxOrder + 4); + + aDerSurf.SetValue(1, 0, theD1U); + aDerSurf.SetValue(0, 1, theD1V); + aDerSurf.SetValue(1, 1, theD2UV); + aDerSurf.SetValue(2, 0, theD2U); + aDerSurf.SetValue(0, 2, theD2V); + aDerSurf.SetValue(3, 0, theD3U); + aDerSurf.SetValue(2, 1, theD3UUV); + aDerSurf.SetValue(1, 2, theD3UVV); + aDerSurf.SetValue(0, 3, theD3V); + + // Check osculating surface only in singular case + OsculatingInfo aOscInfo; + Handle(Geom_BSplineSurface) aOscSurf; + if ((aNStatus != CSLib_Defined) && theOscQuery) + { + aOscInfo.AlongU = theOscQuery->UOscSurf(theU, theV, aOscInfo.IsOpposite, aOscSurf); + aOscInfo.AlongV = theOscQuery->VOscSurf(theU, theV, aOscInfo.IsOpposite, aOscSurf); + } + + // Always use ComputeDerivatives (with MaxOrder=0 or 3) + if (aOscInfo.HasOsculating() && !aOscSurf.IsNull()) + { + ComputeDerivatives(aMaxOrder, + 3, + theU, + theV, + theBasisSurf, + 3, + 3, + aOscInfo.AlongU, + aOscInfo.AlongV, + aOscSurf, + aDerNUV, + aDerSurf); + } + else + { + Handle(Geom_BSplineSurface) aDummy; + ComputeDerivatives(aMaxOrder, + 3, + theU, + theV, + theBasisSurf, + 3, + 3, + false, + false, + aDummy, + aDerNUV, + aDerSurf); + } + + // Always call CSLib::Normal with full derivative arrays + CSLib::Normal(aMaxOrder, + aDerNUV, + THE_D1_MAGNITUDE_TOL, + theU, + theV, + aUMin, + aUMax, + aVMin, + aVMax, + aNStatus, + aNormal, + aOrderU, + aOrderV); + + if (aNStatus == CSLib_Defined) + { + const double aSign = theOffset * aOscInfo.Sign(); + + // Compute offset point + theValue.SetXYZ(theValue.XYZ() + aSign * aNormal.XYZ()); + + // Compute D1 using CSLib + theD1U = + aDerSurf(1, 0).Added(CSLib::DNNormal(1, 0, aDerNUV, aOrderU, aOrderV).Multiplied(aSign)); + theD1V = + aDerSurf(0, 1).Added(CSLib::DNNormal(0, 1, aDerNUV, aOrderU, aOrderV).Multiplied(aSign)); + + // For D2 and D3, re-fetch from basis surface (matching old evaluator behavior) + theD2U = theBasisSurf->DN(theU, theV, 2, 0); + theD2V = theBasisSurf->DN(theU, theV, 0, 2); + theD2UV = theBasisSurf->DN(theU, theV, 1, 1); + theD3U = theBasisSurf->DN(theU, theV, 3, 0); + theD3V = theBasisSurf->DN(theU, theV, 0, 3); + theD3UUV = theBasisSurf->DN(theU, theV, 2, 1); + theD3UVV = theBasisSurf->DN(theU, theV, 1, 2); + + // Add offset corrections + theD2U.Add(CSLib::DNNormal(2, 0, aDerNUV, aOrderU, aOrderV).Multiplied(aSign)); + theD2V.Add(CSLib::DNNormal(0, 2, aDerNUV, aOrderU, aOrderV).Multiplied(aSign)); + theD2UV.Add(CSLib::DNNormal(1, 1, aDerNUV, aOrderU, aOrderV).Multiplied(aSign)); + theD3U.Add(CSLib::DNNormal(3, 0, aDerNUV, aOrderU, aOrderV).Multiplied(aSign)); + theD3V.Add(CSLib::DNNormal(0, 3, aDerNUV, aOrderU, aOrderV).Multiplied(aSign)); + theD3UUV.Add(CSLib::DNNormal(2, 1, aDerNUV, aOrderU, aOrderV).Multiplied(aSign)); + theD3UVV.Add(CSLib::DNNormal(1, 2, aDerNUV, aOrderU, aOrderV).Multiplied(aSign)); + return true; + } + + // Try shifting point towards center - returns false when center is overpassed + if (!ShiftPoint(aUStart, + aVStart, + theU, + theV, + aUMin, + aUMax, + aVMin, + aVMax, + isUPer, + isVPer, + theD1U, + theD1V)) + { + return false; + } + } +} + +//! Template function for DN evaluation with retry mechanism for singular points. +//! Computes arbitrary order derivative of offset surface. +//! +//! @tparam BasisSurfType type of basis surface (must have D1 and DN methods) +//! @tparam OscSurfQueryType type providing osculating surface query +//! @param[in] theU U parameter +//! @param[in] theV V parameter +//! @param[in] theNu derivative order in U +//! @param[in] theNv derivative order in V +//! @param[in] theBasisSurf basis surface adaptor +//! @param[in] theOffset offset distance +//! @param[in] theOscQuery osculating surface query object (may be null) +//! @param[out] theResult computed derivative vector +//! @return true if calculation succeeded, false if failed at singular point +template +bool EvaluateDN(double theU, + double theV, + int theNu, + int theNv, + const BasisSurfType& theBasisSurf, + double theOffset, + const OscSurfQueryType& theOscQuery, + gp_Vec& theResult) +{ + const double aUStart = theU; + const double aVStart = theV; + double aUMin, aUMax, aVMin, aVMax; + theBasisSurf->Bounds(aUMin, aUMax, aVMin, aVMax); + const bool isUPer = theBasisSurf->IsUPeriodic(); + const bool isVPer = theBasisSurf->IsVPeriodic(); + + for (;;) + { + gp_Pnt aP; + gp_Vec aD1U, aD1V; + theBasisSurf->D1(theU, theV, aP, aD1U, aD1V); + + if (IsInfiniteCoord(aD1U) || IsInfiniteCoord(aD1V)) + { + return false; + } + + // Check if singular to determine MaxOrder + gp_Dir aNormal; + CSLib_NormalStatus aNStatus; + CSLib::Normal(aD1U, aD1V, THE_D1_MAGNITUDE_TOL, aNStatus, aNormal); + + const int aMaxOrder = (aNStatus == CSLib_Defined) ? 0 : 3; + int aOrderU, aOrderV; + + TColgp_Array2OfVec aDerNUV(0, aMaxOrder + theNu, 0, aMaxOrder + theNv); + TColgp_Array2OfVec aDerSurf(0, aMaxOrder + theNu + 1, 0, aMaxOrder + theNv + 1); + + aDerSurf.SetValue(1, 0, aD1U); + aDerSurf.SetValue(0, 1, aD1V); + + // Check osculating surface only in singular case + OsculatingInfo aOscInfo; + Handle(Geom_BSplineSurface) aOscSurf; + if ((aNStatus != CSLib_Defined) && theOscQuery) + { + aOscInfo.AlongU = theOscQuery->UOscSurf(theU, theV, aOscInfo.IsOpposite, aOscSurf); + aOscInfo.AlongV = theOscQuery->VOscSurf(theU, theV, aOscInfo.IsOpposite, aOscSurf); + } + + // Use ComputeDerivatives + if (aOscInfo.HasOsculating() && !aOscSurf.IsNull()) + { + ComputeDerivatives(aMaxOrder, + 1, + theU, + theV, + theBasisSurf, + theNu, + theNv, + aOscInfo.AlongU, + aOscInfo.AlongV, + aOscSurf, + aDerNUV, + aDerSurf); + } + else + { + Handle(Geom_BSplineSurface) aDummy; + ComputeDerivatives(aMaxOrder, + 1, + theU, + theV, + theBasisSurf, + theNu, + theNv, + false, + false, + aDummy, + aDerNUV, + aDerSurf); + } + + // Compute normal with CSLib + CSLib::Normal(aMaxOrder, + aDerNUV, + THE_D1_MAGNITUDE_TOL, + theU, + theV, + aUMin, + aUMax, + aVMin, + aVMax, + aNStatus, + aNormal, + aOrderU, + aOrderV); + + if (aNStatus == CSLib_Defined) + { + const double aSign = theOffset * aOscInfo.Sign(); + + // Compute DN result: basis DN + offset * DNNormal + theResult = theBasisSurf->DN(theU, theV, theNu, theNv); + theResult.Add(CSLib::DNNormal(theNu, theNv, aDerNUV, aOrderU, aOrderV).Multiplied(aSign)); + return true; + } + + // Try shifting point towards center - returns false when center is overpassed + if (!ShiftPoint(aUStart, + aVStart, + theU, + theV, + aUMin, + aUMax, + aVMin, + aVMax, + isUPer, + isVPer, + aD1U, + aD1V)) + { + return false; + } + } +} + +} // namespace Geom_OffsetSurfaceUtils + +#endif // _Geom_OffsetSurfaceUtils_HeaderFile diff --git a/src/ModelingData/TKG3d/Geom/Geom_OsculatingSurface.cxx b/src/ModelingData/TKG3d/Geom/Geom_OsculatingSurface.cxx index 3eeb8bf7d2..e5786e69a0 100644 --- a/src/ModelingData/TKG3d/Geom/Geom_OsculatingSurface.cxx +++ b/src/ModelingData/TKG3d/Geom/Geom_OsculatingSurface.cxx @@ -11,10 +11,12 @@ // Alternatively, this file may be used under the terms of Open CASCADE // commercial license or contractual agreement. +#include "Geom_OsculatingSurface.pxx" + #include #include #include -#include +#include #include #include #include @@ -26,61 +28,120 @@ #include #include -IMPLEMENT_STANDARD_RTTIEXT(Geom_OsculatingSurface, Standard_Transient) - //================================================================================================= Geom_OsculatingSurface::Geom_OsculatingSurface() : myTol(0.0), - myAlong(1, 4) + myAlong{false, false, false, false} +{ +} + +//================================================================================================= + +Geom_OsculatingSurface::Geom_OsculatingSurface(const Handle(Geom_Surface)& theBS, double theTol) + : myAlong{false, false, false, false} +{ + Init(theBS, theTol); +} + +//================================================================================================= + +Geom_OsculatingSurface::Geom_OsculatingSurface(const Geom_OsculatingSurface& theOther) + : myBasisSurf(theOther.myBasisSurf), + myTol(theOther.myTol), + myOsculSurf1(theOther.myOsculSurf1), + myOsculSurf2(theOther.myOsculSurf2), + myKdeg(theOther.myKdeg), + myAlong(theOther.myAlong) +{ +} + +//================================================================================================= + +Geom_OsculatingSurface::Geom_OsculatingSurface(Geom_OsculatingSurface&& theOther) noexcept + : myBasisSurf(std::move(theOther.myBasisSurf)), + myTol(theOther.myTol), + myOsculSurf1(std::move(theOther.myOsculSurf1)), + myOsculSurf2(std::move(theOther.myOsculSurf2)), + myKdeg(std::move(theOther.myKdeg)), + myAlong(theOther.myAlong) +{ + theOther.myTol = 0.0; + theOther.myAlong.fill(false); +} + +//================================================================================================= + +Geom_OsculatingSurface& Geom_OsculatingSurface::operator=(const Geom_OsculatingSurface& theOther) { - myAlong.Init(Standard_False); + if (this != &theOther) + { + myBasisSurf = theOther.myBasisSurf; + myTol = theOther.myTol; + myOsculSurf1 = theOther.myOsculSurf1; + myOsculSurf2 = theOther.myOsculSurf2; + myKdeg = theOther.myKdeg; + myAlong = theOther.myAlong; + } + return *this; } //================================================================================================= -Geom_OsculatingSurface::Geom_OsculatingSurface(const Handle(Geom_Surface)& BS, - const Standard_Real Tol) - : myAlong(1, 4) +Geom_OsculatingSurface& Geom_OsculatingSurface::operator=( + Geom_OsculatingSurface&& theOther) noexcept { - Init(BS, Tol); + if (this != &theOther) + { + myBasisSurf = std::move(theOther.myBasisSurf); + myTol = theOther.myTol; + myOsculSurf1 = std::move(theOther.myOsculSurf1); + myOsculSurf2 = std::move(theOther.myOsculSurf2); + myKdeg = std::move(theOther.myKdeg); + myAlong = theOther.myAlong; + + theOther.myTol = 0.0; + theOther.myAlong.fill(false); + } + return *this; } //================================================================================================= -void Geom_OsculatingSurface::Init(const Handle(Geom_Surface)& BS, const Standard_Real Tol) +void Geom_OsculatingSurface::Init(const Handle(Geom_Surface)& theBS, double theTol) { - ClearOsculFlags(); - myTol = Tol; - Standard_Real TolMin = 0.; // consider all singularities below Tol, not just above 1.e-12 - // (id23943) - Standard_Boolean OsculSurf = Standard_True; - myBasisSurf = Handle(Geom_Surface)::DownCast(BS->Copy()); - myOsculSurf1 = new Geom_HSequenceOfBSplineSurface(); - myOsculSurf2 = new Geom_HSequenceOfBSplineSurface(); - if ((BS->IsKind(STANDARD_TYPE(Geom_BSplineSurface))) - || (BS->IsKind(STANDARD_TYPE(Geom_BezierSurface)))) + clearOsculFlags(); + myTol = theTol; + double TolMin = 0.; // consider all singularities below Tol, not just above 1.e-12 + bool OsculSurf = true; + myBasisSurf = Handle(Geom_Surface)::DownCast(theBS->Copy()); + myOsculSurf1.Clear(); + myOsculSurf2.Clear(); + myKdeg.Clear(); + + if ((theBS->IsKind(STANDARD_TYPE(Geom_BSplineSurface))) + || (theBS->IsKind(STANDARD_TYPE(Geom_BezierSurface)))) { - Standard_Real U1 = 0, U2 = 0, V1 = 0, V2 = 0; - - Standard_Integer i = 1; - BS->Bounds(U1, U2, V1, V2); - myAlong.SetValue(1, IsQPunctual(BS, V1, GeomAbs_IsoV, TolMin, Tol)); - myAlong.SetValue(2, IsQPunctual(BS, V2, GeomAbs_IsoV, TolMin, Tol)); - myAlong.SetValue(3, IsQPunctual(BS, U1, GeomAbs_IsoU, TolMin, Tol)); - myAlong.SetValue(4, IsQPunctual(BS, U2, GeomAbs_IsoU, TolMin, Tol)); + double U1 = 0, U2 = 0, V1 = 0, V2 = 0; + + int i = 1; + theBS->Bounds(U1, U2, V1, V2); + myAlong[0] = isQPunctual(theBS, V1, GeomAbs_IsoV, TolMin, theTol); + myAlong[1] = isQPunctual(theBS, V2, GeomAbs_IsoV, TolMin, theTol); + myAlong[2] = isQPunctual(theBS, U1, GeomAbs_IsoU, TolMin, theTol); + myAlong[3] = isQPunctual(theBS, U2, GeomAbs_IsoU, TolMin, theTol); #ifdef OCCT_DEBUG - std::cout << myAlong(1) << std::endl - << myAlong(2) << std::endl - << myAlong(3) << std::endl - << myAlong(4) << std::endl; + std::cout << myAlong[0] << std::endl + << myAlong[1] << std::endl + << myAlong[2] << std::endl + << myAlong[3] << std::endl; #endif - if (myAlong(1) || myAlong(2) || myAlong(3) || myAlong(4)) + if (myAlong[0] || myAlong[1] || myAlong[2] || myAlong[3]) { Handle(Geom_BSplineSurface) InitSurf, L, S; - if (BS->IsKind(STANDARD_TYPE(Geom_BezierSurface))) + if (theBS->IsKind(STANDARD_TYPE(Geom_BezierSurface))) { - Handle(Geom_BezierSurface) BzS = Handle(Geom_BezierSurface)::DownCast(BS); + Handle(Geom_BezierSurface) BzS = Handle(Geom_BezierSurface)::DownCast(theBS); TColgp_Array2OfPnt P(1, BzS->NbUPoles(), 1, BzS->NbVPoles()); TColStd_Array1OfReal UKnots(1, 2); TColStd_Array1OfReal VKnots(1, 2); @@ -114,159 +175,156 @@ void Geom_OsculatingSurface::Init(const Handle(Geom_Surface)& BS, const Standard #endif if (IsAlongU() && IsAlongV()) - ClearOsculFlags(); - // Standard_ConstructionError_Raise_if((IsAlongU() && - // IsAlongV()),"Geom_OsculatingSurface"); + clearOsculFlags(); + if ((IsAlongU() && InitSurf->VDegree() > 1) || (IsAlongV() && InitSurf->UDegree() > 1)) { - myKdeg = new TColStd_HSequenceOfInteger(); - Standard_Integer k = 0; - Standard_Boolean IsQPunc; - Standard_Integer UKnot, VKnot; - if (myAlong(1) || myAlong(2)) + int k = 0; + bool IsQPunc; + int UKnot, VKnot; + if (myAlong[0] || myAlong[1]) { for (i = 1; i < InitSurf->NbUKnots(); i++) { - if (myAlong(1)) + if (myAlong[0]) { S = InitSurf; k = 0; - IsQPunc = Standard_True; + IsQPunc = true; UKnot = i; VKnot = 1; while (IsQPunc) { - OsculSurf = BuildOsculatingSurface(V1, UKnot, VKnot, S, L); + OsculSurf = buildOsculatingSurface(V1, UKnot, VKnot, S, L); if (!OsculSurf) break; k++; #ifdef OCCT_DEBUG std::cout << "1.k = " << k << std::endl; #endif - IsQPunc = IsQPunctual(L, V1, GeomAbs_IsoV, 0., Tol); + IsQPunc = isQPunctual(L, V1, GeomAbs_IsoV, 0., theTol); UKnot = 1; VKnot = 1; S = L; } if (OsculSurf) - myOsculSurf1->Append(L); + myOsculSurf1.Append(L); else - ClearOsculFlags(); // myAlong.SetValue(1,Standard_False); - if (myAlong(2) && OsculSurf) + clearOsculFlags(); + if (myAlong[1] && OsculSurf) { S = InitSurf; k = 0; - IsQPunc = Standard_True; + IsQPunc = true; UKnot = i; VKnot = InitSurf->NbVKnots() - 1; while (IsQPunc) { - OsculSurf = BuildOsculatingSurface(V2, UKnot, VKnot, S, L); + OsculSurf = buildOsculatingSurface(V2, UKnot, VKnot, S, L); if (!OsculSurf) break; k++; #ifdef OCCT_DEBUG std::cout << "2.k = " << k << std::endl; #endif - IsQPunc = IsQPunctual(L, V2, GeomAbs_IsoV, 0., Tol); + IsQPunc = isQPunctual(L, V2, GeomAbs_IsoV, 0., theTol); UKnot = 1; VKnot = 1; S = L; } if (OsculSurf) { - myOsculSurf2->Append(L); - myKdeg->Append(k); + myOsculSurf2.Append(L); + myKdeg.Append(k); } } } else - // if (myAlong(2)) { S = InitSurf; k = 0; - IsQPunc = Standard_True; + IsQPunc = true; UKnot = i; VKnot = InitSurf->NbVKnots() - 1; while (IsQPunc) { - OsculSurf = BuildOsculatingSurface(V2, UKnot, VKnot, S, L); + OsculSurf = buildOsculatingSurface(V2, UKnot, VKnot, S, L); if (!OsculSurf) break; k++; #ifdef OCCT_DEBUG std::cout << "2.k = " << k << std::endl; #endif - IsQPunc = IsQPunctual(L, V2, GeomAbs_IsoV, 0., Tol); + IsQPunc = isQPunctual(L, V2, GeomAbs_IsoV, 0., theTol); UKnot = 1; VKnot = 1; S = L; } if (OsculSurf) { - myOsculSurf2->Append(L); - myKdeg->Append(k); + myOsculSurf2.Append(L); + myKdeg.Append(k); } else - ClearOsculFlags(); // myAlong.SetValue(2,Standard_False); + clearOsculFlags(); } } } - if (myAlong(3) || myAlong(4)) + if (myAlong[2] || myAlong[3]) { for (i = 1; i < InitSurf->NbVKnots(); i++) { - if (myAlong(3)) + if (myAlong[2]) { S = InitSurf; k = 0; - IsQPunc = Standard_True; + IsQPunc = true; UKnot = 1; VKnot = i; while (IsQPunc) { - OsculSurf = BuildOsculatingSurface(U1, UKnot, VKnot, S, L); + OsculSurf = buildOsculatingSurface(U1, UKnot, VKnot, S, L); if (!OsculSurf) break; k++; #ifdef OCCT_DEBUG std::cout << "1.k = " << k << std::endl; #endif - IsQPunc = IsQPunctual(L, U1, GeomAbs_IsoU, 0., Tol); + IsQPunc = isQPunctual(L, U1, GeomAbs_IsoU, 0., theTol); UKnot = 1; VKnot = 1; S = L; } if (OsculSurf) - myOsculSurf1->Append(L); + myOsculSurf1.Append(L); else - ClearOsculFlags(); // myAlong.SetValue(3,Standard_False); - if (myAlong(4) && OsculSurf) + clearOsculFlags(); + if (myAlong[3] && OsculSurf) { S = InitSurf; k = 0; - IsQPunc = Standard_True; + IsQPunc = true; UKnot = InitSurf->NbUKnots() - 1; VKnot = i; while (IsQPunc) { - OsculSurf = BuildOsculatingSurface(U2, UKnot, VKnot, S, L); + OsculSurf = buildOsculatingSurface(U2, UKnot, VKnot, S, L); if (!OsculSurf) break; k++; #ifdef OCCT_DEBUG std::cout << "2.k = " << k << std::endl; #endif - IsQPunc = IsQPunctual(L, U2, GeomAbs_IsoU, 0., Tol); + IsQPunc = isQPunctual(L, U2, GeomAbs_IsoU, 0., theTol); UKnot = 1; VKnot = 1; S = L; } if (OsculSurf) { - myOsculSurf2->Append(L); - myKdeg->Append(k); + myOsculSurf2.Append(L); + myKdeg.Append(k); } } } @@ -274,74 +332,60 @@ void Geom_OsculatingSurface::Init(const Handle(Geom_Surface)& BS, const Standard { S = InitSurf; k = 0; - IsQPunc = Standard_True; + IsQPunc = true; UKnot = InitSurf->NbUKnots() - 1; VKnot = i; while (IsQPunc) { - OsculSurf = BuildOsculatingSurface(U2, UKnot, VKnot, S, L); + OsculSurf = buildOsculatingSurface(U2, UKnot, VKnot, S, L); if (!OsculSurf) break; k++; #ifdef OCCT_DEBUG std::cout << "2.k = " << k << std::endl; #endif - IsQPunc = IsQPunctual(L, U2, GeomAbs_IsoU, 0., Tol); + IsQPunc = isQPunctual(L, U2, GeomAbs_IsoU, 0., theTol); UKnot = 1; VKnot = 1; S = L; } if (OsculSurf) { - myOsculSurf2->Append(L); - myKdeg->Append(k); + myOsculSurf2.Append(L); + myKdeg.Append(k); } else - ClearOsculFlags(); // myAlong.SetValue(4,Standard_False); + clearOsculFlags(); } } } } else { - ClearOsculFlags(); + clearOsculFlags(); } } } else - ClearOsculFlags(); + clearOsculFlags(); } //================================================================================================= -Handle(Geom_Surface) Geom_OsculatingSurface::BasisSurface() const +bool Geom_OsculatingSurface::UOscSurf(double theU, + double theV, + bool& theT, + Handle(Geom_BSplineSurface)& theL) const { - return myBasisSurf; -} - -//================================================================================================= - -Standard_Real Geom_OsculatingSurface::Tolerance() const -{ - return myTol; -} - -//================================================================================================= - -Standard_Boolean Geom_OsculatingSurface::UOscSurf(const Standard_Real U, - const Standard_Real V, - Standard_Boolean& t, - Handle(Geom_BSplineSurface)& L) const -{ - Standard_Boolean along = Standard_False; - if (myAlong(1) || myAlong(2)) + bool along = false; + if (myAlong[0] || myAlong[1]) { - Standard_Integer NU = 1, NV = 1; - Standard_Real u1, u2, v1, v2; - t = Standard_False; + int NU = 1, NV = 1; + double u1, u2, v1, v2; + theT = false; myBasisSurf->Bounds(u1, u2, v1, v2); - Standard_Integer NbUK, NbVK; - Standard_Boolean isToSkipSecond = Standard_False; + int NbUK, NbVK; + bool isToSkipSecond = false; if (myBasisSurf->IsKind(STANDARD_TYPE(Geom_BSplineSurface))) { Handle(Geom_BSplineSurface) BSur = Handle(Geom_BSplineSurface)::DownCast(myBasisSurf); @@ -351,16 +395,16 @@ Standard_Boolean Geom_OsculatingSurface::UOscSurf(const Standard_Real U TColStd_Array1OfReal VKnots(1, NbVK); BSur->UKnots(UKnots); BSur->VKnots(VKnots); - BSplCLib::Hunt(UKnots, U, NU); - BSplCLib::Hunt(VKnots, V, NV); + BSplCLib::Hunt(UKnots, theU, NU); + BSplCLib::Hunt(VKnots, theV, NV); if (NU < 1) NU = 1; if (NU >= NbUK) NU = NbUK - 1; if (NbVK == 2 && NV == 1) // Need to find the closest end - if (VKnots(NbVK) - V > V - VKnots(1)) - isToSkipSecond = Standard_True; + if (VKnots(NbVK) - theV > theV - VKnots(1)) + isToSkipSecond = true; } else { @@ -369,20 +413,20 @@ Standard_Boolean Geom_OsculatingSurface::UOscSurf(const Standard_Real U NbVK = 2; } - if (myAlong(1) && NV == 1) + if (myAlong[0] && NV == 1) { - L = *((Handle(Geom_BSplineSurface)*)&myOsculSurf1->Value(NU)); - along = Standard_True; + theL = myOsculSurf1.Value(NU); + along = true; } - if (myAlong(2) && (NV == NbVK - 1) && !isToSkipSecond) + if (myAlong[1] && (NV == NbVK - 1) && !isToSkipSecond) { - // t means that derivative vector of osculating surface is opposite + // theT means that derivative vector of osculating surface is opposite // to the original. This happens when (v-t)^k is negative, i.e. // difference between degrees (k) is odd and t is the last parameter - if (myKdeg->Value(NU) % 2) - t = Standard_True; - L = *((Handle(Geom_BSplineSurface)*)&myOsculSurf2->Value(NU)); - along = Standard_True; + if (myKdeg.Value(NU) % 2) + theT = true; + theL = myOsculSurf2.Value(NU); + along = true; } } return along; @@ -390,20 +434,20 @@ Standard_Boolean Geom_OsculatingSurface::UOscSurf(const Standard_Real U //================================================================================================= -Standard_Boolean Geom_OsculatingSurface::VOscSurf(const Standard_Real U, - const Standard_Real V, - Standard_Boolean& t, - Handle(Geom_BSplineSurface)& L) const +bool Geom_OsculatingSurface::VOscSurf(double theU, + double theV, + bool& theT, + Handle(Geom_BSplineSurface)& theL) const { - Standard_Boolean along = Standard_False; - if (myAlong(3) || myAlong(4)) + bool along = false; + if (myAlong[2] || myAlong[3]) { - Standard_Integer NU = 1, NV = 1; - Standard_Real u1, u2, v1, v2; - t = Standard_False; + int NU = 1, NV = 1; + double u1, u2, v1, v2; + theT = false; myBasisSurf->Bounds(u1, u2, v1, v2); - Standard_Integer NbUK, NbVK; - Standard_Boolean isToSkipSecond = Standard_False; + int NbUK, NbVK; + bool isToSkipSecond = false; if (myBasisSurf->IsKind(STANDARD_TYPE(Geom_BSplineSurface))) { Handle(Geom_BSplineSurface) BSur = Handle(Geom_BSplineSurface)::DownCast(myBasisSurf); @@ -413,16 +457,16 @@ Standard_Boolean Geom_OsculatingSurface::VOscSurf(const Standard_Real U TColStd_Array1OfReal VKnots(1, NbVK); BSur->UKnots(UKnots); BSur->VKnots(VKnots); - BSplCLib::Hunt(UKnots, U, NU); - BSplCLib::Hunt(VKnots, V, NV); + BSplCLib::Hunt(UKnots, theU, NU); + BSplCLib::Hunt(VKnots, theV, NV); if (NV < 1) NV = 1; if (NV >= NbVK) NV = NbVK - 1; if (NbUK == 2 && NU == 1) // Need to find the closest end - if (UKnots(NbUK) - U > U - UKnots(1)) - isToSkipSecond = Standard_True; + if (UKnots(NbUK) - theU > theU - UKnots(1)) + isToSkipSecond = true; } else { @@ -431,17 +475,17 @@ Standard_Boolean Geom_OsculatingSurface::VOscSurf(const Standard_Real U NbUK = 2; } - if (myAlong(3) && NU == 1) + if (myAlong[2] && NU == 1) { - L = *((Handle(Geom_BSplineSurface)*)&myOsculSurf1->Value(NV)); - along = Standard_True; + theL = myOsculSurf1.Value(NV); + along = true; } - if (myAlong(4) && (NU == NbUK - 1) && !isToSkipSecond) + if (myAlong[3] && (NU == NbUK - 1) && !isToSkipSecond) { - if (myKdeg->Value(NV) % 2) - t = Standard_True; - L = *((Handle(Geom_BSplineSurface)*)&myOsculSurf2->Value(NV)); - along = Standard_True; + if (myKdeg.Value(NV) % 2) + theT = true; + theL = myOsculSurf2.Value(NV); + along = true; } } return along; @@ -449,143 +493,141 @@ Standard_Boolean Geom_OsculatingSurface::VOscSurf(const Standard_Real U //================================================================================================= -Standard_Boolean Geom_OsculatingSurface::BuildOsculatingSurface( - const Standard_Real Param, - const Standard_Integer SUKnot, - const Standard_Integer SVKnot, - const Handle(Geom_BSplineSurface)& BS, - Handle(Geom_BSplineSurface)& BSpl) const +bool Geom_OsculatingSurface::buildOsculatingSurface(double theParam, + int theSUKnot, + int theSVKnot, + const Handle(Geom_BSplineSurface)& theBS, + Handle(Geom_BSplineSurface)& theBSpl) const { - Standard_Boolean OsculSurf = Standard_True; + bool OsculSurf = true; #ifdef OCCT_DEBUG - std::cout << "t = " << Param << std::endl; + std::cout << "t = " << theParam << std::endl; std::cout << "======================================" << std::endl << std::endl; #endif // for cache - Standard_Integer MinDegree, MaxDegree; - Standard_Real udeg, vdeg; - udeg = BS->UDegree(); - vdeg = BS->VDegree(); + int MinDegree, MaxDegree; + double udeg, vdeg; + udeg = theBS->UDegree(); + vdeg = theBS->VDegree(); if ((IsAlongU() && vdeg <= 1) || (IsAlongV() && udeg <= 1)) { #ifdef OCCT_DEBUG std::cout << " surface osculatrice nulle " << std::endl; #endif - // throw Standard_ConstructionError("Geom_OsculatingSurface"); - OsculSurf = Standard_False; + OsculSurf = false; } else { - MinDegree = (Standard_Integer)std::min(udeg, vdeg); - MaxDegree = (Standard_Integer)std::max(udeg, vdeg); + MinDegree = (int)std::min(udeg, vdeg); + MaxDegree = (int)std::max(udeg, vdeg); TColgp_Array2OfPnt cachepoles(1, MaxDegree + 1, 1, MinDegree + 1); // end for cache // for polynomial grid - Standard_Integer MaxUDegree, MaxVDegree; - Standard_Integer UContinuity, VContinuity; + int MaxUDegree, MaxVDegree; + int UContinuity, VContinuity; Handle(TColStd_HArray2OfInteger) NumCoeffPerSurface = new TColStd_HArray2OfInteger(1, 1, 1, 2); Handle(TColStd_HArray1OfReal) PolynomialUIntervals = new TColStd_HArray1OfReal(1, 2); Handle(TColStd_HArray1OfReal) PolynomialVIntervals = new TColStd_HArray1OfReal(1, 2); Handle(TColStd_HArray1OfReal) TrueUIntervals = new TColStd_HArray1OfReal(1, 2); Handle(TColStd_HArray1OfReal) TrueVIntervals = new TColStd_HArray1OfReal(1, 2); - MaxUDegree = (Standard_Integer)udeg; - MaxVDegree = (Standard_Integer)vdeg; + MaxUDegree = (int)udeg; + MaxVDegree = (int)vdeg; - for (Standard_Integer i = 1; i <= 2; i++) + for (int i = 1; i <= 2; i++) { PolynomialUIntervals->ChangeValue(i) = i - 1; PolynomialVIntervals->ChangeValue(i) = i - 1; - TrueUIntervals->ChangeValue(i) = BS->UKnot(SUKnot + i - 1); - TrueVIntervals->ChangeValue(i) = BS->VKnot(SVKnot + i - 1); + TrueUIntervals->ChangeValue(i) = theBS->UKnot(theSUKnot + i - 1); + TrueVIntervals->ChangeValue(i) = theBS->VKnot(theSVKnot + i - 1); } - Standard_Integer OscUNumCoeff = 0, OscVNumCoeff = 0; + int OscUNumCoeff = 0, OscVNumCoeff = 0; if (IsAlongU()) { #ifdef OCCT_DEBUG std::cout << ">>>>>>>>>>> AlongU" << std::endl; #endif - OscUNumCoeff = (Standard_Integer)udeg + 1; - OscVNumCoeff = (Standard_Integer)vdeg; + OscUNumCoeff = (int)udeg + 1; + OscVNumCoeff = (int)vdeg; } if (IsAlongV()) { #ifdef OCCT_DEBUG std::cout << ">>>>>>>>>>> AlongV" << std::endl; #endif - OscUNumCoeff = (Standard_Integer)udeg; - OscVNumCoeff = (Standard_Integer)vdeg + 1; + OscUNumCoeff = (int)udeg; + OscVNumCoeff = (int)vdeg + 1; } NumCoeffPerSurface->ChangeValue(1, 1) = OscUNumCoeff; NumCoeffPerSurface->ChangeValue(1, 2) = OscVNumCoeff; - Standard_Integer nbc = NumCoeffPerSurface->Value(1, 1) * NumCoeffPerSurface->Value(1, 2) * 3; + int nbc = NumCoeffPerSurface->Value(1, 1) * NumCoeffPerSurface->Value(1, 2) * 3; // if (nbc == 0) { - return Standard_False; + return false; } // Handle(TColStd_HArray1OfReal) Coefficients = new TColStd_HArray1OfReal(1, nbc); // end for polynomial grid // building the cache - Standard_Integer ULocalIndex, VLocalIndex; - Standard_Real ucacheparameter, vcacheparameter, uspanlength, vspanlength; - TColgp_Array2OfPnt NewPoles(1, BS->NbUPoles(), 1, BS->NbVPoles()); + int ULocalIndex, VLocalIndex; + double ucacheparameter, vcacheparameter, uspanlength, vspanlength; + TColgp_Array2OfPnt NewPoles(1, theBS->NbUPoles(), 1, theBS->NbVPoles()); - Standard_Integer aUfKnotsLength = BS->NbUPoles() + BS->UDegree() + 1; - Standard_Integer aVfKnotsLength = BS->NbVPoles() + BS->VDegree() + 1; + int aUfKnotsLength = theBS->NbUPoles() + theBS->UDegree() + 1; + int aVfKnotsLength = theBS->NbVPoles() + theBS->VDegree() + 1; - if (BS->IsUPeriodic()) + if (theBS->IsUPeriodic()) { - TColStd_Array1OfInteger aMults(1, BS->NbUKnots()); - BS->UMultiplicities(aMults); - aUfKnotsLength = BSplCLib::KnotSequenceLength(aMults, BS->UDegree(), Standard_True); + TColStd_Array1OfInteger aMults(1, theBS->NbUKnots()); + theBS->UMultiplicities(aMults); + aUfKnotsLength = BSplCLib::KnotSequenceLength(aMults, theBS->UDegree(), Standard_True); } - if (BS->IsVPeriodic()) + if (theBS->IsVPeriodic()) { - TColStd_Array1OfInteger aMults(1, BS->NbVKnots()); - BS->VMultiplicities(aMults); - aVfKnotsLength = BSplCLib::KnotSequenceLength(aMults, BS->VDegree(), Standard_True); + TColStd_Array1OfInteger aMults(1, theBS->NbVKnots()); + theBS->VMultiplicities(aMults); + aVfKnotsLength = BSplCLib::KnotSequenceLength(aMults, theBS->VDegree(), Standard_True); } TColStd_Array1OfReal UFlatKnots(1, aUfKnotsLength); TColStd_Array1OfReal VFlatKnots(1, aVfKnotsLength); - BS->Poles(NewPoles); - BS->UKnotSequence(UFlatKnots); - BS->VKnotSequence(VFlatKnots); + theBS->Poles(NewPoles); + theBS->UKnotSequence(UFlatKnots); + theBS->VKnotSequence(VFlatKnots); VLocalIndex = 0; ULocalIndex = 0; - ucacheparameter = BS->UKnot(SUKnot); - vcacheparameter = BS->VKnot(SVKnot); - vspanlength = BS->VKnot(SVKnot + 1) - BS->VKnot(SVKnot); - uspanlength = BS->UKnot(SUKnot + 1) - BS->UKnot(SUKnot); + ucacheparameter = theBS->UKnot(theSUKnot); + vcacheparameter = theBS->VKnot(theSVKnot); + vspanlength = theBS->VKnot(theSVKnot + 1) - theBS->VKnot(theSVKnot); + uspanlength = theBS->UKnot(theSUKnot + 1) - theBS->UKnot(theSUKnot); // On se ramene toujours a un parametrage tel que localement ce soit l'iso // u=0 ou v=0 qui soit degeneree - Standard_Boolean IsVNegative = Param > vcacheparameter + vspanlength / 2; - Standard_Boolean IsUNegative = Param > ucacheparameter + uspanlength / 2; + bool IsVNegative = theParam > vcacheparameter + vspanlength / 2; + bool IsUNegative = theParam > ucacheparameter + uspanlength / 2; - if (IsAlongU() && (Param > vcacheparameter + vspanlength / 2)) + if (IsAlongU() && (theParam > vcacheparameter + vspanlength / 2)) vcacheparameter = vcacheparameter + vspanlength; - if (IsAlongV() && (Param > ucacheparameter + uspanlength / 2)) + if (IsAlongV() && (theParam > ucacheparameter + uspanlength / 2)) ucacheparameter = ucacheparameter + uspanlength; BSplSLib::BuildCache(ucacheparameter, vcacheparameter, uspanlength, vspanlength, - BS->IsUPeriodic(), - BS->IsVPeriodic(), - BS->UDegree(), - BS->VDegree(), + theBS->IsUPeriodic(), + theBS->IsVPeriodic(), + theBS->UDegree(), + theBS->VDegree(), ULocalIndex, VLocalIndex, UFlatKnots, @@ -594,29 +636,29 @@ Standard_Boolean Geom_OsculatingSurface::BuildOsculatingSurface( BSplSLib::NoWeights(), cachepoles, BSplSLib::NoWeights()); - Standard_Integer m, n, index; + int m, n, index; TColgp_Array2OfPnt OscCoeff(1, OscUNumCoeff, 1, OscVNumCoeff); if (IsAlongU()) { if (udeg > vdeg) { - for (n = 1; n <= udeg + 1; n++) - for (m = 1; m <= vdeg; m++) + for (n = 1; n <= (int)udeg + 1; n++) + for (m = 1; m <= (int)vdeg; m++) OscCoeff(n, m) = cachepoles(n, m + 1); } else { - for (n = 1; n <= udeg + 1; n++) - for (m = 1; m <= vdeg; m++) + for (n = 1; n <= (int)udeg + 1; n++) + for (m = 1; m <= (int)vdeg; m++) OscCoeff(n, m) = cachepoles(m + 1, n); } if (IsVNegative) PLib::VTrimming(-1, 0, OscCoeff, PLib::NoWeights2()); index = 1; - for (n = 1; n <= udeg + 1; n++) - for (m = 1; m <= vdeg; m++) + for (n = 1; n <= (int)udeg + 1; n++) + for (m = 1; m <= (int)vdeg; m++) { Coefficients->ChangeValue(index++) = OscCoeff(n, m).X(); Coefficients->ChangeValue(index++) = OscCoeff(n, m).Y(); @@ -628,21 +670,21 @@ Standard_Boolean Geom_OsculatingSurface::BuildOsculatingSurface( { if (udeg > vdeg) { - for (n = 1; n <= udeg; n++) - for (m = 1; m <= vdeg + 1; m++) + for (n = 1; n <= (int)udeg; n++) + for (m = 1; m <= (int)vdeg + 1; m++) OscCoeff(n, m) = cachepoles(n + 1, m); } else { - for (n = 1; n <= udeg; n++) - for (m = 1; m <= vdeg + 1; m++) + for (n = 1; n <= (int)udeg; n++) + for (m = 1; m <= (int)vdeg + 1; m++) OscCoeff(n, m) = cachepoles(m, n + 1); } if (IsUNegative) PLib::UTrimming(-1, 0, OscCoeff, PLib::NoWeights2()); index = 1; - for (n = 1; n <= udeg; n++) - for (m = 1; m <= vdeg + 1; m++) + for (n = 1; n <= (int)udeg; n++) + for (m = 1; m <= (int)vdeg + 1; m++) { Coefficients->ChangeValue(index++) = OscCoeff(n, m).X(); Coefficients->ChangeValue(index++) = OscCoeff(n, m).Y(); @@ -670,54 +712,51 @@ Standard_Boolean Geom_OsculatingSurface::BuildOsculatingSurface( TrueUIntervals, TrueVIntervals); - // Handle(Geom_BSplineSurface) BSpl = - BSpl = new Geom_BSplineSurface(Data.Poles()->Array2(), - Data.UKnots()->Array1(), - Data.VKnots()->Array1(), - Data.UMultiplicities()->Array1(), - Data.VMultiplicities()->Array1(), - Data.UDegree(), - Data.VDegree(), - 0, - 0); + theBSpl = new Geom_BSplineSurface(Data.Poles()->Array2(), + Data.UKnots()->Array1(), + Data.VKnots()->Array1(), + Data.UMultiplicities()->Array1(), + Data.VMultiplicities()->Array1(), + Data.UDegree(), + Data.VDegree(), + 0, + 0); #ifdef OCCT_DEBUG std::cout << "^====================================^" << std::endl << std::endl; #endif - - // L=BSpl; } return OsculSurf; } //================================================================================================= -Standard_Boolean Geom_OsculatingSurface::IsQPunctual(const Handle(Geom_Surface)& S, - const Standard_Real Param, - const GeomAbs_IsoType IT, - const Standard_Real TolMin, - const Standard_Real TolMax) const +bool Geom_OsculatingSurface::isQPunctual(const Handle(Geom_Surface)& theS, + double theParam, + GeomAbs_IsoType theIT, + double theTolMin, + double theTolMax) const { - Standard_Real U1 = 0, U2 = 0, V1 = 0, V2 = 0, T; - Standard_Boolean Along = Standard_True; - S->Bounds(U1, U2, V1, V2); - gp_Vec D1U, D1V; - gp_Pnt P; - Standard_Real Step, D1NormMax; - if (IT == GeomAbs_IsoV) + double U1 = 0, U2 = 0, V1 = 0, V2 = 0, T; + bool Along = true; + theS->Bounds(U1, U2, V1, V2); + gp_Vec D1U, D1V; + gp_Pnt P; + double Step, D1NormMax; + if (theIT == GeomAbs_IsoV) { Step = (U2 - U1) / 10; D1NormMax = 0.; for (T = U1; T <= U2; T = T + Step) { - S->D1(T, Param, P, D1U, D1V); + theS->D1(T, theParam, P, D1U, D1V); D1NormMax = std::max(D1NormMax, D1U.Magnitude()); } #ifdef OCCT_DEBUG std::cout << " D1NormMax = " << D1NormMax << std::endl; #endif - if (D1NormMax > TolMax || D1NormMax < TolMin) - Along = Standard_False; + if (D1NormMax > theTolMax || D1NormMax < theTolMin) + Along = false; } else { @@ -725,78 +764,21 @@ Standard_Boolean Geom_OsculatingSurface::IsQPunctual(const Handle(Geom_Surface)& D1NormMax = 0.; for (T = V1; T <= V2; T = T + Step) { - S->D1(Param, T, P, D1U, D1V); + theS->D1(theParam, T, P, D1U, D1V); D1NormMax = std::max(D1NormMax, D1V.Magnitude()); } #ifdef OCCT_DEBUG std::cout << " D1NormMax = " << D1NormMax << std::endl; #endif - if (D1NormMax > TolMax || D1NormMax < TolMin) - Along = Standard_False; + if (D1NormMax > theTolMax || D1NormMax < theTolMin) + Along = false; } return Along; } //================================================================================================= -Standard_Boolean Geom_OsculatingSurface::HasOscSurf() const -{ - return (myAlong(1) || myAlong(2) || myAlong(3) || myAlong(4)); -} - -//================================================================================================= - -Standard_Boolean Geom_OsculatingSurface::IsAlongU() const -{ - return (myAlong(1) || myAlong(2)); -} - -//================================================================================================= - -Standard_Boolean Geom_OsculatingSurface::IsAlongV() const +void Geom_OsculatingSurface::clearOsculFlags() { - return (myAlong(3) || myAlong(4)); -} - -//================================================================================================= - -const Geom_SequenceOfBSplineSurface& Geom_OsculatingSurface::GetSeqOfL1() const -{ - return myOsculSurf1->Sequence(); -} - -//================================================================================================= - -const Geom_SequenceOfBSplineSurface& Geom_OsculatingSurface::GetSeqOfL2() const -{ - return myOsculSurf2->Sequence(); -} - -//================================================================================================= - -void Geom_OsculatingSurface::ClearOsculFlags() -{ - myAlong.SetValue(1, Standard_False); - myAlong.SetValue(2, Standard_False); - myAlong.SetValue(3, Standard_False); - myAlong.SetValue(4, Standard_False); -} - -//================================================================================================= - -void Geom_OsculatingSurface::DumpJson(Standard_OStream& theOStream, Standard_Integer theDepth) const -{ - OCCT_DUMP_TRANSIENT_CLASS_BEGIN(theOStream) - - OCCT_DUMP_FIELD_VALUES_DUMPED(theOStream, theDepth, myBasisSurf.get()) - OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, myTol) - - if (!myOsculSurf1.IsNull()) - OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, myOsculSurf1->Size()) - if (!myOsculSurf2.IsNull()) - OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, myOsculSurf2->Size()) - if (!myKdeg.IsNull()) - OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, myKdeg->Size()) - - OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, myAlong.Size()) + myAlong.fill(false); } diff --git a/src/ModelingData/TKG3d/Geom/Geom_OsculatingSurface.hxx b/src/ModelingData/TKG3d/Geom/Geom_OsculatingSurface.hxx deleted file mode 100644 index 45d4d55174..0000000000 --- a/src/ModelingData/TKG3d/Geom/Geom_OsculatingSurface.hxx +++ /dev/null @@ -1,112 +0,0 @@ -// Created on: 1998-05-05 -// Created by: Stepan MISHIN -// Copyright (c) 1998-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 _Geom_OsculatingSurface_HeaderFile -#define _Geom_OsculatingSurface_HeaderFile - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -class Geom_Surface; -class Geom_BSplineSurface; - -class Geom_OsculatingSurface; -DEFINE_STANDARD_HANDLE(Geom_OsculatingSurface, Standard_Transient) - -class Geom_OsculatingSurface : public Standard_Transient -{ -public: - DEFINE_STANDARD_ALLOC - - Standard_EXPORT Geom_OsculatingSurface(); - - //! detects if the surface has punctual U or V - //! isoparametric curve along on the bounds of the surface - //! relatively to the tolerance Tol and Builds the corresponding - //! osculating surfaces. - Standard_EXPORT Geom_OsculatingSurface(const Handle(Geom_Surface)& BS, const Standard_Real Tol); - - Standard_EXPORT void Init(const Handle(Geom_Surface)& BS, const Standard_Real Tol); - - Standard_EXPORT Handle(Geom_Surface) BasisSurface() const; - - Standard_EXPORT Standard_Real Tolerance() const; - - //! if Standard_True, L is the local osculating surface - //! along U at the point U,V. - Standard_EXPORT Standard_Boolean UOscSurf(const Standard_Real U, - const Standard_Real V, - Standard_Boolean& t, - Handle(Geom_BSplineSurface)& L) const; - - //! if Standard_True, L is the local osculating surface - //! along V at the point U,V. - Standard_EXPORT Standard_Boolean VOscSurf(const Standard_Real U, - const Standard_Real V, - Standard_Boolean& t, - Handle(Geom_BSplineSurface)& L) const; - - //! Dumps the content of me into the stream - Standard_EXPORT virtual void DumpJson(Standard_OStream& theOStream, - Standard_Integer theDepth = -1) const; - - DEFINE_STANDARD_RTTIEXT(Geom_OsculatingSurface, Standard_Transient) - -protected: -private: - //! returns False if the osculating surface can't be built - Standard_EXPORT Standard_Boolean BuildOsculatingSurface(const Standard_Real Param, - const Standard_Integer UKnot, - const Standard_Integer VKnot, - const Handle(Geom_BSplineSurface)& BS, - Handle(Geom_BSplineSurface)& L) const; - - //! returns True if the isoparametric is quasi-punctual - Standard_EXPORT Standard_Boolean IsQPunctual(const Handle(Geom_Surface)& S, - const Standard_Real Param, - const GeomAbs_IsoType IT, - const Standard_Real TolMin, - const Standard_Real TolMax) const; - - Standard_EXPORT Standard_Boolean HasOscSurf() const; - - Standard_EXPORT Standard_Boolean IsAlongU() const; - - Standard_EXPORT Standard_Boolean IsAlongV() const; - - Standard_EXPORT void ClearOsculFlags(); - - Standard_EXPORT const Geom_SequenceOfBSplineSurface& GetSeqOfL1() const; - - Standard_EXPORT const Geom_SequenceOfBSplineSurface& GetSeqOfL2() const; - - Handle(Geom_Surface) myBasisSurf; - Standard_Real myTol; - Handle(Geom_HSequenceOfBSplineSurface) myOsculSurf1; - Handle(Geom_HSequenceOfBSplineSurface) myOsculSurf2; - Handle(TColStd_HSequenceOfInteger) myKdeg; - TColStd_Array1OfBoolean myAlong; -}; - -#endif // _Geom_OsculatingSurface_HeaderFile diff --git a/src/ModelingData/TKG3d/Geom/Geom_OsculatingSurface.pxx b/src/ModelingData/TKG3d/Geom/Geom_OsculatingSurface.pxx new file mode 100644 index 0000000000..64910e84ec --- /dev/null +++ b/src/ModelingData/TKG3d/Geom/Geom_OsculatingSurface.pxx @@ -0,0 +1,126 @@ +// Created on: 1998-05-05 +// Created by: Stepan MISHIN +// Copyright (c) 1998-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 _Geom_OsculatingSurface_HeaderFile +#define _Geom_OsculatingSurface_HeaderFile + +#include +#include +#include +#include +#include +#include + +#include + +class Geom_Surface; +class Geom_BSplineSurface; + +//! Internal helper class for Geom_OffsetSurface. +//! Detects if the surface has punctual U or V isoparametric curve +//! along on the bounds of the surface relatively to the tolerance +//! and builds the corresponding osculating surfaces. +//! +//! This is a private implementation class - not for external use. +class Geom_OsculatingSurface +{ +public: + //! Default constructor. + Geom_OsculatingSurface(); + + //! Constructor that detects punctual isoparametric curves and builds + //! osculating surfaces. + //! @param theBS the basis surface to analyze + //! @param theTol tolerance for detecting punctual curves + Geom_OsculatingSurface(const Handle(Geom_Surface)& theBS, double theTol); + + //! Copy constructor. + Geom_OsculatingSurface(const Geom_OsculatingSurface& theOther); + + //! Move constructor. + Geom_OsculatingSurface(Geom_OsculatingSurface&& theOther) noexcept; + + //! Copy assignment. + Geom_OsculatingSurface& operator=(const Geom_OsculatingSurface& theOther); + + //! Move assignment. + Geom_OsculatingSurface& operator=(Geom_OsculatingSurface&& theOther) noexcept; + + //! Destructor. + ~Geom_OsculatingSurface() = default; + + //! Initialize with a surface and tolerance. + void Init(const Handle(Geom_Surface)& theBS, double theTol); + + //! Returns the basis surface. + const Handle(Geom_Surface)& BasisSurface() const { return myBasisSurf; } + + //! Returns the tolerance. + double Tolerance() const { return myTol; } + + //! Returns true if any osculating surface exists. + bool HasOscSurf() const { return myAlong[0] || myAlong[1] || myAlong[2] || myAlong[3]; } + + //! Returns true if osculating surface exists along U direction. + bool IsAlongU() const { return myAlong[0] || myAlong[1]; } + + //! Returns true if osculating surface exists along V direction. + bool IsAlongV() const { return myAlong[2] || myAlong[3]; } + + //! If true, theL is the local osculating surface along U at point (U,V). + //! @param theU U parameter + //! @param theV V parameter + //! @param theT output flag indicating if derivative is opposite + //! @param theL output osculating surface + //! @return true if osculating surface was found + bool UOscSurf(double theU, double theV, bool& theT, Handle(Geom_BSplineSurface)& theL) const; + + //! If true, theL is the local osculating surface along V at point (U,V). + //! @param theU U parameter + //! @param theV V parameter + //! @param theT output flag indicating if derivative is opposite + //! @param theL output osculating surface + //! @return true if osculating surface was found + bool VOscSurf(double theU, double theV, bool& theT, Handle(Geom_BSplineSurface)& theL) const; + +private: + //! Returns false if the osculating surface can't be built. + bool buildOsculatingSurface(double theParam, + int theUKnot, + int theVKnot, + const Handle(Geom_BSplineSurface)& theBS, + Handle(Geom_BSplineSurface)& theL) const; + + //! Returns true if the isoparametric is quasi-punctual. + bool isQPunctual(const Handle(Geom_Surface)& theS, + double theParam, + GeomAbs_IsoType theIT, + double theTolMin, + double theTolMax) const; + + //! Clear osculating flags. + void clearOsculFlags(); + +private: + Handle(Geom_Surface) myBasisSurf; + double myTol; + Geom_SequenceOfBSplineSurface myOsculSurf1; + Geom_SequenceOfBSplineSurface myOsculSurf2; + NCollection_Sequence myKdeg; + std::array myAlong; +}; + +#endif // _Geom_OsculatingSurface_HeaderFile diff --git a/src/ModelingData/TKG3d/Geom/Geom_RevolutionUtils.pxx b/src/ModelingData/TKG3d/Geom/Geom_RevolutionUtils.pxx new file mode 100644 index 0000000000..2c2fa7cc32 --- /dev/null +++ b/src/ModelingData/TKG3d/Geom/Geom_RevolutionUtils.pxx @@ -0,0 +1,272 @@ +// Copyright (c) 2025 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 _Geom_RevolutionUtils_HeaderFile +#define _Geom_RevolutionUtils_HeaderFile + +#include +#include +#include +#include +#include + +//! @file Geom_RevolutionUtils.pxx +//! @brief Shared utility functions for surface of revolution evaluation. +//! +//! This file provides template functions for evaluating points and derivatives +//! on surfaces of revolution. The functions are templated to work with both +//! Geom_Curve (for Geom_SurfaceOfRevolution) and Adaptor3d_Curve +//! (for GeomAdaptor_SurfaceOfRevolution). +//! +//! Revolution surface: P(U,V) = Rotation(Axis, U) * BasisCurve(V) +//! where U is the rotation angle and V is the parameter along the basis curve. + +namespace Geom_RevolutionUtils +{ + +//! Evaluates point on surface of revolution. +//! @tparam CurveType Type supporting D0(param, point) method +//! @param theU Rotation angle parameter +//! @param theV Parameter along the basis curve +//! @param theBasis Basis curve +//! @param theAxis Rotation axis +//! @param theP [out] Evaluated point +template +inline void D0(const double theU, + const double theV, + const CurveType& theBasis, + const gp_Ax1& theAxis, + gp_Pnt& theP) +{ + theBasis.D0(theV, theP); + + gp_Trsf aRotation; + aRotation.SetRotation(theAxis, theU); + theP.Transform(aRotation); +} + +//! Evaluates point and first derivatives on surface of revolution. +//! @tparam CurveType Type supporting D1(param, point, vec) method +//! @param theU Rotation angle parameter +//! @param theV Parameter along the basis curve +//! @param theBasis Basis curve +//! @param theAxis Rotation axis +//! @param theP [out] Evaluated point +//! @param theD1U [out] First derivative with respect to U (rotation) +//! @param theD1V [out] First derivative with respect to V (along curve) +template +inline void D1(const double theU, + const double theV, + const CurveType& theBasis, + const gp_Ax1& theAxis, + gp_Pnt& theP, + gp_Vec& theD1U, + gp_Vec& theD1V) +{ + theBasis.D1(theV, theP, theD1V); + + // Vector from center of rotation to the point on rotated curve + gp_XYZ aCQ = theP.XYZ() - theAxis.Location().XYZ(); + theD1U = gp_Vec(theAxis.Direction().XYZ().Crossed(aCQ)); + // If the point is placed on the axis of revolution then derivatives on U are undefined. + // Manually set them to zero. + if (theD1U.SquareMagnitude() < Precision::SquareConfusion()) + { + theD1U.SetCoord(0.0, 0.0, 0.0); + } + + gp_Trsf aRotation; + aRotation.SetRotation(theAxis, theU); + theP.Transform(aRotation); + theD1U.Transform(aRotation); + theD1V.Transform(aRotation); +} + +//! Evaluates point, first and second derivatives on surface of revolution. +//! @tparam CurveType Type supporting D2(param, point, vec, vec) method +//! @param theU Rotation angle parameter +//! @param theV Parameter along the basis curve +//! @param theBasis Basis curve +//! @param theAxis Rotation axis +//! @param theP [out] Evaluated point +//! @param theD1U [out] First derivative with respect to U +//! @param theD1V [out] First derivative with respect to V +//! @param theD2U [out] Second derivative with respect to U +//! @param theD2V [out] Second derivative with respect to V +//! @param theD2UV [out] Mixed second derivative +template +inline void D2(const double theU, + const double theV, + const CurveType& theBasis, + const gp_Ax1& theAxis, + gp_Pnt& theP, + gp_Vec& theD1U, + gp_Vec& theD1V, + gp_Vec& theD2U, + gp_Vec& theD2V, + gp_Vec& theD2UV) +{ + theBasis.D2(theV, theP, theD1V, theD2V); + + // Vector from center of rotation to the point on rotated curve + gp_XYZ aCQ = theP.XYZ() - theAxis.Location().XYZ(); + const gp_XYZ& aDir = theAxis.Direction().XYZ(); + theD1U = gp_Vec(aDir.Crossed(aCQ)); + // If the point is placed on the axis of revolution then derivatives on U are undefined. + // Manually set them to zero. + if (theD1U.SquareMagnitude() < Precision::SquareConfusion()) + { + theD1U.SetCoord(0.0, 0.0, 0.0); + } + theD2U = gp_Vec(aDir.Dot(aCQ) * aDir - aCQ); + theD2UV = gp_Vec(aDir.Crossed(theD1V.XYZ())); + + gp_Trsf aRotation; + aRotation.SetRotation(theAxis, theU); + theP.Transform(aRotation); + theD1U.Transform(aRotation); + theD1V.Transform(aRotation); + theD2U.Transform(aRotation); + theD2V.Transform(aRotation); + theD2UV.Transform(aRotation); +} + +//! Evaluates point and all derivatives up to third order on surface of revolution. +//! @tparam CurveType Type supporting D3(param, point, vec, vec, vec) method +//! @param theU Rotation angle parameter +//! @param theV Parameter along the basis curve +//! @param theBasis Basis curve +//! @param theAxis Rotation axis +//! @param theP [out] Evaluated point +//! @param theD1U [out] First derivative with respect to U +//! @param theD1V [out] First derivative with respect to V +//! @param theD2U [out] Second derivative with respect to U +//! @param theD2V [out] Second derivative with respect to V +//! @param theD2UV [out] Mixed second derivative +//! @param theD3U [out] Third derivative with respect to U +//! @param theD3V [out] Third derivative with respect to V +//! @param theD3UUV [out] Mixed third derivative (UUV) +//! @param theD3UVV [out] Mixed third derivative (UVV) +template +inline void D3(const double theU, + const double theV, + const CurveType& theBasis, + const gp_Ax1& theAxis, + gp_Pnt& theP, + gp_Vec& theD1U, + gp_Vec& theD1V, + gp_Vec& theD2U, + gp_Vec& theD2V, + gp_Vec& theD2UV, + gp_Vec& theD3U, + gp_Vec& theD3V, + gp_Vec& theD3UUV, + gp_Vec& theD3UVV) +{ + theBasis.D3(theV, theP, theD1V, theD2V, theD3V); + + // Vector from center of rotation to the point on rotated curve + gp_XYZ aCQ = theP.XYZ() - theAxis.Location().XYZ(); + const gp_XYZ& aDir = theAxis.Direction().XYZ(); + theD1U = gp_Vec(aDir.Crossed(aCQ)); + // If the point is placed on the axis of revolution then derivatives on U are undefined. + // Manually set them to zero. + if (theD1U.SquareMagnitude() < Precision::SquareConfusion()) + { + theD1U.SetCoord(0.0, 0.0, 0.0); + } + theD2U = gp_Vec(aDir.Dot(aCQ) * aDir - aCQ); + theD2UV = gp_Vec(aDir.Crossed(theD1V.XYZ())); + theD3U = -theD1U; + theD3UUV = gp_Vec(aDir.Dot(theD1V.XYZ()) * aDir - theD1V.XYZ()); + theD3UVV = gp_Vec(aDir.Crossed(theD2V.XYZ())); + + gp_Trsf aRotation; + aRotation.SetRotation(theAxis, theU); + theP.Transform(aRotation); + theD1U.Transform(aRotation); + theD1V.Transform(aRotation); + theD2U.Transform(aRotation); + theD2V.Transform(aRotation); + theD2UV.Transform(aRotation); + theD3U.Transform(aRotation); + theD3V.Transform(aRotation); + theD3UUV.Transform(aRotation); + theD3UVV.Transform(aRotation); +} + +//! Evaluates N-th derivative on surface of revolution. +//! @tparam CurveType Type supporting D0, DN methods +//! @param theU Rotation angle parameter +//! @param theV Parameter along the basis curve +//! @param theBasis Basis curve +//! @param theAxis Rotation axis +//! @param theDerU Derivative order with respect to U +//! @param theDerV Derivative order with respect to V +//! @return The derivative vector +template +inline gp_Vec DN(const double theU, + const double theV, + const CurveType& theBasis, + const gp_Ax1& theAxis, + const int theDerU, + const int theDerV) +{ + gp_Trsf aRotation; + aRotation.SetRotation(theAxis, theU); + + gp_Pnt aP; + gp_Vec aDV; + gp_Vec aResult; + if (theDerU == 0) + { + aResult = theBasis.DN(theV, theDerV); + } + else + { + if (theDerV == 0) + { + theBasis.D0(theV, aP); + aDV = gp_Vec(aP.XYZ() - theAxis.Location().XYZ()); + } + else + { + aDV = theBasis.DN(theV, theDerV); + } + + const gp_XYZ& aDir = theAxis.Direction().XYZ(); + if (theDerU % 4 == 1) + { + aResult = gp_Vec(aDir.Crossed(aDV.XYZ())); + } + else if (theDerU % 4 == 2) + { + aResult = gp_Vec(aDir.Dot(aDV.XYZ()) * aDir - aDV.XYZ()); + } + else if (theDerU % 4 == 3) + { + aResult = gp_Vec(aDir.Crossed(aDV.XYZ())) * (-1.0); + } + else + { + aResult = gp_Vec(aDV.XYZ() - aDir.Dot(aDV.XYZ()) * aDir); + } + } + + aResult.Transform(aRotation); + return aResult; +} + +} // namespace Geom_RevolutionUtils + +#endif // _Geom_RevolutionUtils_HeaderFile diff --git a/src/ModelingData/TKG3d/Geom/Geom_SurfaceOfLinearExtrusion.cxx b/src/ModelingData/TKG3d/Geom/Geom_SurfaceOfLinearExtrusion.cxx index 017538bd70..d552158157 100644 --- a/src/ModelingData/TKG3d/Geom/Geom_SurfaceOfLinearExtrusion.cxx +++ b/src/ModelingData/TKG3d/Geom/Geom_SurfaceOfLinearExtrusion.cxx @@ -18,11 +18,11 @@ #include #include #include +#include "Geom_ExtrusionUtils.pxx" #include #include #include #include -#include #include #include #include @@ -68,11 +68,9 @@ Handle(Geom_Geometry) Geom_SurfaceOfLinearExtrusion::Copy() const Geom_SurfaceOfLinearExtrusion::Geom_SurfaceOfLinearExtrusion(const Handle(Geom_Curve)& C, const Dir& V) { - - basisCurve = Handle(Geom_Curve)::DownCast(C->Copy()); // Copy 10-03-93 - direction = V; - smooth = C->Continuity(); - myEvaluator = new GeomEvaluator_SurfaceOfExtrusion(basisCurve, direction); + basisCurve = Handle(Geom_Curve)::DownCast(C->Copy()); + direction = V; + smooth = C->Continuity(); } //================================================================================================= @@ -95,9 +93,7 @@ Standard_Real Geom_SurfaceOfLinearExtrusion::UReversedParameter(const Standard_R void Geom_SurfaceOfLinearExtrusion::VReverse() { - direction.Reverse(); - myEvaluator->SetDirection(direction); } //================================================================================================= @@ -113,17 +109,14 @@ Standard_Real Geom_SurfaceOfLinearExtrusion::VReversedParameter(const Standard_R void Geom_SurfaceOfLinearExtrusion::SetDirection(const Dir& V) { direction = V; - myEvaluator->SetDirection(direction); } //================================================================================================= void Geom_SurfaceOfLinearExtrusion::SetBasisCurve(const Handle(Geom_Curve)& C) { - - smooth = C->Continuity(); - basisCurve = Handle(Geom_Curve)::DownCast(C->Copy()); // Copy 10-03-93 - myEvaluator = new GeomEvaluator_SurfaceOfExtrusion(basisCurve, direction); + smooth = C->Continuity(); + basisCurve = Handle(Geom_Curve)::DownCast(C->Copy()); } //================================================================================================= @@ -144,7 +137,7 @@ void Geom_SurfaceOfLinearExtrusion::Bounds(Standard_Real& U1, void Geom_SurfaceOfLinearExtrusion::D0(const Standard_Real U, const Standard_Real V, Pnt& P) const { - myEvaluator->D0(U, V, P); + Geom_ExtrusionUtils::D0(U, V, *basisCurve, direction.XYZ(), P); } //================================================================================================= @@ -155,7 +148,7 @@ void Geom_SurfaceOfLinearExtrusion::D1(const Standard_Real U, Vec& D1U, Vec& D1V) const { - myEvaluator->D1(U, V, P, D1U, D1V); + Geom_ExtrusionUtils::D1(U, V, *basisCurve, direction.XYZ(), P, D1U, D1V); } //================================================================================================= @@ -169,7 +162,7 @@ void Geom_SurfaceOfLinearExtrusion::D2(const Standard_Real U, Vec& D2V, Vec& D2UV) const { - myEvaluator->D2(U, V, P, D1U, D1V, D2U, D2V, D2UV); + Geom_ExtrusionUtils::D2(U, V, *basisCurve, direction.XYZ(), P, D1U, D1V, D2U, D2V, D2UV); } //================================================================================================= @@ -187,17 +180,19 @@ void Geom_SurfaceOfLinearExtrusion::D3(const Standard_Real U, Vec& D3UUV, Vec& D3UVV) const { - myEvaluator->D3(U, V, P, D1U, D1V, D2U, D2V, D2UV, D3U, D3V, D3UUV, D3UVV); + Geom_ExtrusionUtils:: + D3(U, V, *basisCurve, direction.XYZ(), P, D1U, D1V, D2U, D2V, D2UV, D3U, D3V, D3UUV, D3UVV); } //================================================================================================= -Vec Geom_SurfaceOfLinearExtrusion::DN(const Standard_Real U, - const Standard_Real V, +Vec Geom_SurfaceOfLinearExtrusion::DN(const Standard_Real U, + const Standard_Real, const Standard_Integer Nu, const Standard_Integer Nv) const { - return myEvaluator->DN(U, V, Nu, Nv); + Standard_RangeError_Raise_if(Nu + Nv < 1 || Nu < 0 || Nv < 0, " "); + return Geom_ExtrusionUtils::DN(U, *basisCurve, direction.XYZ(), Nu, Nv); } //================================================================================================= @@ -243,10 +238,8 @@ Standard_Boolean Geom_SurfaceOfLinearExtrusion::IsCNv(const Standard_Integer) co void Geom_SurfaceOfLinearExtrusion::Transform(const Trsf& T) { - direction.Transform(T); basisCurve->Transform(T); - myEvaluator->SetDirection(direction); } //================================================================================================= diff --git a/src/ModelingData/TKG3d/Geom/Geom_SurfaceOfLinearExtrusion.hxx b/src/ModelingData/TKG3d/Geom/Geom_SurfaceOfLinearExtrusion.hxx index bbbca71882..46c9e649ae 100644 --- a/src/ModelingData/TKG3d/Geom/Geom_SurfaceOfLinearExtrusion.hxx +++ b/src/ModelingData/TKG3d/Geom/Geom_SurfaceOfLinearExtrusion.hxx @@ -21,7 +21,6 @@ #include #include -#include #include class Geom_Curve; class gp_Dir; @@ -275,10 +274,6 @@ public: Standard_Integer theDepth = -1) const Standard_OVERRIDE; DEFINE_STANDARD_RTTIEXT(Geom_SurfaceOfLinearExtrusion, Geom_SweptSurface) - -protected: -private: - Handle(GeomEvaluator_SurfaceOfExtrusion) myEvaluator; }; #endif // _Geom_SurfaceOfLinearExtrusion_HeaderFile diff --git a/src/ModelingData/TKG3d/Geom/Geom_SurfaceOfRevolution.cxx b/src/ModelingData/TKG3d/Geom/Geom_SurfaceOfRevolution.cxx index 02b09a0832..223d8554e6 100644 --- a/src/ModelingData/TKG3d/Geom/Geom_SurfaceOfRevolution.cxx +++ b/src/ModelingData/TKG3d/Geom/Geom_SurfaceOfRevolution.cxx @@ -20,9 +20,9 @@ #include #include #include +#include "Geom_RevolutionUtils.pxx" #include #include -#include #include #include #include @@ -73,7 +73,6 @@ Handle(Geom_Geometry) Geom_SurfaceOfRevolution::Copy() const Geom_SurfaceOfRevolution::Geom_SurfaceOfRevolution(const Handle(Geom_Curve)& C, const Ax1& A1) : loc(A1.Location()) { - direction = A1.Direction(); SetBasisCurve(C); } @@ -82,9 +81,7 @@ Geom_SurfaceOfRevolution::Geom_SurfaceOfRevolution(const Handle(Geom_Curve)& C, void Geom_SurfaceOfRevolution::UReverse() { - direction.Reverse(); - myEvaluator->SetDirection(direction); } //================================================================================================= @@ -179,38 +176,30 @@ Standard_Boolean Geom_SurfaceOfRevolution::IsVPeriodic() const void Geom_SurfaceOfRevolution::SetAxis(const Ax1& A1) { - direction = A1.Direction(); loc = A1.Location(); - myEvaluator->SetAxis(A1); } //================================================================================================= void Geom_SurfaceOfRevolution::SetDirection(const Dir& V) { - direction = V; - myEvaluator->SetDirection(direction); } //================================================================================================= void Geom_SurfaceOfRevolution::SetBasisCurve(const Handle(Geom_Curve)& C) { - - basisCurve = Handle(Geom_Curve)::DownCast(C->Copy()); - smooth = C->Continuity(); - myEvaluator = new GeomEvaluator_SurfaceOfRevolution(basisCurve, direction, loc); + basisCurve = Handle(Geom_Curve)::DownCast(C->Copy()); + smooth = C->Continuity(); } //================================================================================================= void Geom_SurfaceOfRevolution::SetLocation(const Pnt& P) { - loc = P; - myEvaluator->SetLocation(loc); } //================================================================================================= @@ -231,7 +220,7 @@ void Geom_SurfaceOfRevolution::Bounds(Standard_Real& U1, void Geom_SurfaceOfRevolution::D0(const Standard_Real U, const Standard_Real V, Pnt& P) const { - myEvaluator->D0(U, V, P); + Geom_RevolutionUtils::D0(U, V, *basisCurve, loc.XYZ(), direction.XYZ(), P); } //================================================================================================= @@ -242,7 +231,7 @@ void Geom_SurfaceOfRevolution::D1(const Standard_Real U, Vec& D1U, Vec& D1V) const { - myEvaluator->D1(U, V, P, D1U, D1V); + Geom_RevolutionUtils::D1(U, V, *basisCurve, loc.XYZ(), direction.XYZ(), P, D1U, D1V); } //================================================================================================= @@ -256,7 +245,17 @@ void Geom_SurfaceOfRevolution::D2(const Standard_Real U, Vec& D2V, Vec& D2UV) const { - myEvaluator->D2(U, V, P, D1U, D1V, D2U, D2V, D2UV); + Geom_RevolutionUtils::D2(U, + V, + *basisCurve, + loc.XYZ(), + direction.XYZ(), + P, + D1U, + D1V, + D2U, + D2V, + D2UV); } //================================================================================================= @@ -274,7 +273,21 @@ void Geom_SurfaceOfRevolution::D3(const Standard_Real U, Vec& D3UUV, Vec& D3UVV) const { - myEvaluator->D3(U, V, P, D1U, D1V, D2U, D2V, D2UV, D3U, D3V, D3UUV, D3UVV); + Geom_RevolutionUtils::D3(U, + V, + *basisCurve, + loc.XYZ(), + direction.XYZ(), + P, + D1U, + D1V, + D2U, + D2V, + D2UV, + D3U, + D3V, + D3UUV, + D3UVV); } //================================================================================================= @@ -284,7 +297,8 @@ Vec Geom_SurfaceOfRevolution::DN(const Standard_Real U, const Standard_Integer Nu, const Standard_Integer Nv) const { - return myEvaluator->DN(U, V, Nu, Nv); + Standard_RangeError_Raise_if(Nu + Nv < 1 || Nu < 0 || Nv < 0, " "); + return Geom_RevolutionUtils::DN(U, V, *basisCurve, loc.XYZ(), direction.XYZ(), Nu, Nv); } //================================================================================================= @@ -342,14 +356,11 @@ Handle(Geom_Curve) Geom_SurfaceOfRevolution::VIso(const Standard_Real V) const void Geom_SurfaceOfRevolution::Transform(const Trsf& T) { - loc.Transform(T); direction.Transform(T); basisCurve->Transform(T); if (T.ScaleFactor() * T.HVectorialPart().Determinant() < 0.) UReverse(); - myEvaluator->SetDirection(direction); - myEvaluator->SetLocation(loc); } //================================================================================================= diff --git a/src/ModelingData/TKG3d/Geom/Geom_SurfaceOfRevolution.hxx b/src/ModelingData/TKG3d/Geom/Geom_SurfaceOfRevolution.hxx index 32a694642d..eebb1445a5 100644 --- a/src/ModelingData/TKG3d/Geom/Geom_SurfaceOfRevolution.hxx +++ b/src/ModelingData/TKG3d/Geom/Geom_SurfaceOfRevolution.hxx @@ -20,10 +20,10 @@ #include #include -#include #include -#include +#include #include + class Geom_Curve; class gp_Ax1; class gp_Dir; @@ -329,8 +329,7 @@ public: DEFINE_STANDARD_RTTIEXT(Geom_SurfaceOfRevolution, Geom_SweptSurface) private: - Handle(GeomEvaluator_SurfaceOfRevolution) myEvaluator; - gp_Pnt loc; + gp_Pnt loc; }; #endif // _Geom_SurfaceOfRevolution_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor.cxx b/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor.cxx index 8a2fe56b1d..bcc9819d11 100644 --- a/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor.cxx +++ b/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor.cxx @@ -17,9 +17,12 @@ #include #include +#include +#include #include #include #include +#include #include #include #include diff --git a/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_Curve.cxx b/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_Curve.cxx index 0ea5b5300e..9d164f791c 100644 --- a/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_Curve.cxx +++ b/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_Curve.cxx @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include @@ -53,6 +52,8 @@ #include #include +#include "../Geom/Geom_OffsetCurveUtils.pxx" + // #include static const Standard_Real PosTol = Precision::PConfusion() / 2; @@ -69,9 +70,16 @@ Handle(Adaptor3d_Curve) GeomAdaptor_Curve::ShallowCopy() const aCopy->myFirst = myFirst; aCopy->myLast = myLast; aCopy->myBSplineCurve = myBSplineCurve; - if (!myNestedEvaluator.IsNull()) + + // Handle offset curve data - create shallow copy of basis adaptor + if (const auto* anOffsetData = std::get_if(&myCurveData)) { - aCopy->myNestedEvaluator = myNestedEvaluator->ShallowCopy(); + GeomAdaptor_OffsetCurveData aCopyData; + aCopyData.BasisAdaptor = + Handle(GeomAdaptor_Curve)::DownCast(anOffsetData->BasisAdaptor->ShallowCopy()); + aCopyData.Offset = anOffsetData->Offset; + aCopyData.Direction = anOffsetData->Direction; + aCopy->myCurveData = aCopyData; } return aCopy; @@ -168,9 +176,9 @@ void GeomAdaptor_Curve::Reset() { myTypeCurve = GeomAbs_OtherCurve; myCurve.Nullify(); - myNestedEvaluator.Nullify(); myBSplineCurve.Nullify(); myCurveCache.Nullify(); + myCurveData = std::monostate{}; myFirst = myLast = 0.0; } @@ -187,8 +195,8 @@ void GeomAdaptor_Curve::load(const Handle(Geom_Curve)& C, if (myCurve != C) { myCurve = C; - myNestedEvaluator.Nullify(); myBSplineCurve.Nullify(); + myCurveData = std::monostate{}; const Handle(Standard_Type)& TheType = C->DynamicType(); if (TheType == STANDARD_TYPE(Geom_TrimmedCurve)) @@ -226,14 +234,13 @@ void GeomAdaptor_Curve::load(const Handle(Geom_Curve)& C, } else if (TheType == STANDARD_TYPE(Geom_OffsetCurve)) { - myTypeCurve = GeomAbs_OffsetCurve; - Handle(Geom_OffsetCurve) anOffsetCurve = Handle(Geom_OffsetCurve)::DownCast(myCurve); - // Create nested adaptor for base curve - Handle(Geom_Curve) aBaseCurve = anOffsetCurve->BasisCurve(); - Handle(GeomAdaptor_Curve) aBaseAdaptor = new GeomAdaptor_Curve(aBaseCurve); - myNestedEvaluator = new GeomEvaluator_OffsetCurve(aBaseAdaptor, - anOffsetCurve->Offset(), - anOffsetCurve->Direction()); + myTypeCurve = GeomAbs_OffsetCurve; + Handle(Geom_OffsetCurve) anOffsetCurve = Handle(Geom_OffsetCurve)::DownCast(C); + GeomAdaptor_OffsetCurveData anOffsetData; + anOffsetData.BasisAdaptor = new GeomAdaptor_Curve(anOffsetCurve->BasisCurve()); + anOffsetData.Offset = anOffsetCurve->Offset(); + anOffsetData.Direction = anOffsetCurve->Direction(); + myCurveData = anOffsetData; } else { @@ -599,9 +606,18 @@ void GeomAdaptor_Curve::D0(const Standard_Real U, gp_Pnt& P) const break; } - case GeomAbs_OffsetCurve: - myNestedEvaluator->D0(U, P); + case GeomAbs_OffsetCurve: { + const auto& anOffsetData = std::get(myCurveData); + if (!Geom_OffsetCurveUtils::EvaluateD0(U, + anOffsetData.BasisAdaptor.get(), + anOffsetData.Direction, + anOffsetData.Offset, + P)) + { + throw Standard_NullValue("GeomAdaptor_Curve::D0: Unable to calculate offset point"); + } break; + } default: myCurve->D0(U, P); @@ -631,9 +647,19 @@ void GeomAdaptor_Curve::D1(const Standard_Real U, gp_Pnt& P, gp_Vec& V) const break; } - case GeomAbs_OffsetCurve: - myNestedEvaluator->D1(U, P, V); + case GeomAbs_OffsetCurve: { + const auto& anOffsetData = std::get(myCurveData); + if (!Geom_OffsetCurveUtils::EvaluateD1(U, + anOffsetData.BasisAdaptor.get(), + anOffsetData.Direction, + anOffsetData.Offset, + P, + V)) + { + throw Standard_NullValue("GeomAdaptor_Curve::D1: Unable to calculate offset D1"); + } break; + } default: myCurve->D1(U, P, V); @@ -663,9 +689,20 @@ void GeomAdaptor_Curve::D2(const Standard_Real U, gp_Pnt& P, gp_Vec& V1, gp_Vec& break; } - case GeomAbs_OffsetCurve: - myNestedEvaluator->D2(U, P, V1, V2); + case GeomAbs_OffsetCurve: { + const auto& anOffsetData = std::get(myCurveData); + if (!Geom_OffsetCurveUtils::EvaluateD2(U, + anOffsetData.BasisAdaptor.get(), + anOffsetData.Direction, + anOffsetData.Offset, + P, + V1, + V2)) + { + throw Standard_NullValue("GeomAdaptor_Curve::D2: Unable to calculate offset D2"); + } break; + } default: myCurve->D2(U, P, V1, V2); @@ -699,9 +736,21 @@ void GeomAdaptor_Curve::D3(const Standard_Real U, break; } - case GeomAbs_OffsetCurve: - myNestedEvaluator->D3(U, P, V1, V2, V3); + case GeomAbs_OffsetCurve: { + const auto& anOffsetData = std::get(myCurveData); + if (!Geom_OffsetCurveUtils::EvaluateD3(U, + anOffsetData.BasisAdaptor.get(), + anOffsetData.Direction, + anOffsetData.Offset, + P, + V1, + V2, + V3)) + { + throw Standard_NullValue("GeomAdaptor_Curve::D3: Unable to calculate offset D3"); + } break; + } default: myCurve->D3(U, P, V1, V2, V3); @@ -725,9 +774,25 @@ gp_Vec GeomAdaptor_Curve::DN(const Standard_Real U, const Standard_Integer N) co return myCurve->DN(U, N); } - case GeomAbs_OffsetCurve: - return myNestedEvaluator->DN(U, N); - break; + case GeomAbs_OffsetCurve: { + const auto& anOffsetData = std::get(myCurveData); + gp_Vec aDN; + if (!Geom_OffsetCurveUtils::EvaluateDN(U, + anOffsetData.BasisAdaptor.get(), + anOffsetData.Direction, + anOffsetData.Offset, + N, + aDN)) + { + if (N > 3) + { + throw Standard_NotImplemented( + "GeomAdaptor_Curve::DN: Derivative order > 3 not supported"); + } + throw Standard_NullValue("GeomAdaptor_Curve::DN: Unable to calculate offset DN"); + } + return aDN; + } default: // to eliminate gcc warning break; diff --git a/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_Curve.hxx b/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_Curve.hxx index efc455c536..8e464ec792 100644 --- a/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_Curve.hxx +++ b/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_Curve.hxx @@ -21,13 +21,26 @@ #include #include #include -#include +#include #include #include #include +#include + DEFINE_STANDARD_HANDLE(GeomAdaptor_Curve, Adaptor3d_Curve) +//! Internal structure for offset curve evaluation data. +struct GeomAdaptor_OffsetCurveData +{ + Handle(GeomAdaptor_Curve) BasisAdaptor; //!< Adaptor for basis curve + double Offset; //!< Offset distance + gp_Dir Direction; //!< Offset direction +}; + +//! Variant type for curve-specific evaluation data. +using GeomAdaptor_CurveDataVariant = std::variant; + //! This class provides an interface between the services provided by any //! curve from the package Geom and those required of the curve by algorithms which use it. //! Creation of the loaded curve the curve is C1 by piece. @@ -250,9 +263,9 @@ private: Standard_Real myFirst; Standard_Real myLast; - Handle(Geom_BSplineCurve) myBSplineCurve; ///< B-spline representation to prevent castings - mutable Handle(BSplCLib_Cache) myCurveCache; ///< Cached data for B-spline or Bezier curve - Handle(GeomEvaluator_Curve) myNestedEvaluator; ///< Calculates value of offset curve + Handle(Geom_BSplineCurve) myBSplineCurve; ///< B-spline representation to prevent castings + mutable Handle(BSplCLib_Cache) myCurveCache; ///< Cached data for B-spline or Bezier curve + GeomAdaptor_CurveDataVariant myCurveData; ///< Curve-specific evaluation data (offset, etc.) }; #endif // _GeomAdaptor_Curve_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_Surface.cxx b/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_Surface.cxx index e944a3ef37..f201c81765 100644 --- a/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_Surface.cxx +++ b/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_Surface.cxx @@ -23,10 +23,17 @@ #include +#include "../Geom/Geom_ExtrusionUtils.pxx" +#include "../Geom/Geom_OffsetSurfaceUtils.pxx" +#include "../Geom/Geom_OsculatingSurface.pxx" +#include "../Geom/Geom_RevolutionUtils.pxx" + #include #include #include #include +#include +#include #include #include #include @@ -40,10 +47,8 @@ #include #include #include +#include #include -#include -#include -#include #include #include #include @@ -52,11 +57,14 @@ #include #include #include +#include #include #include #include #include #include +#include +#include #include #include @@ -66,6 +74,49 @@ IMPLEMENT_STANDARD_RTTIEXT(GeomAdaptor_Surface, Adaptor3d_Surface) //================================================================================================= +void GeomAdaptor_Surface::OsculatingSurfaceDeleter::operator()(Geom_OsculatingSurface* thePtr) const +{ + delete thePtr; +} + +//================================================================================================= + +GeomAdaptor_Surface::OffsetData::OffsetData(const OffsetData& theOther) + : BasisAdaptor(theOther.BasisAdaptor), + Offset(theOther.Offset) +{ + if (theOther.OsculatingSurface) + { + OsculatingSurface.reset(new Geom_OsculatingSurface(*theOther.OsculatingSurface)); + } +} + +//================================================================================================= + +GeomAdaptor_Surface::OffsetData& GeomAdaptor_Surface::OffsetData::operator=( + const OffsetData& theOther) +{ + if (this != &theOther) + { + BasisAdaptor = theOther.BasisAdaptor; + Offset = theOther.Offset; + if (theOther.OsculatingSurface) + { + OsculatingSurface.reset(new Geom_OsculatingSurface(*theOther.OsculatingSurface)); + } + else + { + OsculatingSurface.reset(); + } + } + return *this; +} + +namespace +{ + +//================================================================================================= + GeomAbs_Shape LocalContinuity(Standard_Integer Degree, Standard_Integer Nb, TColStd_Array1OfReal& TK, @@ -113,25 +164,191 @@ GeomAbs_Shape LocalContinuity(Standard_Integer Degree, return GeomAbs_CN; } +//! Offset surface D0 evaluation with retry mechanism for singular points. +//! Uses template utility that shifts point towards center when normal calculation fails. +inline void offsetD0(const double theU, + const double theV, + const GeomAdaptor_Surface::OffsetData& theData, + gp_Pnt& theValue) +{ + const auto& anOscQuery = theData.OsculatingSurface; + if (!Geom_OffsetSurfaceUtils::EvaluateD0(theU, + theV, + theData.BasisAdaptor, + theData.Offset, + anOscQuery, + theValue)) + { + throw Standard_NumericError("GeomAdaptor_Surface: Unable to calculate offset D0"); + } +} + +//! Offset surface D1 evaluation with retry mechanism for singular points. +//! Uses template utility that shifts point towards center when normal calculation fails. +inline void offsetD1(const double theU, + const double theV, + const GeomAdaptor_Surface::OffsetData& theData, + gp_Pnt& theValue, + gp_Vec& theD1U, + gp_Vec& theD1V) +{ + const auto& anOscQuery = theData.OsculatingSurface; + if (!Geom_OffsetSurfaceUtils::EvaluateD1(theU, + theV, + theData.BasisAdaptor, + theData.Offset, + anOscQuery, + theValue, + theD1U, + theD1V)) + { + throw Standard_NumericError("GeomAdaptor_Surface: Unable to calculate offset D1"); + } +} + +//! Offset surface D2 evaluation with retry mechanism for singular points. +//! Uses template utility that shifts point towards center when normal calculation fails. +inline void offsetD2(const double theU, + const double theV, + const GeomAdaptor_Surface::OffsetData& theData, + gp_Pnt& theValue, + gp_Vec& theD1U, + gp_Vec& theD1V, + gp_Vec& theD2U, + gp_Vec& theD2V, + gp_Vec& theD2UV) +{ + const auto& anOscQuery = theData.OsculatingSurface; + if (!Geom_OffsetSurfaceUtils::EvaluateD2(theU, + theV, + theData.BasisAdaptor, + theData.Offset, + anOscQuery, + theValue, + theD1U, + theD1V, + theD2U, + theD2V, + theD2UV)) + { + throw Standard_NumericError("GeomAdaptor_Surface: Unable to calculate offset D2"); + } +} + +//! Offset surface D3 evaluation with retry mechanism for singular points. +//! Uses template utility that shifts point towards center when normal calculation fails. +inline void offsetD3(const double theU, + const double theV, + const GeomAdaptor_Surface::OffsetData& theData, + gp_Pnt& theValue, + gp_Vec& theD1U, + gp_Vec& theD1V, + gp_Vec& theD2U, + gp_Vec& theD2V, + gp_Vec& theD2UV, + gp_Vec& theD3U, + gp_Vec& theD3V, + gp_Vec& theD3UUV, + gp_Vec& theD3UVV) +{ + const auto& anOscQuery = theData.OsculatingSurface; + if (!Geom_OffsetSurfaceUtils::EvaluateD3(theU, + theV, + theData.BasisAdaptor, + theData.Offset, + anOscQuery, + theValue, + theD1U, + theD1V, + theD2U, + theD2V, + theD2UV, + theD3U, + theD3V, + theD3UUV, + theD3UVV)) + { + throw Standard_NumericError("GeomAdaptor_Surface: Unable to calculate offset D3"); + } +} + +//! Offset surface DN evaluation +inline gp_Vec offsetDN(const double theU, + const double theV, + const GeomAdaptor_Surface::OffsetData& theData, + int theNu, + int theNv) +{ + gp_Vec aResult; + if (!Geom_OffsetSurfaceUtils::EvaluateDN(theU, + theV, + theNu, + theNv, + theData.BasisAdaptor.get(), + theData.Offset, + theData.OsculatingSurface.get(), + aResult)) + { + throw Standard_NumericError("GeomAdaptor_Surface: Unable to calculate offset DN"); + } + return aResult; +} + +} // end of anonymous namespace + //================================================================================================= Handle(Adaptor3d_Surface) GeomAdaptor_Surface::ShallowCopy() const { Handle(GeomAdaptor_Surface) aCopy = new GeomAdaptor_Surface(); - aCopy->mySurface = mySurface; - aCopy->myUFirst = myUFirst; - aCopy->myULast = myULast; - aCopy->myVFirst = myVFirst; - aCopy->myVLast = myVLast; - aCopy->myTolU = myTolU; - aCopy->myTolV = myTolV; - aCopy->myBSplineSurface = myBSplineSurface; - + aCopy->mySurface = mySurface; + aCopy->myUFirst = myUFirst; + aCopy->myULast = myULast; + aCopy->myVFirst = myVFirst; + aCopy->myVLast = myVLast; + aCopy->myTolU = myTolU; + aCopy->myTolV = myTolV; aCopy->mySurfaceType = mySurfaceType; - if (!myNestedEvaluator.IsNull()) + + // Copy surface-specific evaluation data + if (auto* anExtrusionData = std::get_if(&mySurfaceData)) + { + GeomAdaptor_Surface::ExtrusionData aNewData; + aNewData.BasisCurve = + Handle(GeomAdaptor_Curve)::DownCast(anExtrusionData->BasisCurve->ShallowCopy()); + aNewData.Direction = anExtrusionData->Direction; + aCopy->mySurfaceData = aNewData; + } + else if (auto* aRevolutionData = std::get_if(&mySurfaceData)) { - aCopy->myNestedEvaluator = myNestedEvaluator->ShallowCopy(); + GeomAdaptor_Surface::RevolutionData aNewData; + aNewData.BasisCurve = + Handle(GeomAdaptor_Curve)::DownCast(aRevolutionData->BasisCurve->ShallowCopy()); + aNewData.AxisLoc = aRevolutionData->AxisLoc; + aNewData.AxisDir = aRevolutionData->AxisDir; + aCopy->mySurfaceData = aNewData; + } + else if (auto* anOffsetData = std::get_if(&mySurfaceData)) + { + GeomAdaptor_Surface::OffsetData aNewData; + aNewData.BasisAdaptor = + Handle(GeomAdaptor_Surface)::DownCast(anOffsetData->BasisAdaptor->ShallowCopy()); + aNewData.Offset = anOffsetData->Offset; + // Clone the osculating surface if present + if (anOffsetData->OsculatingSurface) + { + aNewData.OsculatingSurface.reset( + new Geom_OsculatingSurface(*anOffsetData->OsculatingSurface)); + } + aCopy->mySurfaceData = std::move(aNewData); + } + else if (auto* aBSplineData = std::get_if(&mySurfaceData)) + { + GeomAdaptor_Surface::BSplineData aNewData; + aNewData.Surface = aBSplineData->Surface; + // Cache is not copied - will be rebuilt on demand + aCopy->mySurfaceData = aNewData; } return aCopy; @@ -153,13 +370,11 @@ void GeomAdaptor_Surface::load(const Handle(Geom_Surface)& S, myULast = ULast; myVFirst = VFirst; myVLast = VLast; - mySurfaceCache.Nullify(); if (mySurface != S) { - mySurface = S; - myNestedEvaluator.Nullify(); - myBSplineSurface.Nullify(); + mySurface = S; + mySurfaceData = std::monostate{}; const Handle(Standard_Type)& TheType = S->DynamicType(); if (TheType == STANDARD_TYPE(Geom_RectangularTrimmedSurface)) @@ -183,48 +398,56 @@ void GeomAdaptor_Surface::load(const Handle(Geom_Surface)& S, else if (TheType == STANDARD_TYPE(Geom_SurfaceOfRevolution)) { mySurfaceType = GeomAbs_SurfaceOfRevolution; - Handle(Geom_SurfaceOfRevolution) myRevSurf = + Handle(Geom_SurfaceOfRevolution) aRevSurf = Handle(Geom_SurfaceOfRevolution)::DownCast(mySurface); - // Create nested adaptor for base curve - Handle(Geom_Curve) aBaseCurve = myRevSurf->BasisCurve(); - Handle(Adaptor3d_Curve) aBaseAdaptor = new GeomAdaptor_Curve(aBaseCurve); - // Create corresponding evaluator - myNestedEvaluator = new GeomEvaluator_SurfaceOfRevolution(aBaseAdaptor, - myRevSurf->Direction(), - myRevSurf->Location()); + // Populate revolution surface data with XYZ for fast evaluation + GeomAdaptor_Surface::RevolutionData aRevData; + aRevData.BasisCurve = new GeomAdaptor_Curve(aRevSurf->BasisCurve()); + aRevData.AxisLoc = aRevSurf->Location().XYZ(); + aRevData.AxisDir = aRevSurf->Direction().XYZ(); + mySurfaceData = aRevData; } else if (TheType == STANDARD_TYPE(Geom_SurfaceOfLinearExtrusion)) { mySurfaceType = GeomAbs_SurfaceOfExtrusion; - Handle(Geom_SurfaceOfLinearExtrusion) myExtSurf = + Handle(Geom_SurfaceOfLinearExtrusion) anExtSurf = Handle(Geom_SurfaceOfLinearExtrusion)::DownCast(mySurface); - // Create nested adaptor for base curve - Handle(Geom_Curve) aBaseCurve = myExtSurf->BasisCurve(); - Handle(Adaptor3d_Curve) aBaseAdaptor = new GeomAdaptor_Curve(aBaseCurve); - // Create corresponding evaluator - myNestedEvaluator = - new GeomEvaluator_SurfaceOfExtrusion(aBaseAdaptor, myExtSurf->Direction()); + // Populate extrusion surface data with XYZ for fast evaluation + GeomAdaptor_Surface::ExtrusionData anExtData; + anExtData.BasisCurve = new GeomAdaptor_Curve(anExtSurf->BasisCurve()); + anExtData.Direction = anExtSurf->Direction().XYZ(); + mySurfaceData = anExtData; } else if (TheType == STANDARD_TYPE(Geom_BezierSurface)) { mySurfaceType = GeomAbs_BezierSurface; + mySurfaceData = GeomAdaptor_Surface::BezierData{}; } else if (TheType == STANDARD_TYPE(Geom_BSplineSurface)) { - mySurfaceType = GeomAbs_BSplineSurface; - myBSplineSurface = Handle(Geom_BSplineSurface)::DownCast(mySurface); + mySurfaceType = GeomAbs_BSplineSurface; + GeomAdaptor_Surface::BSplineData aBSplineData; + aBSplineData.Surface = Handle(Geom_BSplineSurface)::DownCast(mySurface); + mySurfaceData = aBSplineData; } else if (TheType == STANDARD_TYPE(Geom_OffsetSurface)) { mySurfaceType = GeomAbs_OffsetSurface; - Handle(Geom_OffsetSurface) myOffSurf = Handle(Geom_OffsetSurface)::DownCast(mySurface); - // Create nested adaptor for base surface - Handle(Geom_Surface) aBaseSurf = myOffSurf->BasisSurface(); - Handle(GeomAdaptor_Surface) aBaseAdaptor = - new GeomAdaptor_Surface(aBaseSurf, myUFirst, myULast, myVFirst, myVLast, myTolU, myTolV); - myNestedEvaluator = new GeomEvaluator_OffsetSurface(aBaseAdaptor, - myOffSurf->Offset(), - myOffSurf->OsculatingSurface()); + Handle(Geom_OffsetSurface) anOffSurf = Handle(Geom_OffsetSurface)::DownCast(mySurface); + // Populate offset surface data + GeomAdaptor_Surface::OffsetData anOffsetData; + anOffsetData.BasisAdaptor = new GeomAdaptor_Surface(anOffSurf->BasisSurface(), + myUFirst, + myULast, + myVFirst, + myVLast, + myTolU, + myTolV); + anOffsetData.Offset = anOffSurf->Offset(); + // Create osculating surface detector from basis surface + anOffsetData.OsculatingSurface.reset( + new Geom_OsculatingSurface(anOffSurf->BasisSurface(), Precision::Confusion())); + mySurfaceData = std::move(anOffsetData); } else mySurfaceType = GeomAbs_OtherSurface; @@ -242,13 +465,14 @@ GeomAbs_Shape GeomAdaptor_Surface::UContinuity() const switch (mySurfaceType) { case GeomAbs_BSplineSurface: { - const Standard_Integer N = myBSplineSurface->NbUKnots(); + const auto& aBSpl = std::get(mySurfaceData).Surface; + const Standard_Integer N = aBSpl->NbUKnots(); TColStd_Array1OfReal TK(1, N); TColStd_Array1OfInteger TM(1, N); - myBSplineSurface->UKnots(TK); - myBSplineSurface->UMultiplicities(TM); - return LocalContinuity(myBSplineSurface->UDegree(), - myBSplineSurface->NbUKnots(), + aBSpl->UKnots(TK); + aBSpl->UMultiplicities(TM); + return LocalContinuity(aBSpl->UDegree(), + aBSpl->NbUKnots(), TK, TM, myUFirst, @@ -299,13 +523,14 @@ GeomAbs_Shape GeomAdaptor_Surface::VContinuity() const switch (mySurfaceType) { case GeomAbs_BSplineSurface: { - const Standard_Integer N = myBSplineSurface->NbVKnots(); + const auto& aBSpl = std::get(mySurfaceData).Surface; + const Standard_Integer N = aBSpl->NbVKnots(); TColStd_Array1OfReal TK(1, N); TColStd_Array1OfInteger TM(1, N); - myBSplineSurface->VKnots(TK); - myBSplineSurface->VMultiplicities(TM); - return LocalContinuity(myBSplineSurface->VDegree(), - myBSplineSurface->NbVKnots(), + aBSpl->VKnots(TK); + aBSpl->VMultiplicities(TM); + return LocalContinuity(aBSpl->VDegree(), + aBSpl->NbVKnots(), TK, TM, myVFirst, @@ -356,10 +581,10 @@ Standard_Integer GeomAdaptor_Surface::NbUIntervals(const GeomAbs_Shape S) const switch (mySurfaceType) { case GeomAbs_BSplineSurface: { - GeomAdaptor_Curve myBasisCurve( - myBSplineSurface->VIso(myBSplineSurface->VKnot(myBSplineSurface->FirstVKnotIndex())), - myUFirst, - myULast); + const auto& aBSpl = std::get(mySurfaceData).Surface; + GeomAdaptor_Curve myBasisCurve(aBSpl->VIso(aBSpl->VKnot(aBSpl->FirstVKnotIndex())), + myUFirst, + myULast); return myBasisCurve.NbIntervals(S); } case GeomAbs_SurfaceOfExtrusion: { @@ -414,10 +639,10 @@ Standard_Integer GeomAdaptor_Surface::NbVIntervals(const GeomAbs_Shape S) const switch (mySurfaceType) { case GeomAbs_BSplineSurface: { - GeomAdaptor_Curve myBasisCurve( - myBSplineSurface->UIso(myBSplineSurface->UKnot(myBSplineSurface->FirstUKnotIndex())), - myVFirst, - myVLast); + const auto& aBSpl = std::get(mySurfaceData).Surface; + GeomAdaptor_Curve myBasisCurve(aBSpl->UIso(aBSpl->UKnot(aBSpl->FirstUKnotIndex())), + myVFirst, + myVLast); return myBasisCurve.NbIntervals(S); } case GeomAbs_SurfaceOfRevolution: { @@ -472,10 +697,10 @@ void GeomAdaptor_Surface::UIntervals(TColStd_Array1OfReal& T, const GeomAbs_Shap switch (mySurfaceType) { case GeomAbs_BSplineSurface: { - GeomAdaptor_Curve myBasisCurve( - myBSplineSurface->VIso(myBSplineSurface->VKnot(myBSplineSurface->FirstVKnotIndex())), - myUFirst, - myULast); + const auto& aBSpl = std::get(mySurfaceData).Surface; + GeomAdaptor_Curve myBasisCurve(aBSpl->VIso(aBSpl->VKnot(aBSpl->FirstVKnotIndex())), + myUFirst, + myULast); myBasisCurve.Intervals(T, S); return; } @@ -538,10 +763,10 @@ void GeomAdaptor_Surface::VIntervals(TColStd_Array1OfReal& T, const GeomAbs_Shap switch (mySurfaceType) { case GeomAbs_BSplineSurface: { - GeomAdaptor_Curve myBasisCurve( - myBSplineSurface->UIso(myBSplineSurface->UKnot(myBSplineSurface->FirstUKnotIndex())), - myVFirst, - myVLast); + const auto& aBSpl = std::get(mySurfaceData).Surface; + GeomAdaptor_Curve myBasisCurve(aBSpl->UIso(aBSpl->UKnot(aBSpl->FirstUKnotIndex())), + myVFirst, + myVLast); myBasisCurve.Intervals(T, S); return; } @@ -685,39 +910,42 @@ void GeomAdaptor_Surface::RebuildCache(const Standard_Real theU, const Standard_ if (mySurfaceType == GeomAbs_BezierSurface) { // Create cache for Bezier - Handle(Geom_BezierSurface) aBezier = Handle(Geom_BezierSurface)::DownCast(mySurface); - Standard_Integer aDegU = aBezier->UDegree(); - Standard_Integer aDegV = aBezier->VDegree(); + auto& aBezData = std::get(mySurfaceData); + Handle(Geom_BezierSurface) aBezier = Handle(Geom_BezierSurface)::DownCast(mySurface); + Standard_Integer aDegU = aBezier->UDegree(); + Standard_Integer aDegV = aBezier->VDegree(); TColStd_Array1OfReal aFlatKnotsU(BSplCLib::FlatBezierKnots(aDegU), 1, 2 * (aDegU + 1)); TColStd_Array1OfReal aFlatKnotsV(BSplCLib::FlatBezierKnots(aDegV), 1, 2 * (aDegV + 1)); - if (mySurfaceCache.IsNull()) - mySurfaceCache = new BSplSLib_Cache(aDegU, + if (aBezData.Cache.IsNull()) + aBezData.Cache = new BSplSLib_Cache(aDegU, aBezier->IsUPeriodic(), aFlatKnotsU, aDegV, aBezier->IsVPeriodic(), aFlatKnotsV, aBezier->Weights()); - mySurfaceCache + aBezData.Cache ->BuildCache(theU, theV, aFlatKnotsU, aFlatKnotsV, aBezier->Poles(), aBezier->Weights()); } else if (mySurfaceType == GeomAbs_BSplineSurface) { // Create cache for B-spline - if (mySurfaceCache.IsNull()) - mySurfaceCache = new BSplSLib_Cache(myBSplineSurface->UDegree(), - myBSplineSurface->IsUPeriodic(), - myBSplineSurface->UKnotSequence(), - myBSplineSurface->VDegree(), - myBSplineSurface->IsVPeriodic(), - myBSplineSurface->VKnotSequence(), - myBSplineSurface->Weights()); - mySurfaceCache->BuildCache(theU, - theV, - myBSplineSurface->UKnotSequence(), - myBSplineSurface->VKnotSequence(), - myBSplineSurface->Poles(), - myBSplineSurface->Weights()); + auto& aBSplData = std::get(mySurfaceData); + const auto& aBSpl = aBSplData.Surface; + if (aBSplData.Cache.IsNull()) + aBSplData.Cache = new BSplSLib_Cache(aBSpl->UDegree(), + aBSpl->IsUPeriodic(), + aBSpl->UKnotSequence(), + aBSpl->VDegree(), + aBSpl->IsVPeriodic(), + aBSpl->VKnotSequence(), + aBSpl->Weights()); + aBSplData.Cache->BuildCache(theU, + theV, + aBSpl->UKnotSequence(), + aBSpl->VKnotSequence(), + aBSpl->Poles(), + aBSpl->Weights()); } } @@ -736,20 +964,38 @@ void GeomAdaptor_Surface::D0(const Standard_Real U, const Standard_Real V, gp_Pn { switch (mySurfaceType) { - case GeomAbs_BezierSurface: - case GeomAbs_BSplineSurface: - if (mySurfaceCache.IsNull() || !mySurfaceCache->IsCacheValid(U, V)) + case GeomAbs_BezierSurface: { + auto& aCache = std::get(mySurfaceData).Cache; + if (aCache.IsNull() || !aCache->IsCacheValid(U, V)) + RebuildCache(U, V); + aCache->D0(U, V, P); + break; + } + case GeomAbs_BSplineSurface: { + auto& aCache = std::get(mySurfaceData).Cache; + if (aCache.IsNull() || !aCache->IsCacheValid(U, V)) RebuildCache(U, V); - mySurfaceCache->D0(U, V, P); + aCache->D0(U, V, P); break; + } - case GeomAbs_OffsetSurface: - case GeomAbs_SurfaceOfExtrusion: - case GeomAbs_SurfaceOfRevolution: - Standard_NoSuchObject_Raise_if(myNestedEvaluator.IsNull(), - "GeomAdaptor_Surface::D0: evaluator is not initialized"); - myNestedEvaluator->D0(U, V, P); + case GeomAbs_SurfaceOfExtrusion: { + const auto& anExtData = std::get(mySurfaceData); + Geom_ExtrusionUtils::D0(U, V, *anExtData.BasisCurve, anExtData.Direction, P); break; + } + + case GeomAbs_SurfaceOfRevolution: { + const auto& aRevData = std::get(mySurfaceData); + Geom_RevolutionUtils::D0(U, V, *aRevData.BasisCurve, aRevData.AxisLoc, aRevData.AxisDir, P); + break; + } + + case GeomAbs_OffsetSurface: { + const auto& anOffData = std::get(mySurfaceData); + offsetD0(U, V, anOffData, P); + break; + } default: mySurface->D0(U, V, P); @@ -789,27 +1035,51 @@ void GeomAdaptor_Surface::D1(const Standard_Real U, switch (mySurfaceType) { - case GeomAbs_BezierSurface: + case GeomAbs_BezierSurface: { + auto& aCache = std::get(mySurfaceData).Cache; + if (aCache.IsNull() || !aCache->IsCacheValid(U, V)) + RebuildCache(U, V); + aCache->D1(U, V, P, D1U, D1V); + break; + } case GeomAbs_BSplineSurface: { - if (!myBSplineSurface.IsNull() && (USide != 0 || VSide != 0) - && IfUVBound(u, v, Ideb, Ifin, IVdeb, IVfin, USide, VSide)) - myBSplineSurface->LocalD1(u, v, Ideb, Ifin, IVdeb, IVfin, P, D1U, D1V); + auto& aBSplData = std::get(mySurfaceData); + const auto& aBSpl = aBSplData.Surface; + if ((USide != 0 || VSide != 0) && IfUVBound(u, v, Ideb, Ifin, IVdeb, IVfin, USide, VSide)) + aBSpl->LocalD1(u, v, Ideb, Ifin, IVdeb, IVfin, P, D1U, D1V); else { - if (mySurfaceCache.IsNull() || !mySurfaceCache->IsCacheValid(U, V)) + if (aBSplData.Cache.IsNull() || !aBSplData.Cache->IsCacheValid(U, V)) RebuildCache(U, V); - mySurfaceCache->D1(U, V, P, D1U, D1V); + aBSplData.Cache->D1(U, V, P, D1U, D1V); } break; } - case GeomAbs_SurfaceOfExtrusion: - case GeomAbs_SurfaceOfRevolution: - case GeomAbs_OffsetSurface: - Standard_NoSuchObject_Raise_if(myNestedEvaluator.IsNull(), - "GeomAdaptor_Surface::D1: evaluator is not initialized"); - myNestedEvaluator->D1(u, v, P, D1U, D1V); + case GeomAbs_SurfaceOfExtrusion: { + const auto& anExtData = std::get(mySurfaceData); + Geom_ExtrusionUtils::D1(u, v, *anExtData.BasisCurve, anExtData.Direction, P, D1U, D1V); + break; + } + + case GeomAbs_SurfaceOfRevolution: { + const auto& aRevData = std::get(mySurfaceData); + Geom_RevolutionUtils::D1(u, + v, + *aRevData.BasisCurve, + aRevData.AxisLoc, + aRevData.AxisDir, + P, + D1U, + D1V); break; + } + + case GeomAbs_OffsetSurface: { + const auto& anOffData = std::get(mySurfaceData); + offsetD1(u, v, anOffData, P, D1U, D1V); + break; + } default: mySurface->D1(u, v, P, D1U, D1V); @@ -852,27 +1122,63 @@ void GeomAdaptor_Surface::D2(const Standard_Real U, switch (mySurfaceType) { - case GeomAbs_BezierSurface: + case GeomAbs_BezierSurface: { + auto& aCache = std::get(mySurfaceData).Cache; + if (aCache.IsNull() || !aCache->IsCacheValid(U, V)) + RebuildCache(U, V); + aCache->D2(U, V, P, D1U, D1V, D2U, D2V, D2UV); + break; + } case GeomAbs_BSplineSurface: { - if (!myBSplineSurface.IsNull() && (USide != 0 || VSide != 0) - && IfUVBound(u, v, Ideb, Ifin, IVdeb, IVfin, USide, VSide)) - myBSplineSurface->LocalD2(u, v, Ideb, Ifin, IVdeb, IVfin, P, D1U, D1V, D2U, D2V, D2UV); + auto& aBSplData = std::get(mySurfaceData); + const auto& aBSpl = aBSplData.Surface; + if ((USide != 0 || VSide != 0) && IfUVBound(u, v, Ideb, Ifin, IVdeb, IVfin, USide, VSide)) + aBSpl->LocalD2(u, v, Ideb, Ifin, IVdeb, IVfin, P, D1U, D1V, D2U, D2V, D2UV); else { - if (mySurfaceCache.IsNull() || !mySurfaceCache->IsCacheValid(U, V)) + if (aBSplData.Cache.IsNull() || !aBSplData.Cache->IsCacheValid(U, V)) RebuildCache(U, V); - mySurfaceCache->D2(U, V, P, D1U, D1V, D2U, D2V, D2UV); + aBSplData.Cache->D2(U, V, P, D1U, D1V, D2U, D2V, D2UV); } break; } - case GeomAbs_SurfaceOfExtrusion: - case GeomAbs_SurfaceOfRevolution: - case GeomAbs_OffsetSurface: - Standard_NoSuchObject_Raise_if(myNestedEvaluator.IsNull(), - "GeomAdaptor_Surface::D2: evaluator is not initialized"); - myNestedEvaluator->D2(u, v, P, D1U, D1V, D2U, D2V, D2UV); + case GeomAbs_SurfaceOfExtrusion: { + const auto& anExtData = std::get(mySurfaceData); + Geom_ExtrusionUtils::D2(u, + v, + *anExtData.BasisCurve, + anExtData.Direction, + P, + D1U, + D1V, + D2U, + D2V, + D2UV); break; + } + + case GeomAbs_SurfaceOfRevolution: { + const auto& aRevData = std::get(mySurfaceData); + Geom_RevolutionUtils::D2(u, + v, + *aRevData.BasisCurve, + aRevData.AxisLoc, + aRevData.AxisDir, + P, + D1U, + D1V, + D2U, + D2V, + D2UV); + break; + } + + case GeomAbs_OffsetSurface: { + const auto& anOffData = std::get(mySurfaceData); + offsetD2(u, v, anOffData, P, D1U, D1V, D2U, D2V, D2UV); + break; + } default: { mySurface->D2(u, v, P, D1U, D1V, D2U, D2V, D2UV); @@ -922,40 +1228,78 @@ void GeomAdaptor_Surface::D3(const Standard_Real U, switch (mySurfaceType) { case GeomAbs_BSplineSurface: { + const auto& aBSpl = std::get(mySurfaceData).Surface; if ((USide == 0) && (VSide == 0)) - myBSplineSurface->D3(u, v, P, D1U, D1V, D2U, D2V, D2UV, D3U, D3V, D3UUV, D3UVV); + aBSpl->D3(u, v, P, D1U, D1V, D2U, D2V, D2UV, D3U, D3V, D3UUV, D3UVV); else { if (IfUVBound(u, v, Ideb, Ifin, IVdeb, IVfin, USide, VSide)) - myBSplineSurface->LocalD3(u, - v, - Ideb, - Ifin, - IVdeb, - IVfin, - P, - D1U, - D1V, - D2U, - D2V, - D2UV, - D3U, - D3V, - D3UUV, - D3UVV); + aBSpl->LocalD3(u, + v, + Ideb, + Ifin, + IVdeb, + IVfin, + P, + D1U, + D1V, + D2U, + D2V, + D2UV, + D3U, + D3V, + D3UUV, + D3UVV); else - myBSplineSurface->D3(u, v, P, D1U, D1V, D2U, D2V, D2UV, D3U, D3V, D3UUV, D3UVV); + aBSpl->D3(u, v, P, D1U, D1V, D2U, D2V, D2UV, D3U, D3V, D3UUV, D3UVV); } break; } - case GeomAbs_SurfaceOfExtrusion: - case GeomAbs_SurfaceOfRevolution: - case GeomAbs_OffsetSurface: - Standard_NoSuchObject_Raise_if(myNestedEvaluator.IsNull(), - "GeomAdaptor_Surface::D3: evaluator is not initialized"); - myNestedEvaluator->D3(u, v, P, D1U, D1V, D2U, D2V, D2UV, D3U, D3V, D3UUV, D3UVV); + case GeomAbs_SurfaceOfExtrusion: { + const auto& anExtData = std::get(mySurfaceData); + Geom_ExtrusionUtils::D3(u, + v, + *anExtData.BasisCurve, + anExtData.Direction, + P, + D1U, + D1V, + D2U, + D2V, + D2UV, + D3U, + D3V, + D3UUV, + D3UVV); break; + } + + case GeomAbs_SurfaceOfRevolution: { + const auto& aRevData = std::get(mySurfaceData); + Geom_RevolutionUtils::D3(u, + v, + *aRevData.BasisCurve, + aRevData.AxisLoc, + aRevData.AxisDir, + P, + D1U, + D1V, + D2U, + D2V, + D2UV, + D3U, + D3V, + D3UUV, + D3UVV); + break; + } + + case GeomAbs_OffsetSurface: { + const auto& anOffData = std::get(mySurfaceData); + offsetD3(u, v, anOffData, P, D1U, D1V, D2U, D2V, D2UV, D3U, D3V, D3UUV, D3UVV); + break; + } default: { mySurface->D3(u, v, P, D1U, D1V, D2U, D2V, D2UV, D3U, D3V, D3UUV, D3UVV); @@ -997,23 +1341,38 @@ gp_Vec GeomAdaptor_Surface::DN(const Standard_Real U, switch (mySurfaceType) { case GeomAbs_BSplineSurface: { + const auto& aBSpl = std::get(mySurfaceData).Surface; if ((USide == 0) && (VSide == 0)) - return myBSplineSurface->DN(u, v, Nu, Nv); + return aBSpl->DN(u, v, Nu, Nv); else { if (IfUVBound(u, v, Ideb, Ifin, IVdeb, IVfin, USide, VSide)) - return myBSplineSurface->LocalDN(u, v, Ideb, Ifin, IVdeb, IVfin, Nu, Nv); + return aBSpl->LocalDN(u, v, Ideb, Ifin, IVdeb, IVfin, Nu, Nv); else - return myBSplineSurface->DN(u, v, Nu, Nv); + return aBSpl->DN(u, v, Nu, Nv); } } - case GeomAbs_SurfaceOfExtrusion: - case GeomAbs_SurfaceOfRevolution: - case GeomAbs_OffsetSurface: - Standard_NoSuchObject_Raise_if(myNestedEvaluator.IsNull(), - "GeomAdaptor_Surface::DN: evaluator is not initialized"); - return myNestedEvaluator->DN(u, v, Nu, Nv); + case GeomAbs_SurfaceOfExtrusion: { + const auto& anExtData = std::get(mySurfaceData); + return Geom_ExtrusionUtils::DN(u, *anExtData.BasisCurve, anExtData.Direction, Nu, Nv); + } + + case GeomAbs_SurfaceOfRevolution: { + const auto& aRevData = std::get(mySurfaceData); + return Geom_RevolutionUtils::DN(u, + v, + *aRevData.BasisCurve, + aRevData.AxisLoc, + aRevData.AxisDir, + Nu, + Nv); + } + + case GeomAbs_OffsetSurface: { + const auto& anOffData = std::get(mySurfaceData); + return offsetDN(u, v, anOffData, Nu, Nv); + } case GeomAbs_Plane: case GeomAbs_Cylinder: @@ -1089,7 +1448,7 @@ Standard_Real GeomAdaptor_Surface::UResolution(const Standard_Real R3d) const } case GeomAbs_BSplineSurface: { Standard_Real Ures, Vres; - myBSplineSurface->Resolution(R3d, Ures, Vres); + std::get(mySurfaceData).Surface->Resolution(R3d, Ures, Vres); return Ures; } case GeomAbs_OffsetSurface: { @@ -1149,7 +1508,7 @@ Standard_Real GeomAdaptor_Surface::VResolution(const Standard_Real R3d) const } case GeomAbs_BSplineSurface: { Standard_Real Ures, Vres; - myBSplineSurface->Resolution(R3d, Ures, Vres); + std::get(mySurfaceData).Surface->Resolution(R3d, Ures, Vres); return Vres; } case GeomAbs_OffsetSurface: { @@ -1217,7 +1576,7 @@ gp_Torus GeomAdaptor_Surface::Torus() const Standard_Integer GeomAdaptor_Surface::UDegree() const { if (mySurfaceType == GeomAbs_BSplineSurface) - return myBSplineSurface->UDegree(); + return std::get(mySurfaceData).Surface->UDegree(); if (mySurfaceType == GeomAbs_BezierSurface) return Handle(Geom_BezierSurface)::DownCast(mySurface)->UDegree(); if (mySurfaceType == GeomAbs_SurfaceOfExtrusion) @@ -1236,7 +1595,7 @@ Standard_Integer GeomAdaptor_Surface::UDegree() const Standard_Integer GeomAdaptor_Surface::NbUPoles() const { if (mySurfaceType == GeomAbs_BSplineSurface) - return myBSplineSurface->NbUPoles(); + return std::get(mySurfaceData).Surface->NbUPoles(); if (mySurfaceType == GeomAbs_BezierSurface) return Handle(Geom_BezierSurface)::DownCast(mySurface)->NbUPoles(); if (mySurfaceType == GeomAbs_SurfaceOfExtrusion) @@ -1255,7 +1614,7 @@ Standard_Integer GeomAdaptor_Surface::NbUPoles() const Standard_Integer GeomAdaptor_Surface::VDegree() const { if (mySurfaceType == GeomAbs_BSplineSurface) - return myBSplineSurface->VDegree(); + return std::get(mySurfaceData).Surface->VDegree(); if (mySurfaceType == GeomAbs_BezierSurface) return Handle(Geom_BezierSurface)::DownCast(mySurface)->VDegree(); if (mySurfaceType == GeomAbs_SurfaceOfRevolution) @@ -1274,7 +1633,7 @@ Standard_Integer GeomAdaptor_Surface::VDegree() const Standard_Integer GeomAdaptor_Surface::NbVPoles() const { if (mySurfaceType == GeomAbs_BSplineSurface) - return myBSplineSurface->NbVPoles(); + return std::get(mySurfaceData).Surface->NbVPoles(); if (mySurfaceType == GeomAbs_BezierSurface) return Handle(Geom_BezierSurface)::DownCast(mySurface)->NbVPoles(); if (mySurfaceType == GeomAbs_SurfaceOfRevolution) @@ -1293,7 +1652,7 @@ Standard_Integer GeomAdaptor_Surface::NbVPoles() const Standard_Integer GeomAdaptor_Surface::NbUKnots() const { if (mySurfaceType == GeomAbs_BSplineSurface) - return myBSplineSurface->NbUKnots(); + return std::get(mySurfaceData).Surface->NbUKnots(); if (mySurfaceType == GeomAbs_SurfaceOfExtrusion) { GeomAdaptor_Curve myBasisCurve( @@ -1310,7 +1669,7 @@ Standard_Integer GeomAdaptor_Surface::NbUKnots() const Standard_Integer GeomAdaptor_Surface::NbVKnots() const { if (mySurfaceType == GeomAbs_BSplineSurface) - return myBSplineSurface->NbVKnots(); + return std::get(mySurfaceData).Surface->NbVKnots(); throw Standard_NoSuchObject("GeomAdaptor_Surface::NbVKnots"); } @@ -1319,7 +1678,7 @@ Standard_Integer GeomAdaptor_Surface::NbVKnots() const Standard_Boolean GeomAdaptor_Surface::IsURational() const { if (mySurfaceType == GeomAbs_BSplineSurface) - return myBSplineSurface->IsURational(); + return std::get(mySurfaceData).Surface->IsURational(); if (mySurfaceType == GeomAbs_BezierSurface) return Handle(Geom_BezierSurface)::DownCast(mySurface)->IsURational(); return Standard_False; @@ -1330,7 +1689,7 @@ Standard_Boolean GeomAdaptor_Surface::IsURational() const Standard_Boolean GeomAdaptor_Surface::IsVRational() const { if (mySurfaceType == GeomAbs_BSplineSurface) - return myBSplineSurface->IsVRational(); + return std::get(mySurfaceData).Surface->IsVRational(); if (mySurfaceType == GeomAbs_BezierSurface) return Handle(Geom_BezierSurface)::DownCast(mySurface)->IsVRational(); return Standard_False; @@ -1351,7 +1710,7 @@ Handle(Geom_BSplineSurface) GeomAdaptor_Surface::BSpline() const { if (mySurfaceType != GeomAbs_BSplineSurface) throw Standard_NoSuchObject("GeomAdaptor_Surface::BSpline"); - return myBSplineSurface; + return std::get(mySurfaceData).Surface; } //================================================================================================= @@ -1424,16 +1783,15 @@ Standard_Boolean GeomAdaptor_Surface::IfUVBound(const Standard_Real U, const Standard_Integer USide, const Standard_Integer VSide) const { + const auto& aBSpl = std::get(mySurfaceData).Surface; Standard_Integer Ideb, Ifin; - Standard_Integer anUFKIndx = myBSplineSurface->FirstUKnotIndex(), - anULKIndx = myBSplineSurface->LastUKnotIndex(), - aVFKIndx = myBSplineSurface->FirstVKnotIndex(), - aVLKIndx = myBSplineSurface->LastVKnotIndex(); - myBSplineSurface->LocateU(U, PosTol, Ideb, Ifin, Standard_False); + Standard_Integer anUFKIndx = aBSpl->FirstUKnotIndex(), anULKIndx = aBSpl->LastUKnotIndex(), + aVFKIndx = aBSpl->FirstVKnotIndex(), aVLKIndx = aBSpl->LastVKnotIndex(); + aBSpl->LocateU(U, PosTol, Ideb, Ifin, Standard_False); Standard_Boolean Local = (Ideb == Ifin); Span(USide, Ideb, Ifin, Ideb, Ifin, anUFKIndx, anULKIndx); Standard_Integer IVdeb, IVfin; - myBSplineSurface->LocateV(V, PosTol, IVdeb, IVfin, Standard_False); + aBSpl->LocateV(V, PosTol, IVdeb, IVfin, Standard_False); if (IVdeb == IVfin) Local = Standard_True; Span(VSide, IVdeb, IVfin, IVdeb, IVfin, aVFKIndx, aVLKIndx); diff --git a/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_Surface.hxx b/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_Surface.hxx index 849f4e6760..23c9c010fe 100644 --- a/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_Surface.hxx +++ b/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_Surface.hxx @@ -20,11 +20,19 @@ #include #include #include -#include #include +#include +#include +#include #include #include +#include +#include + +class GeomAdaptor_Curve; +class Geom_OsculatingSurface; + DEFINE_STANDARD_HANDLE(GeomAdaptor_Surface, Adaptor3d_Surface) //! An interface between the services provided by any @@ -38,6 +46,60 @@ DEFINE_STANDARD_HANDLE(GeomAdaptor_Surface, Adaptor3d_Surface) class GeomAdaptor_Surface : public Adaptor3d_Surface { DEFINE_STANDARD_RTTIEXT(GeomAdaptor_Surface, Adaptor3d_Surface) +public: + //! Internal structure for extrusion surface evaluation data. + struct ExtrusionData + { + Handle(GeomAdaptor_Curve) BasisCurve; //!< Adaptor for basis curve + gp_XYZ Direction; //!< Extrusion direction XYZ (normalized) + }; + + //! Internal structure for revolution surface evaluation data. + struct RevolutionData + { + Handle(GeomAdaptor_Curve) BasisCurve; //!< Adaptor for basis curve + gp_XYZ AxisLoc; //!< Axis location XYZ + gp_XYZ AxisDir; //!< Axis direction XYZ (normalized) + }; + + //! Custom deleter for Geom_OsculatingSurface to allow incomplete type in header. + struct OsculatingSurfaceDeleter + { + Standard_EXPORT void operator()(Geom_OsculatingSurface* thePtr) const; + }; + + //! Internal structure for offset surface evaluation data. + struct OffsetData + { + Handle(GeomAdaptor_Surface) BasisAdaptor; //!< Adaptor for basis surface + double Offset = 0.0; //!< Offset distance + std::unique_ptr + OsculatingSurface; //!< Osculating surface for singular cases + + OffsetData() = default; + Standard_EXPORT OffsetData(const OffsetData& theOther); + Standard_EXPORT OffsetData& operator=(const OffsetData& theOther); + OffsetData(OffsetData&&) = default; + OffsetData& operator=(OffsetData&&) = default; + }; + + //! Internal structure for Bezier surface cache data. + struct BezierData + { + mutable Handle(BSplSLib_Cache) Cache; //!< Cached data for evaluation + }; + + //! Internal structure for BSpline surface cache data. + struct BSplineData + { + Handle(Geom_BSplineSurface) Surface; //!< BSpline surface to prevent downcasts + mutable Handle(BSplSLib_Cache) Cache; //!< Cached data for evaluation + }; + + //! Variant type for surface-specific evaluation data. + using SurfaceDataVariant = std:: + variant; + public: GeomAdaptor_Surface() : myUFirst(0.), @@ -115,6 +177,22 @@ public: virtual Standard_Real LastVParameter() const Standard_OVERRIDE { return myVLast; } + //! Returns the parametric bounds of the surface. + //! @param[out] theU1 minimum U parameter + //! @param[out] theU2 maximum U parameter + //! @param[out] theV1 minimum V parameter + //! @param[out] theV2 maximum V parameter + void Bounds(Standard_Real& theU1, + Standard_Real& theU2, + Standard_Real& theV1, + Standard_Real& theV2) const + { + theU1 = FirstUParameter(); + theU2 = LastUParameter(); + theV1 = FirstVParameter(); + theV2 = LastVParameter(); + } + Standard_EXPORT GeomAbs_Shape UContinuity() const Standard_OVERRIDE; Standard_EXPORT GeomAbs_Shape VContinuity() const Standard_OVERRIDE; @@ -340,14 +418,8 @@ protected: Standard_Real myVLast; Standard_Real myTolU; Standard_Real myTolV; - - Handle(Geom_BSplineSurface) myBSplineSurface; ///< B-spline representation to prevent downcasts - mutable Handle(BSplSLib_Cache) mySurfaceCache; ///< Cached data for B-spline or Bezier surface - - GeomAbs_SurfaceType mySurfaceType; - // clang-format off - Handle(GeomEvaluator_Surface) myNestedEvaluator; ///< Calculates values of nested complex surfaces (offset surface, surface of extrusion or revolution) - // clang-format on + GeomAbs_SurfaceType mySurfaceType; + SurfaceDataVariant mySurfaceData; ///< Surface-specific evaluation data }; #endif // _GeomAdaptor_Surface_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_SurfaceOfLinearExtrusion.cxx b/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_SurfaceOfLinearExtrusion.cxx index b89f570f8f..d9704cbfba 100644 --- a/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_SurfaceOfLinearExtrusion.cxx +++ b/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_SurfaceOfLinearExtrusion.cxx @@ -17,8 +17,8 @@ #include #include +#include #include -#include #include IMPLEMENT_STANDARD_RTTIEXT(GeomAdaptor_SurfaceOfLinearExtrusion, GeomAdaptor_Surface) @@ -58,24 +58,31 @@ Handle(Adaptor3d_Surface) GeomAdaptor_SurfaceOfLinearExtrusion::ShallowCopy() co if (!myBasisCurve.IsNull()) { - aCopy->myBasisCurve = myBasisCurve->ShallowCopy(); + aCopy->myBasisCurve = Handle(GeomAdaptor_Curve)::DownCast(myBasisCurve->ShallowCopy()); } aCopy->myDirection = myDirection; aCopy->myHaveDir = myHaveDir; - aCopy->mySurface = mySurface; - aCopy->myUFirst = myUFirst; - aCopy->myULast = myULast; - aCopy->myVFirst = myVFirst; - aCopy->myVLast = myVLast; - aCopy->myTolU = myTolU; - aCopy->myTolV = myTolV; - aCopy->myBSplineSurface = myBSplineSurface; - + aCopy->mySurface = mySurface; + aCopy->myUFirst = myUFirst; + aCopy->myULast = myULast; + aCopy->myVFirst = myVFirst; + aCopy->myVLast = myVLast; + aCopy->myTolU = myTolU; + aCopy->myTolV = myTolV; aCopy->mySurfaceType = mySurfaceType; - if (!myNestedEvaluator.IsNull()) + + // Copy surface data variant - for extrusion, shallow copy of basis curve is sufficient + if (const auto* anExtData = std::get_if(&mySurfaceData)) { - aCopy->myNestedEvaluator = myNestedEvaluator->ShallowCopy(); + GeomAdaptor_Surface::ExtrusionData aNewData; + if (!anExtData->BasisCurve.IsNull()) + { + aNewData.BasisCurve = + Handle(GeomAdaptor_Curve)::DownCast(anExtData->BasisCurve->ShallowCopy()); + } + aNewData.Direction = anExtData->Direction; + aCopy->mySurfaceData = std::move(aNewData); } return aCopy; @@ -85,7 +92,7 @@ Handle(Adaptor3d_Surface) GeomAdaptor_SurfaceOfLinearExtrusion::ShallowCopy() co void GeomAdaptor_SurfaceOfLinearExtrusion::Load(const Handle(Adaptor3d_Curve)& C) { - myBasisCurve = C; + myBasisCurve = Handle(GeomAdaptor_Curve)::DownCast(C); if (myHaveDir) Load(myDirection); } @@ -97,8 +104,13 @@ void GeomAdaptor_SurfaceOfLinearExtrusion::Load(const gp_Dir& V) myHaveDir = Standard_True; myDirection = V; - mySurfaceType = GeomAbs_SurfaceOfExtrusion; - myNestedEvaluator = new GeomEvaluator_SurfaceOfExtrusion(myBasisCurve, myDirection); + mySurfaceType = GeomAbs_SurfaceOfExtrusion; + + // Populate the surface data variant for extrusion evaluation + GeomAdaptor_Surface::ExtrusionData anExtData; + anExtData.BasisCurve = myBasisCurve; + anExtData.Direction = myDirection.XYZ(); + mySurfaceData = std::move(anExtData); } //================================================================================================= @@ -180,7 +192,8 @@ Handle(Adaptor3d_Surface) GeomAdaptor_SurfaceOfLinearExtrusion::VTrim(const Stan const Standard_Real Last, const Standard_Real Tol) const { - Handle(Adaptor3d_Curve) HC = BasisCurve()->Trim(First, Last, Tol); + Handle(GeomAdaptor_Curve) HC = + Handle(GeomAdaptor_Curve)::DownCast(BasisCurve()->Trim(First, Last, Tol)); Handle(GeomAdaptor_SurfaceOfLinearExtrusion) HR = new GeomAdaptor_SurfaceOfLinearExtrusion(GeomAdaptor_SurfaceOfLinearExtrusion(HC, myDirection)); return HR; diff --git a/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_SurfaceOfLinearExtrusion.hxx b/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_SurfaceOfLinearExtrusion.hxx index 0571ae6c93..c519eb9c16 100644 --- a/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_SurfaceOfLinearExtrusion.hxx +++ b/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_SurfaceOfLinearExtrusion.hxx @@ -19,6 +19,7 @@ #include +class GeomAdaptor_Curve; class gp_Pln; class gp_Cylinder; class gp_Cone; @@ -161,9 +162,9 @@ public: Standard_EXPORT Handle(Adaptor3d_Curve) BasisCurve() const Standard_OVERRIDE; private: - Handle(Adaptor3d_Curve) myBasisCurve; ///< extruded curve - gp_Dir myDirection; ///< direction of extrusion - Standard_Boolean myHaveDir; ///< whether the direction of extrusion is initialized + Handle(GeomAdaptor_Curve) myBasisCurve; ///< extruded curve + gp_Dir myDirection; ///< direction of extrusion + Standard_Boolean myHaveDir; ///< whether the direction of extrusion is initialized }; #endif // _GeomAdaptor_SurfaceOfLinearExtrusion_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_SurfaceOfRevolution.cxx b/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_SurfaceOfRevolution.cxx index f8524375ea..588ad8f20c 100644 --- a/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_SurfaceOfRevolution.cxx +++ b/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_SurfaceOfRevolution.cxx @@ -17,8 +17,8 @@ #include #include +#include #include -#include #include IMPLEMENT_STANDARD_RTTIEXT(GeomAdaptor_SurfaceOfRevolution, GeomAdaptor_Surface) @@ -56,25 +56,33 @@ Handle(Adaptor3d_Surface) GeomAdaptor_SurfaceOfRevolution::ShallowCopy() const if (!myBasisCurve.IsNull()) { - aCopy->myBasisCurve = myBasisCurve->ShallowCopy(); + aCopy->myBasisCurve = Handle(GeomAdaptor_Curve)::DownCast(myBasisCurve->ShallowCopy()); } aCopy->myAxis = myAxis; aCopy->myHaveAxis = myHaveAxis; aCopy->myAxeRev = myAxeRev; - aCopy->mySurface = mySurface; - aCopy->myUFirst = myUFirst; - aCopy->myULast = myULast; - aCopy->myVFirst = myVFirst; - aCopy->myVLast = myVLast; - aCopy->myTolU = myTolU; - aCopy->myTolV = myTolV; - aCopy->myBSplineSurface = myBSplineSurface; - + aCopy->mySurface = mySurface; + aCopy->myUFirst = myUFirst; + aCopy->myULast = myULast; + aCopy->myVFirst = myVFirst; + aCopy->myVLast = myVLast; + aCopy->myTolU = myTolU; + aCopy->myTolV = myTolV; aCopy->mySurfaceType = mySurfaceType; - if (!myNestedEvaluator.IsNull()) + + // Copy surface data variant - for revolution, shallow copy of basis curve is sufficient + if (const auto* aRevData = std::get_if(&mySurfaceData)) { - aCopy->myNestedEvaluator = myNestedEvaluator->ShallowCopy(); + GeomAdaptor_Surface::RevolutionData aNewData; + if (!aRevData->BasisCurve.IsNull()) + { + aNewData.BasisCurve = + Handle(GeomAdaptor_Curve)::DownCast(aRevData->BasisCurve->ShallowCopy()); + } + aNewData.AxisLoc = aRevData->AxisLoc; + aNewData.AxisDir = aRevData->AxisDir; + aCopy->mySurfaceData = std::move(aNewData); } return aCopy; @@ -84,7 +92,7 @@ Handle(Adaptor3d_Surface) GeomAdaptor_SurfaceOfRevolution::ShallowCopy() const void GeomAdaptor_SurfaceOfRevolution::Load(const Handle(Adaptor3d_Curve)& C) { - myBasisCurve = C; + myBasisCurve = Handle(GeomAdaptor_Curve)::DownCast(C); if (myHaveAxis) Load(myAxis); // to evaluate the new myAxeRev. } @@ -97,8 +105,13 @@ void GeomAdaptor_SurfaceOfRevolution::Load(const gp_Ax1& V) myAxis = V; mySurfaceType = GeomAbs_SurfaceOfRevolution; - myNestedEvaluator = - new GeomEvaluator_SurfaceOfRevolution(myBasisCurve, myAxis.Direction(), myAxis.Location()); + + // Populate the surface data variant for revolution evaluation + GeomAdaptor_Surface::RevolutionData aRevData; + aRevData.BasisCurve = myBasisCurve; + aRevData.AxisLoc = myAxis.Location().XYZ(); + aRevData.AxisDir = myAxis.Direction().XYZ(); + mySurfaceData = std::move(aRevData); // Eval myAxeRev : axe of revolution ( Determination de Ox). gp_Pnt P, Q; @@ -282,7 +295,8 @@ Handle(Adaptor3d_Surface) GeomAdaptor_SurfaceOfRevolution::VTrim(const Standard_ const Standard_Real Last, const Standard_Real Tol) const { - Handle(Adaptor3d_Curve) HC = BasisCurve()->Trim(First, Last, Tol); + Handle(GeomAdaptor_Curve) HC = + Handle(GeomAdaptor_Curve)::DownCast(BasisCurve()->Trim(First, Last, Tol)); Handle(GeomAdaptor_SurfaceOfRevolution) HR = new GeomAdaptor_SurfaceOfRevolution(GeomAdaptor_SurfaceOfRevolution(HC, myAxis)); return HR; diff --git a/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_SurfaceOfRevolution.hxx b/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_SurfaceOfRevolution.hxx index ce46c262df..8b6467657a 100644 --- a/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_SurfaceOfRevolution.hxx +++ b/src/ModelingData/TKG3d/GeomAdaptor/GeomAdaptor_SurfaceOfRevolution.hxx @@ -19,6 +19,7 @@ #include +class GeomAdaptor_Curve; class gp_Pln; class gp_Cylinder; class gp_Cone; @@ -174,10 +175,10 @@ public: Standard_EXPORT Handle(Adaptor3d_Curve) BasisCurve() const Standard_OVERRIDE; private: - Handle(Adaptor3d_Curve) myBasisCurve; ///< revolved curve - gp_Ax1 myAxis; ///< axis of revolution - Standard_Boolean myHaveAxis; ///< whether axis of revolution is initialized - gp_Ax3 myAxeRev; ///< auxiliary trihedron according to the curve position + Handle(GeomAdaptor_Curve) myBasisCurve; ///< revolved curve + gp_Ax1 myAxis; ///< axis of revolution + Standard_Boolean myHaveAxis; ///< whether axis of revolution is initialized + gp_Ax3 myAxeRev; ///< auxiliary trihedron according to the curve position }; #endif // _GeomAdaptor_SurfaceOfRevolution_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomEvaluator/FILES.cmake b/src/ModelingData/TKG3d/GeomEvaluator/FILES.cmake deleted file mode 100644 index d86f4e5541..0000000000 --- a/src/ModelingData/TKG3d/GeomEvaluator/FILES.cmake +++ /dev/null @@ -1,15 +0,0 @@ -# Source files for GeomEvaluator package -set(OCCT_GeomEvaluator_FILES_LOCATION "${CMAKE_CURRENT_LIST_DIR}") - -set(OCCT_GeomEvaluator_FILES - GeomEvaluator_Curve.hxx - GeomEvaluator_OffsetCurve.cxx - GeomEvaluator_OffsetCurve.hxx - GeomEvaluator_OffsetSurface.cxx - GeomEvaluator_OffsetSurface.hxx - GeomEvaluator_Surface.hxx - GeomEvaluator_SurfaceOfExtrusion.cxx - GeomEvaluator_SurfaceOfExtrusion.hxx - GeomEvaluator_SurfaceOfRevolution.cxx - GeomEvaluator_SurfaceOfRevolution.hxx -) diff --git a/src/ModelingData/TKG3d/GeomEvaluator/GeomEvaluator_Curve.hxx b/src/ModelingData/TKG3d/GeomEvaluator/GeomEvaluator_Curve.hxx deleted file mode 100644 index 5080bdb844..0000000000 --- a/src/ModelingData/TKG3d/GeomEvaluator/GeomEvaluator_Curve.hxx +++ /dev/null @@ -1,56 +0,0 @@ -// Created on: 2015-09-21 -// Copyright (c) 2015 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 _GeomEvaluator_Curve_HeaderFile -#define _GeomEvaluator_Curve_HeaderFile - -#include -#include - -class gp_Pnt; -class gp_Vec; - -//! Interface for calculation of values and derivatives for different kinds of curves in 3D. -//! Works both with adaptors and curves. -class GeomEvaluator_Curve : public Standard_Transient -{ -public: - GeomEvaluator_Curve() {} - - //! Value of 3D curve - virtual void D0(const Standard_Real theU, gp_Pnt& theValue) const = 0; - //! Value and first derivatives of curve - virtual void D1(const Standard_Real theU, gp_Pnt& theValue, gp_Vec& theD1) const = 0; - //! Value, first and second derivatives of curve - virtual void D2(const Standard_Real theU, - gp_Pnt& theValue, - gp_Vec& theD1, - gp_Vec& theD2) const = 0; - //! Value, first, second and third derivatives of curve - virtual void D3(const Standard_Real theU, - gp_Pnt& theValue, - gp_Vec& theD1, - gp_Vec& theD2, - gp_Vec& theD3) const = 0; - //! Calculates N-th derivatives of curve, where N = theDerU. Raises if N < 1 - virtual gp_Vec DN(const Standard_Real theU, const Standard_Integer theDerU) const = 0; - - virtual Handle(GeomEvaluator_Curve) ShallowCopy() const = 0; - - DEFINE_STANDARD_RTTI_INLINE(GeomEvaluator_Curve, Standard_Transient) -}; - -DEFINE_STANDARD_HANDLE(GeomEvaluator_Curve, Standard_Transient) - -#endif // _GeomEvaluator_Curve_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomEvaluator/GeomEvaluator_OffsetCurve.cxx b/src/ModelingData/TKG3d/GeomEvaluator/GeomEvaluator_OffsetCurve.cxx deleted file mode 100644 index 2e04c94af2..0000000000 --- a/src/ModelingData/TKG3d/GeomEvaluator/GeomEvaluator_OffsetCurve.cxx +++ /dev/null @@ -1,466 +0,0 @@ -// Created on: 2015-09-21 -// Copyright (c) 2015 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. - -#include - -#include -#include - -IMPLEMENT_STANDARD_RTTIEXT(GeomEvaluator_OffsetCurve, GeomEvaluator_Curve) - -GeomEvaluator_OffsetCurve::GeomEvaluator_OffsetCurve(const Handle(Geom_Curve)& theBase, - const Standard_Real theOffset, - const gp_Dir& theDirection) - : GeomEvaluator_Curve(), - myBaseCurve(theBase), - myOffset(theOffset), - myOffsetDir(theDirection) -{ -} - -GeomEvaluator_OffsetCurve::GeomEvaluator_OffsetCurve(const Handle(GeomAdaptor_Curve)& theBase, - const Standard_Real theOffset, - const gp_Dir& theDirection) - : GeomEvaluator_Curve(), - myBaseAdaptor(theBase), - myOffset(theOffset), - myOffsetDir(theDirection) -{ -} - -void GeomEvaluator_OffsetCurve::D0(const Standard_Real theU, gp_Pnt& theValue) const -{ - gp_Vec aD1; - BaseD1(theU, theValue, aD1); - CalculateD0(theValue, aD1); -} - -void GeomEvaluator_OffsetCurve::D1(const Standard_Real theU, gp_Pnt& theValue, gp_Vec& theD1) const -{ - gp_Vec aD2; - BaseD2(theU, theValue, theD1, aD2); - CalculateD1(theValue, theD1, aD2); -} - -void GeomEvaluator_OffsetCurve::D2(const Standard_Real theU, - gp_Pnt& theValue, - gp_Vec& theD1, - gp_Vec& theD2) const -{ - gp_Vec aD3; - BaseD3(theU, theValue, theD1, theD2, aD3); - - Standard_Boolean isDirectionChange = Standard_False; - if (theD1.SquareMagnitude() <= gp::Resolution()) - { - gp_Vec aDummyD4; - isDirectionChange = AdjustDerivative(3, theU, theD1, theD2, aD3, aDummyD4); - } - - CalculateD2(theValue, theD1, theD2, aD3, isDirectionChange); -} - -void GeomEvaluator_OffsetCurve::D3(const Standard_Real theU, - gp_Pnt& theValue, - gp_Vec& theD1, - gp_Vec& theD2, - gp_Vec& theD3) const -{ - gp_Vec aD4; - BaseD4(theU, theValue, theD1, theD2, theD3, aD4); - - Standard_Boolean isDirectionChange = Standard_False; - if (theD1.SquareMagnitude() <= gp::Resolution()) - isDirectionChange = AdjustDerivative(4, theU, theD1, theD2, theD3, aD4); - - CalculateD3(theValue, theD1, theD2, theD3, aD4, isDirectionChange); -} - -gp_Vec GeomEvaluator_OffsetCurve::DN(const Standard_Real theU, - const Standard_Integer theDeriv) const -{ - Standard_RangeError_Raise_if(theDeriv < 1, "GeomEvaluator_OffsetCurve::DN(): theDeriv < 1"); - - gp_Pnt aPnt; - gp_Vec aDummy, aDN; - switch (theDeriv) - { - case 1: - D1(theU, aPnt, aDN); - break; - case 2: - D2(theU, aPnt, aDummy, aDN); - break; - case 3: - D3(theU, aPnt, aDummy, aDummy, aDN); - break; - default: - aDN = BaseDN(theU, theDeriv); - } - return aDN; -} - -Handle(GeomEvaluator_Curve) GeomEvaluator_OffsetCurve::ShallowCopy() const -{ - Handle(GeomEvaluator_OffsetCurve) aCopy; - if (!myBaseAdaptor.IsNull()) - { - aCopy = new GeomEvaluator_OffsetCurve( - Handle(GeomAdaptor_Curve)::DownCast(myBaseAdaptor->ShallowCopy()), - myOffset, - myOffsetDir); - } - else - { - aCopy = new GeomEvaluator_OffsetCurve(myBaseCurve, myOffset, myOffsetDir); - } - return aCopy; -} - -void GeomEvaluator_OffsetCurve::BaseD0(const Standard_Real theU, gp_Pnt& theValue) const -{ - if (!myBaseAdaptor.IsNull()) - myBaseAdaptor->D0(theU, theValue); - else - myBaseCurve->D0(theU, theValue); -} - -void GeomEvaluator_OffsetCurve::BaseD1(const Standard_Real theU, - gp_Pnt& theValue, - gp_Vec& theD1) const -{ - if (!myBaseAdaptor.IsNull()) - myBaseAdaptor->D1(theU, theValue, theD1); - else - myBaseCurve->D1(theU, theValue, theD1); -} - -void GeomEvaluator_OffsetCurve::BaseD2(const Standard_Real theU, - gp_Pnt& theValue, - gp_Vec& theD1, - gp_Vec& theD2) const -{ - if (!myBaseAdaptor.IsNull()) - myBaseAdaptor->D2(theU, theValue, theD1, theD2); - else - myBaseCurve->D2(theU, theValue, theD1, theD2); -} - -void GeomEvaluator_OffsetCurve::BaseD3(const Standard_Real theU, - gp_Pnt& theValue, - gp_Vec& theD1, - gp_Vec& theD2, - gp_Vec& theD3) const -{ - if (!myBaseAdaptor.IsNull()) - myBaseAdaptor->D3(theU, theValue, theD1, theD2, theD3); - else - myBaseCurve->D3(theU, theValue, theD1, theD2, theD3); -} - -void GeomEvaluator_OffsetCurve::BaseD4(const Standard_Real theU, - gp_Pnt& theValue, - gp_Vec& theD1, - gp_Vec& theD2, - gp_Vec& theD3, - gp_Vec& theD4) const -{ - if (!myBaseAdaptor.IsNull()) - { - myBaseAdaptor->D3(theU, theValue, theD1, theD2, theD3); - theD4 = myBaseAdaptor->DN(theU, 4); - } - else - { - myBaseCurve->D3(theU, theValue, theD1, theD2, theD3); - theD4 = myBaseCurve->DN(theU, 4); - } -} - -gp_Vec GeomEvaluator_OffsetCurve::BaseDN(const Standard_Real theU, - const Standard_Integer theDeriv) const -{ - if (!myBaseAdaptor.IsNull()) - return myBaseAdaptor->DN(theU, theDeriv); - return myBaseCurve->DN(theU, theDeriv); -} - -void GeomEvaluator_OffsetCurve::CalculateD0(gp_Pnt& theValue, const gp_Vec& theD1) const -{ - gp_XYZ Ndir = (theD1.XYZ()).Crossed(myOffsetDir.XYZ()); - Standard_Real R = Ndir.Modulus(); - if (R <= gp::Resolution()) - throw Standard_NullValue("GeomEvaluator_OffsetCurve: Undefined normal vector " - "because tangent vector has zero-magnitude!"); - - Ndir.Multiply(myOffset / R); - theValue.ChangeCoord().Add(Ndir); -} - -void GeomEvaluator_OffsetCurve::CalculateD1(gp_Pnt& theValue, - gp_Vec& theD1, - const gp_Vec& theD2) const -{ - // P(u) = p(u) + Offset * Ndir / R - // with R = || p' ^ V|| and Ndir = P' ^ direction (local normal direction) - - // P'(u) = p'(u) + (Offset / R**2) * (DNdir/DU * R - Ndir * (DR/R)) - - gp_XYZ Ndir = (theD1.XYZ()).Crossed(myOffsetDir.XYZ()); - gp_XYZ DNdir = (theD2.XYZ()).Crossed(myOffsetDir.XYZ()); - Standard_Real R2 = Ndir.SquareModulus(); - Standard_Real R = std::sqrt(R2); - Standard_Real R3 = R * R2; - Standard_Real Dr = Ndir.Dot(DNdir); - if (R3 <= gp::Resolution()) - { - if (R2 <= gp::Resolution()) - throw Standard_NullValue("GeomEvaluator_OffsetCurve: Null derivative"); - // We try another computation but the stability is not very good. - DNdir.Multiply(R); - DNdir.Subtract(Ndir.Multiplied(Dr / R)); - DNdir.Multiply(myOffset / R2); - } - else - { - // Same computation as IICURV in EUCLID-IS because the stability is better - DNdir.Multiply(myOffset / R); - DNdir.Subtract(Ndir.Multiplied(myOffset * Dr / R3)); - } - - Ndir.Multiply(myOffset / R); - // P(u) - theValue.ChangeCoord().Add(Ndir); - // P'(u) - theD1.Add(gp_Vec(DNdir)); -} - -void GeomEvaluator_OffsetCurve::CalculateD2(gp_Pnt& theValue, - gp_Vec& theD1, - gp_Vec& theD2, - const gp_Vec& theD3, - const Standard_Boolean theIsDirChange) const -{ - // P(u) = p(u) + Offset * Ndir / R - // with R = || p' ^ V|| and Ndir = P' ^ direction (local normal direction) - - // P'(u) = p'(u) + (Offset / R**2) * (DNdir/DU * R - Ndir * (DR/R)) - - // P"(u) = p"(u) + (Offset / R) * (D2Ndir/DU - DNdir * (2.0 * Dr/ R**2) + - // Ndir * ( (3.0 * Dr**2 / R**4) - (D2r / R**2))) - - gp_XYZ Ndir = (theD1.XYZ()).Crossed(myOffsetDir.XYZ()); - gp_XYZ DNdir = (theD2.XYZ()).Crossed(myOffsetDir.XYZ()); - gp_XYZ D2Ndir = (theD3.XYZ()).Crossed(myOffsetDir.XYZ()); - Standard_Real R2 = Ndir.SquareModulus(); - Standard_Real R = std::sqrt(R2); - Standard_Real R3 = R2 * R; - Standard_Real R4 = R2 * R2; - Standard_Real R5 = R3 * R2; - Standard_Real Dr = Ndir.Dot(DNdir); - Standard_Real D2r = Ndir.Dot(D2Ndir) + DNdir.Dot(DNdir); - - if (R5 <= gp::Resolution()) - { - if (R4 <= gp::Resolution()) - throw Standard_NullValue("GeomEvaluator_OffsetCurve: Null derivative"); - // We try another computation but the stability is not very good - // dixit ISG. - // V2 = P" (U) : - R4 = R2 * R2; - D2Ndir.Subtract(DNdir.Multiplied(2.0 * Dr / R2)); - D2Ndir.Add(Ndir.Multiplied(((3.0 * Dr * Dr) / R4) - (D2r / R2))); - D2Ndir.Multiply(myOffset / R); - - // V1 = P' (U) : - DNdir.Multiply(R); - DNdir.Subtract(Ndir.Multiplied(Dr / R)); - DNdir.Multiply(myOffset / R2); - } - else - { - // Same computation as IICURV in EUCLID-IS because the stability is better. - // V2 = P" (U) : - D2Ndir.Multiply(myOffset / R); - D2Ndir.Subtract(DNdir.Multiplied(2.0 * myOffset * Dr / R3)); - D2Ndir.Add(Ndir.Multiplied(myOffset * (((3.0 * Dr * Dr) / R5) - (D2r / R3)))); - - // V1 = P' (U) : - DNdir.Multiply(myOffset / R); - DNdir.Subtract(Ndir.Multiplied(myOffset * Dr / R3)); - } - - Ndir.Multiply(myOffset / R); - // P(u) - theValue.ChangeCoord().Add(Ndir); - // P'(u) : - theD1.Add(gp_Vec(DNdir)); - // P"(u) : - if (theIsDirChange) - theD2.Reverse(); - theD2.Add(gp_Vec(D2Ndir)); -} - -void GeomEvaluator_OffsetCurve::CalculateD3(gp_Pnt& theValue, - gp_Vec& theD1, - gp_Vec& theD2, - gp_Vec& theD3, - const gp_Vec& theD4, - const Standard_Boolean theIsDirChange) const -{ - // P(u) = p(u) + Offset * Ndir / R - // with R = || p' ^ V|| and Ndir = P' ^ direction (local normal direction) - - // P'(u) = p'(u) + (Offset / R**2) * (DNdir/DU * R - Ndir * (DR/R)) - - // P"(u) = p"(u) + (Offset / R) * (D2Ndir/DU - DNdir * (2.0 * Dr/ R**2) + - // Ndir * ( (3.0 * Dr**2 / R**4) - (D2r / R**2))) - - // P"'(u) = p"'(u) + (Offset / R) * (D3Ndir - (3.0 * Dr/R**2) * D2Ndir - - // (3.0 * D2r / R2) * DNdir + (3.0 * Dr * Dr / R4) * DNdir - - // (D3r/R2) * Ndir + (6.0 * Dr * Dr / R4) * Ndir + - // (6.0 * Dr * D2r / R4) * Ndir - (15.0 * Dr* Dr* Dr /R6) * Ndir - - gp_XYZ Ndir = (theD1.XYZ()).Crossed(myOffsetDir.XYZ()); - gp_XYZ DNdir = (theD2.XYZ()).Crossed(myOffsetDir.XYZ()); - gp_XYZ D2Ndir = (theD3.XYZ()).Crossed(myOffsetDir.XYZ()); - gp_XYZ D3Ndir = (theD4.XYZ()).Crossed(myOffsetDir.XYZ()); - Standard_Real R2 = Ndir.SquareModulus(); - Standard_Real R = std::sqrt(R2); - Standard_Real R3 = R2 * R; - Standard_Real R4 = R2 * R2; - Standard_Real R5 = R3 * R2; - Standard_Real R6 = R3 * R3; - Standard_Real R7 = R5 * R2; - Standard_Real Dr = Ndir.Dot(DNdir); - Standard_Real D2r = Ndir.Dot(D2Ndir) + DNdir.Dot(DNdir); - Standard_Real D3r = Ndir.Dot(D3Ndir) + 3.0 * DNdir.Dot(D2Ndir); - if (R7 <= gp::Resolution()) - { - if (R6 <= gp::Resolution()) - throw Standard_NullValue("CSLib_Offset: Null derivative"); - // V3 = P"' (U) : - D3Ndir.Subtract(D2Ndir.Multiplied(3.0 * Dr / R2)); - D3Ndir.Subtract(DNdir.Multiplied(3.0 * ((D2r / R2) + (Dr * Dr / R4)))); - D3Ndir.Add( - Ndir.Multiplied(6.0 * Dr * Dr / R4 + 6.0 * Dr * D2r / R4 - 15.0 * Dr * Dr * Dr / R6 - D3r)); - D3Ndir.Multiply(myOffset / R); - // V2 = P" (U) : - R4 = R2 * R2; - D2Ndir.Subtract(DNdir.Multiplied(2.0 * Dr / R2)); - D2Ndir.Subtract(Ndir.Multiplied((3.0 * Dr * Dr / R4) - (D2r / R2))); - D2Ndir.Multiply(myOffset / R); - // V1 = P' (U) : - DNdir.Multiply(R); - DNdir.Subtract(Ndir.Multiplied(Dr / R)); - DNdir.Multiply(myOffset / R2); - } - else - { - // V3 = P"' (U) : - D3Ndir.Divide(R); - D3Ndir.Subtract(D2Ndir.Multiplied(3.0 * Dr / R3)); - D3Ndir.Subtract(DNdir.Multiplied((3.0 * ((D2r / R3) + (Dr * Dr) / R5)))); - D3Ndir.Add( - Ndir.Multiplied(6.0 * Dr * Dr / R5 + 6.0 * Dr * D2r / R5 - 15.0 * Dr * Dr * Dr / R7 - D3r)); - D3Ndir.Multiply(myOffset); - // V2 = P" (U) : - D2Ndir.Divide(R); - D2Ndir.Subtract(DNdir.Multiplied(2.0 * Dr / R3)); - D2Ndir.Subtract(Ndir.Multiplied((3.0 * Dr * Dr / R5) - (D2r / R3))); - D2Ndir.Multiply(myOffset); - // V1 = P' (U) : - DNdir.Multiply(myOffset / R); - DNdir.Subtract(Ndir.Multiplied(myOffset * Dr / R3)); - } - - Ndir.Multiply(myOffset / R); - // P(u) - theValue.ChangeCoord().Add(Ndir); - // P'(u) : - theD1.Add(gp_Vec(DNdir)); - // P"(u) - theD2.Add(gp_Vec(D2Ndir)); - // P"'(u) - if (theIsDirChange) - theD3.Reverse(); - theD3.Add(gp_Vec(D2Ndir)); -} - -Standard_Boolean GeomEvaluator_OffsetCurve::AdjustDerivative( - const Standard_Integer theMaxDerivative, - const Standard_Real theU, - gp_Vec& theD1, - gp_Vec& theD2, - gp_Vec& theD3, - gp_Vec& theD4) const -{ - static const Standard_Real aTol = gp::Resolution(); - static const Standard_Real aMinStep = 1e-7; - static const Standard_Integer aMaxDerivOrder = 3; - - Standard_Boolean isDirectionChange = Standard_False; - Standard_Real anUinfium; - Standard_Real anUsupremum; - if (!myBaseAdaptor.IsNull()) - { - anUinfium = myBaseAdaptor->FirstParameter(); - anUsupremum = myBaseAdaptor->LastParameter(); - } - else - { - anUinfium = myBaseCurve->FirstParameter(); - anUsupremum = myBaseCurve->LastParameter(); - } - - static const Standard_Real DivisionFactor = 1.e-3; - Standard_Real du; - if ((anUsupremum >= RealLast()) || (anUinfium <= RealFirst())) - du = 0.0; - else - du = anUsupremum - anUinfium; - - const Standard_Real aDelta = std::max(du * DivisionFactor, aMinStep); - - // Derivative is approximated by Taylor-series - Standard_Integer anIndex = 1; // Derivative order - gp_Vec V; - - do - { - V = BaseDN(theU, ++anIndex); - } while ((V.SquareMagnitude() <= aTol) && anIndex < aMaxDerivOrder); - - Standard_Real u; - - if (theU - anUinfium < aDelta) - u = theU + aDelta; - else - u = theU - aDelta; - - gp_Pnt P1, P2; - BaseD0(std::min(theU, u), P1); - BaseD0(std::max(theU, u), P2); - - gp_Vec V1(P1, P2); - isDirectionChange = V.Dot(V1) < 0.0; - Standard_Real aSign = isDirectionChange ? -1.0 : 1.0; - - theD1 = V * aSign; - gp_Vec* aDeriv[3] = {&theD2, &theD3, &theD4}; - for (Standard_Integer i = 1; i < theMaxDerivative; i++) - *(aDeriv[i - 1]) = BaseDN(theU, anIndex + i) * aSign; - - return isDirectionChange; -} diff --git a/src/ModelingData/TKG3d/GeomEvaluator/GeomEvaluator_OffsetCurve.hxx b/src/ModelingData/TKG3d/GeomEvaluator/GeomEvaluator_OffsetCurve.hxx deleted file mode 100644 index 22a9e13550..0000000000 --- a/src/ModelingData/TKG3d/GeomEvaluator/GeomEvaluator_OffsetCurve.hxx +++ /dev/null @@ -1,125 +0,0 @@ -// Created on: 2015-09-21 -// Copyright (c) 2015 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 _GeomEvaluator_OffsetCurve_HeaderFile -#define _GeomEvaluator_OffsetCurve_HeaderFile - -#include -#include -#include - -//! Allows to calculate values and derivatives for offset curves in 3D -class GeomEvaluator_OffsetCurve : public GeomEvaluator_Curve -{ -public: - //! Initialize evaluator by curve - Standard_EXPORT GeomEvaluator_OffsetCurve(const Handle(Geom_Curve)& theBase, - const Standard_Real theOffset, - const gp_Dir& theDirection); - //! Initialize evaluator by curve adaptor - Standard_EXPORT GeomEvaluator_OffsetCurve(const Handle(GeomAdaptor_Curve)& theBase, - const Standard_Real theOffset, - const gp_Dir& theDirection); - - //! Change the offset value - void SetOffsetValue(Standard_Real theOffset) { myOffset = theOffset; } - - void SetOffsetDirection(const gp_Dir& theDirection) { myOffsetDir = theDirection; } - - //! Value of curve - Standard_EXPORT void D0(const Standard_Real theU, gp_Pnt& theValue) const Standard_OVERRIDE; - //! Value and first derivatives of curve - Standard_EXPORT void D1(const Standard_Real theU, - gp_Pnt& theValue, - gp_Vec& theD1) const Standard_OVERRIDE; - //! Value, first and second derivatives of curve - Standard_EXPORT void D2(const Standard_Real theU, - gp_Pnt& theValue, - gp_Vec& theD1, - gp_Vec& theD2) const Standard_OVERRIDE; - //! Value, first, second and third derivatives of curve - Standard_EXPORT void D3(const Standard_Real theU, - gp_Pnt& theValue, - gp_Vec& theD1, - gp_Vec& theD2, - gp_Vec& theD3) const Standard_OVERRIDE; - //! Calculates N-th derivatives of curve, where N = theDeriv. Raises if N < 1 - Standard_EXPORT gp_Vec DN(const Standard_Real theU, - const Standard_Integer theDeriv) const Standard_OVERRIDE; - - Standard_EXPORT virtual Handle(GeomEvaluator_Curve) ShallowCopy() const Standard_OVERRIDE; - - DEFINE_STANDARD_RTTIEXT(GeomEvaluator_OffsetCurve, GeomEvaluator_Curve) - -private: - //! Recalculate D1 values of base curve into D0 value of offset curve - void CalculateD0(gp_Pnt& theValue, const gp_Vec& theD1) const; - //! Recalculate D2 values of base curve into D1 values of offset curve - void CalculateD1(gp_Pnt& theValue, gp_Vec& theD1, const gp_Vec& theD2) const; - //! Recalculate D3 values of base curve into D2 values of offset curve - void CalculateD2(gp_Pnt& theValue, - gp_Vec& theD1, - gp_Vec& theD2, - const gp_Vec& theD3, - const Standard_Boolean theIsDirChange) const; - //! Recalculate D3 values of base curve into D3 values of offset curve - void CalculateD3(gp_Pnt& theValue, - gp_Vec& theD1, - gp_Vec& theD2, - gp_Vec& theD3, - const gp_Vec& theD4, - const Standard_Boolean theIsDirChange) const; - - //! Calculate value of base curve/adaptor - void BaseD0(const Standard_Real theU, gp_Pnt& theValue) const; - //! Calculate value and first derivatives of base curve/adaptor - void BaseD1(const Standard_Real theU, gp_Pnt& theValue, gp_Vec& theD1) const; - //! Calculate value, first and second derivatives of base curve/adaptor - void BaseD2(const Standard_Real theU, gp_Pnt& theValue, gp_Vec& theD1, gp_Vec& theD2) const; - //! Calculate value, first, second and third derivatives of base curve/adaptor - void BaseD3(const Standard_Real theU, - gp_Pnt& theValue, - gp_Vec& theD1, - gp_Vec& theD2, - gp_Vec& theD3) const; - //! Calculate value and derivatives till 4th of base curve/adaptor - void BaseD4(const Standard_Real theU, - gp_Pnt& theValue, - gp_Vec& theD1, - gp_Vec& theD2, - gp_Vec& theD3, - gp_Vec& theD4) const; - //! Calculate N-th derivative of base curve/adaptor - gp_Vec BaseDN(const Standard_Real theU, const Standard_Integer theDeriv) const; - - // Recalculate derivatives in the singular point - // Returns true if the direction of derivatives is changed - Standard_Boolean AdjustDerivative(const Standard_Integer theMaxDerivative, - const Standard_Real theU, - gp_Vec& theD1, - gp_Vec& theD2, - gp_Vec& theD3, - gp_Vec& theD4) const; - -private: - Handle(Geom_Curve) myBaseCurve; - Handle(GeomAdaptor_Curve) myBaseAdaptor; - - Standard_Real myOffset; ///< offset value - gp_Dir myOffsetDir; ///< offset direction -}; - -DEFINE_STANDARD_HANDLE(GeomEvaluator_OffsetCurve, GeomEvaluator_Curve) - -#endif // _GeomEvaluator_OffsetCurve_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomEvaluator/GeomEvaluator_OffsetSurface.cxx b/src/ModelingData/TKG3d/GeomEvaluator/GeomEvaluator_OffsetSurface.cxx deleted file mode 100644 index 954891a84f..0000000000 --- a/src/ModelingData/TKG3d/GeomEvaluator/GeomEvaluator_OffsetSurface.cxx +++ /dev/null @@ -1,1171 +0,0 @@ -// Created on: 2015-09-21 -// Copyright (c) 2015 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. - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -IMPLEMENT_STANDARD_RTTIEXT(GeomEvaluator_OffsetSurface, GeomEvaluator_Surface) - -namespace -{ - -// tolerance for considering derivative to be null -const Standard_Real the_D1MagTol = 1.e-9; - -// If calculation of normal fails, try shifting the point towards the center -// of the parametric space of the surface, in the hope that derivatives -// are better defined there. -// -// This shift is iterative, starting with Precision::PConfusion() -// and increasing by multiple of 2 on each step. -// -// NB: temporarily this is made as static function and not class method, -// hence code duplications -static Standard_Boolean shiftPoint(const Standard_Real theUStart, - const Standard_Real theVStart, - Standard_Real& theU, - Standard_Real& theV, - const Handle(Geom_Surface)& theSurf, - const Handle(GeomAdaptor_Surface)& theAdaptor, - const gp_Vec& theD1U, - const gp_Vec& theD1V) -{ - // Get parametric bounds and closure status - Standard_Real aUMin, aUMax, aVMin, aVMax; - Standard_Boolean isUPeriodic, isVPeriodic; - if (!theSurf.IsNull()) - { - theSurf->Bounds(aUMin, aUMax, aVMin, aVMax); - isUPeriodic = theSurf->IsUPeriodic(); - isVPeriodic = theSurf->IsVPeriodic(); - } - else - { - aUMin = theAdaptor->FirstUParameter(); - aUMax = theAdaptor->LastUParameter(); - aVMin = theAdaptor->FirstVParameter(); - aVMax = theAdaptor->LastVParameter(); - isUPeriodic = theAdaptor->IsUPeriodic(); - isVPeriodic = theAdaptor->IsVPeriodic(); - } - - // check if either U or V is singular (normally one of them is) - Standard_Boolean isUSingular = (theD1U.SquareMagnitude() < the_D1MagTol * the_D1MagTol); - Standard_Boolean isVSingular = (theD1V.SquareMagnitude() < the_D1MagTol * the_D1MagTol); - - // compute vector to shift from start point to center of the surface; - // if surface is periodic or singular in some direction, take shift in that direction zero - Standard_Real aDirU = - (isUPeriodic || (isUSingular && !isVSingular) ? 0. : 0.5 * (aUMin + aUMax) - theUStart); - Standard_Real aDirV = - (isVPeriodic || (isVSingular && !isUSingular) ? 0. : 0.5 * (aVMin + aVMax) - theVStart); - Standard_Real aDist = std::sqrt(aDirU * aDirU + aDirV * aDirV); - - // shift current point from its current position towards center, by value of twice - // current distance from it to start (but not less than Precision::PConfusion()); - // fail if center is overpassed. - Standard_Real aDU = theU - theUStart; - Standard_Real aDV = theV - theVStart; - Standard_Real aStep = std::max(2. * std::sqrt(aDU * aDU + aDV * aDV), Precision::PConfusion()); - if (aStep >= aDist) - { - return Standard_False; - } - - aStep /= aDist; - theU += aDirU * aStep; - theV += aDirV * aStep; - - // std::cout << "Normal calculation failed at (" << theUStart << ", " << theVStart << "), - // shifting to (" << theU << ", " << theV << ")" << std::endl; - - return Standard_True; -} - -// auxiliary function -template -static void derivatives(Standard_Integer theMaxOrder, - Standard_Integer theMinOrder, - const Standard_Real theU, - const Standard_Real theV, - const SurfOrAdapt& theBasisSurf, - const Standard_Integer theNU, - const Standard_Integer theNV, - const Standard_Boolean theAlongU, - const Standard_Boolean theAlongV, - const Handle(Geom_BSplineSurface)& theL, - TColgp_Array2OfVec& theDerNUV, - TColgp_Array2OfVec& theDerSurf) -{ - Standard_Integer i, j; - gp_Pnt P; - gp_Vec DL1U, DL1V, DL2U, DL2V, DL2UV, DL3U, DL3UUV, DL3UVV, DL3V; - - if (theAlongU || theAlongV) - { - theMaxOrder = 0; - TColgp_Array2OfVec DerSurfL(0, theMaxOrder + theNU + 1, 0, theMaxOrder + theNV + 1); - switch (theMinOrder) - { - case 1: - theL->D1(theU, theV, P, DL1U, DL1V); - DerSurfL.SetValue(1, 0, DL1U); - DerSurfL.SetValue(0, 1, DL1V); - break; - case 2: - theL->D2(theU, theV, P, DL1U, DL1V, DL2U, DL2V, DL2UV); - DerSurfL.SetValue(1, 0, DL1U); - DerSurfL.SetValue(0, 1, DL1V); - DerSurfL.SetValue(1, 1, DL2UV); - DerSurfL.SetValue(2, 0, DL2U); - DerSurfL.SetValue(0, 2, DL2V); - break; - case 3: - theL->D3(theU, theV, P, DL1U, DL1V, DL2U, DL2V, DL2UV, DL3U, DL3V, DL3UUV, DL3UVV); - DerSurfL.SetValue(1, 0, DL1U); - DerSurfL.SetValue(0, 1, DL1V); - DerSurfL.SetValue(1, 1, DL2UV); - DerSurfL.SetValue(2, 0, DL2U); - DerSurfL.SetValue(0, 2, DL2V); - DerSurfL.SetValue(3, 0, DL3U); - DerSurfL.SetValue(2, 1, DL3UUV); - DerSurfL.SetValue(1, 2, DL3UVV); - DerSurfL.SetValue(0, 3, DL3V); - break; - default: - break; - } - - if (theNU <= theNV) - { - for (i = 0; i <= theMaxOrder + 1 + theNU; i++) - for (j = i; j <= theMaxOrder + theNV + 1; j++) - if (i + j > theMinOrder) - { - DerSurfL.SetValue(i, j, theL->DN(theU, theV, i, j)); - theDerSurf.SetValue(i, j, theBasisSurf->DN(theU, theV, i, j)); - if (i != j && j <= theNU + 1) - { - theDerSurf.SetValue(j, i, theBasisSurf->DN(theU, theV, j, i)); - DerSurfL.SetValue(j, i, theL->DN(theU, theV, j, i)); - } - } - } - else - { - for (j = 0; j <= theMaxOrder + 1 + theNV; j++) - for (i = j; i <= theMaxOrder + theNU + 1; i++) - if (i + j > theMinOrder) - { - DerSurfL.SetValue(i, j, theL->DN(theU, theV, i, j)); - theDerSurf.SetValue(i, j, theBasisSurf->DN(theU, theV, i, j)); - if (i != j && i <= theNV + 1) - { - theDerSurf.SetValue(j, i, theBasisSurf->DN(theU, theV, j, i)); - DerSurfL.SetValue(j, i, theL->DN(theU, theV, j, i)); - } - } - } - for (i = 0; i <= theMaxOrder + theNU; i++) - for (j = 0; j <= theMaxOrder + theNV; j++) - { - if (theAlongU) - theDerNUV.SetValue(i, j, CSLib::DNNUV(i, j, DerSurfL, theDerSurf)); - if (theAlongV) - theDerNUV.SetValue(i, j, CSLib::DNNUV(i, j, theDerSurf, DerSurfL)); - } - } - else - { - for (i = 0; i <= theMaxOrder + theNU + 1; i++) - { - for (j = i; j <= theMaxOrder + theNV + 1; j++) - { - if (i + j > theMinOrder) - { - theDerSurf.SetValue(i, j, theBasisSurf->DN(theU, theV, i, j)); - if (i != j && j <= theDerSurf.UpperRow() && i <= theDerSurf.UpperCol()) - { - theDerSurf.SetValue(j, i, theBasisSurf->DN(theU, theV, j, i)); - } - } - } - } - for (i = 0; i <= theMaxOrder + theNU; i++) - for (j = 0; j <= theMaxOrder + theNV; j++) - theDerNUV.SetValue(i, j, CSLib::DNNUV(i, j, theDerSurf)); - } -} - -inline Standard_Boolean IsInfiniteCoord(const gp_Vec& theVec) -{ - return Precision::IsInfinite(theVec.X()) || Precision::IsInfinite(theVec.Y()) - || Precision::IsInfinite(theVec.Z()); -} - -inline void CheckInfinite(const gp_Vec& theVecU, const gp_Vec& theVecV) -{ - if (IsInfiniteCoord(theVecU) || IsInfiniteCoord(theVecV)) - { - throw Standard_NumericError("GeomEvaluator_OffsetSurface: Evaluation of infinite parameters"); - } -} - -} // end of anonymous namespace - -GeomEvaluator_OffsetSurface::GeomEvaluator_OffsetSurface( - const Handle(Geom_Surface)& theBase, - const Standard_Real theOffset, - const Handle(Geom_OsculatingSurface)& theOscSurf) - : GeomEvaluator_Surface(), - myBaseSurf(theBase), - myOffset(theOffset), - myOscSurf(theOscSurf) -{ - if (!myOscSurf.IsNull()) - return; // osculating surface already exists - - // Create osculating surface for B-spline and Besier surfaces only - if (myBaseSurf->IsKind(STANDARD_TYPE(Geom_BSplineSurface)) - || myBaseSurf->IsKind(STANDARD_TYPE(Geom_BezierSurface))) - myOscSurf = new Geom_OsculatingSurface(myBaseSurf, Precision::Confusion()); -} - -GeomEvaluator_OffsetSurface::GeomEvaluator_OffsetSurface( - const Handle(GeomAdaptor_Surface)& theBase, - const Standard_Real theOffset, - const Handle(Geom_OsculatingSurface)& theOscSurf) - : GeomEvaluator_Surface(), - myBaseAdaptor(theBase), - myOffset(theOffset), - myOscSurf(theOscSurf) -{ -} - -void GeomEvaluator_OffsetSurface::D0(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue) const -{ - Standard_Real aU = theU, aV = theV; - for (;;) - { - gp_Vec aD1U, aD1V; - BaseD1(aU, aV, theValue, aD1U, aD1V); - - CheckInfinite(aD1U, aD1V); - - try - { - CalculateD0(aU, aV, theValue, aD1U, aD1V); - break; - } - catch (Geom_UndefinedValue&) - { - // if failed at parametric boundary, try taking derivative at shifted point - if (!shiftPoint(theU, theV, aU, aV, myBaseSurf, myBaseAdaptor, aD1U, aD1V)) - { - throw; - } - } - } -} - -void GeomEvaluator_OffsetSurface::D1(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V) const -{ - Standard_Real aU = theU, aV = theV; - for (;;) - { - gp_Vec aD2U, aD2V, aD2UV; - BaseD2(aU, aV, theValue, theD1U, theD1V, aD2U, aD2V, aD2UV); - - CheckInfinite(theD1U, theD1V); - - try - { - CalculateD1(aU, aV, theValue, theD1U, theD1V, aD2U, aD2V, aD2UV); - break; - } - catch (Geom_UndefinedValue&) - { - // if failed at parametric boundary, try taking derivative at shifted point - if (!shiftPoint(theU, theV, aU, aV, myBaseSurf, myBaseAdaptor, theD1U, theD1V)) - { - throw; - } - } - } -} - -void GeomEvaluator_OffsetSurface::D2(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V, - gp_Vec& theD2U, - gp_Vec& theD2V, - gp_Vec& theD2UV) const -{ - Standard_Real aU = theU, aV = theV; - for (;;) - { - gp_Vec aD3U, aD3V, aD3UUV, aD3UVV; - BaseD3(aU, aV, theValue, theD1U, theD1V, theD2U, theD2V, theD2UV, aD3U, aD3V, aD3UUV, aD3UVV); - - CheckInfinite(theD1U, theD1V); - - try - { - CalculateD2(aU, - aV, - theValue, - theD1U, - theD1V, - theD2U, - theD2V, - theD2UV, - aD3U, - aD3V, - aD3UUV, - aD3UVV); - break; - } - catch (Geom_UndefinedValue&) - { - // if failed at parametric boundary, try taking derivative at shifted point - if (!shiftPoint(theU, theV, aU, aV, myBaseSurf, myBaseAdaptor, theD1U, theD1V)) - { - throw; - } - } - } -} - -void GeomEvaluator_OffsetSurface::D3(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V, - gp_Vec& theD2U, - gp_Vec& theD2V, - gp_Vec& theD2UV, - gp_Vec& theD3U, - gp_Vec& theD3V, - gp_Vec& theD3UUV, - gp_Vec& theD3UVV) const -{ - Standard_Real aU = theU, aV = theV; - for (;;) - { - BaseD3(aU, - aV, - theValue, - theD1U, - theD1V, - theD2U, - theD2V, - theD2UV, - theD3U, - theD3V, - theD3UUV, - theD3UVV); - - CheckInfinite(theD1U, theD1V); - - try - { - CalculateD3(aU, - aV, - theValue, - theD1U, - theD1V, - theD2U, - theD2V, - theD2UV, - theD3U, - theD3V, - theD3UUV, - theD3UVV); - break; - } - catch (Geom_UndefinedValue&) - { - // if failed at parametric boundary, try taking derivative at shifted point - if (!shiftPoint(theU, theV, aU, aV, myBaseSurf, myBaseAdaptor, theD1U, theD1V)) - { - throw; - } - } - } -} - -gp_Vec GeomEvaluator_OffsetSurface::DN(const Standard_Real theU, - const Standard_Real theV, - const Standard_Integer theDerU, - const Standard_Integer theDerV) const -{ - Standard_RangeError_Raise_if(theDerU < 0, "GeomEvaluator_OffsetSurface::DN(): theDerU < 0"); - Standard_RangeError_Raise_if(theDerV < 0, "GeomEvaluator_OffsetSurface::DN(): theDerV < 0"); - Standard_RangeError_Raise_if(theDerU + theDerV < 1, - "GeomEvaluator_OffsetSurface::DN(): theDerU + theDerV < 1"); - - Standard_Real aU = theU, aV = theV; - for (;;) - { - gp_Pnt aP; - gp_Vec aD1U, aD1V; - BaseD1(aU, aV, aP, aD1U, aD1V); - - CheckInfinite(aD1U, aD1V); - - try - { - return CalculateDN(aU, aV, theDerU, theDerV, aD1U, aD1V); - } - catch (Geom_UndefinedValue&) - { - // if failed at parametric boundary, try taking derivative at shifted point - if (!shiftPoint(theU, theV, aU, aV, myBaseSurf, myBaseAdaptor, aD1U, aD1V)) - { - throw; - } - } - } -} - -Handle(GeomEvaluator_Surface) GeomEvaluator_OffsetSurface::ShallowCopy() const -{ - Handle(GeomEvaluator_OffsetSurface) aCopy; - if (!myBaseAdaptor.IsNull()) - { - aCopy = new GeomEvaluator_OffsetSurface( - Handle(GeomAdaptor_Surface)::DownCast(myBaseAdaptor->ShallowCopy()), - myOffset, - myOscSurf); - } - else - { - aCopy = new GeomEvaluator_OffsetSurface(myBaseSurf, myOffset, myOscSurf); - } - - return aCopy; -} - -void GeomEvaluator_OffsetSurface::BaseD0(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue) const -{ - if (!myBaseAdaptor.IsNull()) - myBaseAdaptor->D0(theU, theV, theValue); - else - myBaseSurf->D0(theU, theV, theValue); -} - -void GeomEvaluator_OffsetSurface::BaseD1(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V) const -{ - if (!myBaseAdaptor.IsNull()) - myBaseAdaptor->D1(theU, theV, theValue, theD1U, theD1V); - else - myBaseSurf->D1(theU, theV, theValue, theD1U, theD1V); -} - -void GeomEvaluator_OffsetSurface::BaseD2(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V, - gp_Vec& theD2U, - gp_Vec& theD2V, - gp_Vec& theD2UV) const -{ - if (!myBaseAdaptor.IsNull()) - myBaseAdaptor->D2(theU, theV, theValue, theD1U, theD1V, theD2U, theD2V, theD2UV); - else - myBaseSurf->D2(theU, theV, theValue, theD1U, theD1V, theD2U, theD2V, theD2UV); -} - -void GeomEvaluator_OffsetSurface::BaseD3(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V, - gp_Vec& theD2U, - gp_Vec& theD2V, - gp_Vec& theD2UV, - gp_Vec& theD3U, - gp_Vec& theD3V, - gp_Vec& theD3UUV, - gp_Vec& theD3UVV) const -{ - if (!myBaseAdaptor.IsNull()) - myBaseAdaptor->D3(theU, - theV, - theValue, - theD1U, - theD1V, - theD2U, - theD2V, - theD2UV, - theD3U, - theD3V, - theD3UUV, - theD3UVV); - else - myBaseSurf->D3(theU, - theV, - theValue, - theD1U, - theD1V, - theD2U, - theD2V, - theD2UV, - theD3U, - theD3V, - theD3UUV, - theD3UVV); -} - -void GeomEvaluator_OffsetSurface::CalculateD0(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - const gp_Vec& theD1U, - const gp_Vec& theD1V) const -{ - // Normalize derivatives before normal calculation because it gives more stable result. - // There will be normalized only derivatives greater than 1.0 to avoid differences in last - // significant digit - gp_Vec aD1U(theD1U); - gp_Vec aD1V(theD1V); - Standard_Real aD1UNorm2 = aD1U.SquareMagnitude(); - Standard_Real aD1VNorm2 = aD1V.SquareMagnitude(); - if (aD1UNorm2 > 1.0) - aD1U /= std::sqrt(aD1UNorm2); - if (aD1VNorm2 > 1.0) - aD1V /= std::sqrt(aD1VNorm2); - - gp_Vec aNorm = aD1U.Crossed(aD1V); - if (aNorm.SquareMagnitude() > the_D1MagTol * the_D1MagTol) - { - // Non singular case. Simple computations. - aNorm.Normalize(); - theValue.SetXYZ(theValue.XYZ() + myOffset * aNorm.XYZ()); - } - else - { - const Standard_Integer MaxOrder = 3; - - Handle(Geom_BSplineSurface) L; - Standard_Boolean isOpposite = Standard_False; - Standard_Boolean AlongU = Standard_False; - Standard_Boolean AlongV = Standard_False; - if (!myOscSurf.IsNull()) - { - AlongU = myOscSurf->UOscSurf(theU, theV, isOpposite, L); - AlongV = myOscSurf->VOscSurf(theU, theV, isOpposite, L); - } - const Standard_Real aSign = ((AlongV || AlongU) && isOpposite) ? -1. : 1.; - - TColgp_Array2OfVec DerNUV(0, MaxOrder, 0, MaxOrder); - TColgp_Array2OfVec DerSurf(0, MaxOrder + 1, 0, MaxOrder + 1); - Standard_Integer OrderU, OrderV; - Standard_Real Umin = 0, Umax = 0, Vmin = 0, Vmax = 0; - Bounds(Umin, Umax, Vmin, Vmax); - - DerSurf.SetValue(1, 0, theD1U); - DerSurf.SetValue(0, 1, theD1V); - if (!myBaseSurf.IsNull()) - derivatives(MaxOrder, 1, theU, theV, myBaseSurf, 0, 0, AlongU, AlongV, L, DerNUV, DerSurf); - else - derivatives(MaxOrder, 1, theU, theV, myBaseAdaptor, 0, 0, AlongU, AlongV, L, DerNUV, DerSurf); - - gp_Dir Normal; - CSLib_NormalStatus NStatus = CSLib_Singular; - CSLib::Normal(MaxOrder, - DerNUV, - the_D1MagTol, - theU, - theV, - Umin, - Umax, - Vmin, - Vmax, - NStatus, - Normal, - OrderU, - OrderV); - if (NStatus == CSLib_InfinityOfSolutions) - { - // Replace zero derivative and try to calculate normal - gp_Vec aNewDU = theD1U; - gp_Vec aNewDV = theD1V; - if (ReplaceDerivative(theU, theV, aNewDU, aNewDV, the_D1MagTol * the_D1MagTol)) - CSLib::Normal(aNewDU, aNewDV, the_D1MagTol, NStatus, Normal); - } - - if (NStatus != CSLib_Defined) - throw Geom_UndefinedValue( - "GeomEvaluator_OffsetSurface::CalculateD0(): Unable to calculate normal"); - - theValue.SetXYZ(theValue.XYZ() + myOffset * aSign * Normal.XYZ()); - } -} - -void GeomEvaluator_OffsetSurface::CalculateD1(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V, - const gp_Vec& theD2U, - const gp_Vec& theD2V, - const gp_Vec& theD2UV) const -{ - // Check offset side. - Handle(Geom_BSplineSurface) L; - Standard_Boolean isOpposite = Standard_False; - Standard_Boolean AlongU = Standard_False; - Standard_Boolean AlongV = Standard_False; - - // Normalize derivatives before normal calculation because it gives more stable result. - // There will be normalized only derivatives greater than 1.0 to avoid differences in last - // significant digit - gp_Vec aD1U(theD1U); - gp_Vec aD1V(theD1V); - Standard_Real aD1UNorm2 = aD1U.SquareMagnitude(); - Standard_Real aD1VNorm2 = aD1V.SquareMagnitude(); - if (aD1UNorm2 > 1.0) - aD1U /= std::sqrt(aD1UNorm2); - if (aD1VNorm2 > 1.0) - aD1V /= std::sqrt(aD1VNorm2); - - Standard_Boolean isSingular = Standard_False; - Standard_Integer MaxOrder = 0; - gp_Vec aNorm = aD1U.Crossed(aD1V); - if (aNorm.SquareMagnitude() <= the_D1MagTol * the_D1MagTol) - { - if (!myOscSurf.IsNull()) - { - AlongU = myOscSurf->UOscSurf(theU, theV, isOpposite, L); - AlongV = myOscSurf->VOscSurf(theU, theV, isOpposite, L); - } - - MaxOrder = 3; - isSingular = Standard_True; - } - - const Standard_Real aSign = ((AlongV || AlongU) && isOpposite) ? -1. : 1.; - - if (!isSingular) - { - // AlongU or AlongV leads to more complex D1 computation - // Try to compute D0 and D1 much simpler - aNorm.Normalize(); - theValue.SetXYZ(theValue.XYZ() + myOffset * aSign * aNorm.XYZ()); - - gp_Vec aN0(aNorm.XYZ()), aN1U, aN1V; - Standard_Real aScale = (theD1U ^ theD1V).Dot(aN0); - aN1U.SetX(theD2U.Y() * theD1V.Z() + theD1U.Y() * theD2UV.Z() - theD2U.Z() * theD1V.Y() - - theD1U.Z() * theD2UV.Y()); - aN1U.SetY((theD2U.X() * theD1V.Z() + theD1U.X() * theD2UV.Z() - theD2U.Z() * theD1V.X() - - theD1U.Z() * theD2UV.X()) - * -1.0); - aN1U.SetZ(theD2U.X() * theD1V.Y() + theD1U.X() * theD2UV.Y() - theD2U.Y() * theD1V.X() - - theD1U.Y() * theD2UV.X()); - Standard_Real aScaleU = aN1U.Dot(aN0); - aN1U.Subtract(aScaleU * aN0); - aN1U /= aScale; - - aN1V.SetX(theD2UV.Y() * theD1V.Z() + theD2V.Z() * theD1U.Y() - theD2UV.Z() * theD1V.Y() - - theD2V.Y() * theD1U.Z()); - aN1V.SetY((theD2UV.X() * theD1V.Z() + theD2V.Z() * theD1U.X() - theD2UV.Z() * theD1V.X() - - theD2V.X() * theD1U.Z()) - * -1.0); - aN1V.SetZ(theD2UV.X() * theD1V.Y() + theD2V.Y() * theD1U.X() - theD2UV.Y() * theD1V.X() - - theD2V.X() * theD1U.Y()); - Standard_Real aScaleV = aN1V.Dot(aN0); - aN1V.Subtract(aScaleV * aN0); - aN1V /= aScale; - - theD1U += myOffset * aSign * aN1U; - theD1V += myOffset * aSign * aN1V; - - return; - } - - Standard_Integer OrderU, OrderV; - TColgp_Array2OfVec DerNUV(0, MaxOrder + 1, 0, MaxOrder + 1); - TColgp_Array2OfVec DerSurf(0, MaxOrder + 2, 0, MaxOrder + 2); - Standard_Real Umin = 0, Umax = 0, Vmin = 0, Vmax = 0; - Bounds(Umin, Umax, Vmin, Vmax); - - DerSurf.SetValue(1, 0, theD1U); - DerSurf.SetValue(0, 1, theD1V); - DerSurf.SetValue(1, 1, theD2UV); - DerSurf.SetValue(2, 0, theD2U); - DerSurf.SetValue(0, 2, theD2V); - if (!myBaseSurf.IsNull()) - derivatives(MaxOrder, 2, theU, theV, myBaseSurf, 1, 1, AlongU, AlongV, L, DerNUV, DerSurf); - else - derivatives(MaxOrder, 2, theU, theV, myBaseAdaptor, 1, 1, AlongU, AlongV, L, DerNUV, DerSurf); - - gp_Dir Normal; - CSLib_NormalStatus NStatus; - CSLib::Normal(MaxOrder, - DerNUV, - the_D1MagTol, - theU, - theV, - Umin, - Umax, - Vmin, - Vmax, - NStatus, - Normal, - OrderU, - OrderV); - if (NStatus == CSLib_InfinityOfSolutions) - { - gp_Vec aNewDU = theD1U; - gp_Vec aNewDV = theD1V; - // Replace zero derivative and try to calculate normal - if (ReplaceDerivative(theU, theV, aNewDU, aNewDV, the_D1MagTol * the_D1MagTol)) - { - DerSurf.SetValue(1, 0, aNewDU); - DerSurf.SetValue(0, 1, aNewDV); - if (!myBaseSurf.IsNull()) - derivatives(MaxOrder, 2, theU, theV, myBaseSurf, 1, 1, AlongU, AlongV, L, DerNUV, DerSurf); - else - derivatives(MaxOrder, - 2, - theU, - theV, - myBaseAdaptor, - 1, - 1, - AlongU, - AlongV, - L, - DerNUV, - DerSurf); - CSLib::Normal(MaxOrder, - DerNUV, - the_D1MagTol, - theU, - theV, - Umin, - Umax, - Vmin, - Vmax, - NStatus, - Normal, - OrderU, - OrderV); - } - } - - if (NStatus != CSLib_Defined) - throw Geom_UndefinedValue( - "GeomEvaluator_OffsetSurface::CalculateD1(): Unable to calculate normal"); - - theValue.SetXYZ(theValue.XYZ() + myOffset * aSign * Normal.XYZ()); - - theD1U = DerSurf(1, 0) + myOffset * aSign * CSLib::DNNormal(1, 0, DerNUV, OrderU, OrderV); - theD1V = DerSurf(0, 1) + myOffset * aSign * CSLib::DNNormal(0, 1, DerNUV, OrderU, OrderV); -} - -void GeomEvaluator_OffsetSurface::CalculateD2(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V, - gp_Vec& theD2U, - gp_Vec& theD2V, - gp_Vec& theD2UV, - const gp_Vec& theD3U, - const gp_Vec& theD3V, - const gp_Vec& theD3UUV, - const gp_Vec& theD3UVV) const -{ - gp_Dir Normal; - CSLib_NormalStatus NStatus; - CSLib::Normal(theD1U, theD1V, the_D1MagTol, NStatus, Normal); - - const Standard_Integer MaxOrder = (NStatus == CSLib_Defined) ? 0 : 3; - Standard_Integer OrderU, OrderV; - TColgp_Array2OfVec DerNUV(0, MaxOrder + 2, 0, MaxOrder + 2); - TColgp_Array2OfVec DerSurf(0, MaxOrder + 3, 0, MaxOrder + 3); - - Standard_Real Umin = 0, Umax = 0, Vmin = 0, Vmax = 0; - Bounds(Umin, Umax, Vmin, Vmax); - - DerSurf.SetValue(1, 0, theD1U); - DerSurf.SetValue(0, 1, theD1V); - DerSurf.SetValue(1, 1, theD2UV); - DerSurf.SetValue(2, 0, theD2U); - DerSurf.SetValue(0, 2, theD2V); - DerSurf.SetValue(3, 0, theD3U); - DerSurf.SetValue(2, 1, theD3UUV); - DerSurf.SetValue(1, 2, theD3UVV); - DerSurf.SetValue(0, 3, theD3V); - //********************* - - Handle(Geom_BSplineSurface) L; - Standard_Boolean isOpposite = Standard_False; - Standard_Boolean AlongU = Standard_False; - Standard_Boolean AlongV = Standard_False; - if ((NStatus != CSLib_Defined) && !myOscSurf.IsNull()) - { - AlongU = myOscSurf->UOscSurf(theU, theV, isOpposite, L); - AlongV = myOscSurf->VOscSurf(theU, theV, isOpposite, L); - } - const Standard_Real aSign = ((AlongV || AlongU) && isOpposite) ? -1. : 1.; - - if (!myBaseSurf.IsNull()) - derivatives(MaxOrder, 3, theU, theV, myBaseSurf, 2, 2, AlongU, AlongV, L, DerNUV, DerSurf); - else - derivatives(MaxOrder, 3, theU, theV, myBaseAdaptor, 2, 2, AlongU, AlongV, L, DerNUV, DerSurf); - - CSLib::Normal(MaxOrder, - DerNUV, - the_D1MagTol, - theU, - theV, - Umin, - Umax, - Vmin, - Vmax, - NStatus, - Normal, - OrderU, - OrderV); - if (NStatus != CSLib_Defined) - throw Geom_UndefinedValue( - "GeomEvaluator_OffsetSurface::CalculateD2(): Unable to calculate normal"); - - theValue.SetXYZ(theValue.XYZ() + myOffset * aSign * Normal.XYZ()); - - theD1U = DerSurf(1, 0) + myOffset * aSign * CSLib::DNNormal(1, 0, DerNUV, OrderU, OrderV); - theD1V = DerSurf(0, 1) + myOffset * aSign * CSLib::DNNormal(0, 1, DerNUV, OrderU, OrderV); - - if (!myBaseSurf.IsNull()) - { - theD2U = myBaseSurf->DN(theU, theV, 2, 0); - theD2V = myBaseSurf->DN(theU, theV, 0, 2); - theD2UV = myBaseSurf->DN(theU, theV, 1, 1); - } - else - { - theD2U = myBaseAdaptor->DN(theU, theV, 2, 0); - theD2V = myBaseAdaptor->DN(theU, theV, 0, 2); - theD2UV = myBaseAdaptor->DN(theU, theV, 1, 1); - } - - theD2U += aSign * myOffset * CSLib::DNNormal(2, 0, DerNUV, OrderU, OrderV); - theD2V += aSign * myOffset * CSLib::DNNormal(0, 2, DerNUV, OrderU, OrderV); - theD2UV += aSign * myOffset * CSLib::DNNormal(1, 1, DerNUV, OrderU, OrderV); -} - -void GeomEvaluator_OffsetSurface::CalculateD3(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V, - gp_Vec& theD2U, - gp_Vec& theD2V, - gp_Vec& theD2UV, - gp_Vec& theD3U, - gp_Vec& theD3V, - gp_Vec& theD3UUV, - gp_Vec& theD3UVV) const -{ - gp_Dir Normal; - CSLib_NormalStatus NStatus; - CSLib::Normal(theD1U, theD1V, the_D1MagTol, NStatus, Normal); - const Standard_Integer MaxOrder = (NStatus == CSLib_Defined) ? 0 : 3; - Standard_Integer OrderU, OrderV; - TColgp_Array2OfVec DerNUV(0, MaxOrder + 3, 0, MaxOrder + 3); - TColgp_Array2OfVec DerSurf(0, MaxOrder + 4, 0, MaxOrder + 4); - Standard_Real Umin = 0, Umax = 0, Vmin = 0, Vmax = 0; - Bounds(Umin, Umax, Vmin, Vmax); - - DerSurf.SetValue(1, 0, theD1U); - DerSurf.SetValue(0, 1, theD1V); - DerSurf.SetValue(1, 1, theD2UV); - DerSurf.SetValue(2, 0, theD2U); - DerSurf.SetValue(0, 2, theD2V); - DerSurf.SetValue(3, 0, theD3U); - DerSurf.SetValue(2, 1, theD3UUV); - DerSurf.SetValue(1, 2, theD3UVV); - DerSurf.SetValue(0, 3, theD3V); - - //********************* - Handle(Geom_BSplineSurface) L; - Standard_Boolean isOpposite = Standard_False; - Standard_Boolean AlongU = Standard_False; - Standard_Boolean AlongV = Standard_False; - if ((NStatus != CSLib_Defined) && !myOscSurf.IsNull()) - { - AlongU = myOscSurf->UOscSurf(theU, theV, isOpposite, L); - AlongV = myOscSurf->VOscSurf(theU, theV, isOpposite, L); - } - const Standard_Real aSign = ((AlongV || AlongU) && isOpposite) ? -1. : 1.; - - if (!myBaseSurf.IsNull()) - derivatives(MaxOrder, 3, theU, theV, myBaseSurf, 3, 3, AlongU, AlongV, L, DerNUV, DerSurf); - else - derivatives(MaxOrder, 3, theU, theV, myBaseAdaptor, 3, 3, AlongU, AlongV, L, DerNUV, DerSurf); - - CSLib::Normal(MaxOrder, - DerNUV, - the_D1MagTol, - theU, - theV, - Umin, - Umax, - Vmin, - Vmax, - NStatus, - Normal, - OrderU, - OrderV); - if (NStatus != CSLib_Defined) - throw Geom_UndefinedValue( - "GeomEvaluator_OffsetSurface::CalculateD3(): Unable to calculate normal"); - - theValue.SetXYZ(theValue.XYZ() + myOffset * aSign * Normal.XYZ()); - - theD1U = DerSurf(1, 0) + myOffset * aSign * CSLib::DNNormal(1, 0, DerNUV, OrderU, OrderV); - theD1V = DerSurf(0, 1) + myOffset * aSign * CSLib::DNNormal(0, 1, DerNUV, OrderU, OrderV); - - if (!myBaseSurf.IsNull()) - { - theD2U = myBaseSurf->DN(theU, theV, 2, 0); - theD2V = myBaseSurf->DN(theU, theV, 0, 2); - theD2UV = myBaseSurf->DN(theU, theV, 1, 1); - theD3U = myBaseSurf->DN(theU, theV, 3, 0); - theD3V = myBaseSurf->DN(theU, theV, 0, 3); - theD3UUV = myBaseSurf->DN(theU, theV, 2, 1); - theD3UVV = myBaseSurf->DN(theU, theV, 1, 2); - } - else - { - theD2U = myBaseAdaptor->DN(theU, theV, 2, 0); - theD2V = myBaseAdaptor->DN(theU, theV, 0, 2); - theD2UV = myBaseAdaptor->DN(theU, theV, 1, 1); - theD3U = myBaseAdaptor->DN(theU, theV, 3, 0); - theD3V = myBaseAdaptor->DN(theU, theV, 0, 3); - theD3UUV = myBaseAdaptor->DN(theU, theV, 2, 1); - theD3UVV = myBaseAdaptor->DN(theU, theV, 1, 2); - } - - theD2U += aSign * myOffset * CSLib::DNNormal(2, 0, DerNUV, OrderU, OrderV); - theD2V += aSign * myOffset * CSLib::DNNormal(0, 2, DerNUV, OrderU, OrderV); - theD2UV += aSign * myOffset * CSLib::DNNormal(1, 1, DerNUV, OrderU, OrderV); - theD3U += aSign * myOffset * CSLib::DNNormal(3, 0, DerNUV, OrderU, OrderV); - theD3V += aSign * myOffset * CSLib::DNNormal(0, 3, DerNUV, OrderU, OrderV); - theD3UUV += aSign * myOffset * CSLib::DNNormal(2, 1, DerNUV, OrderU, OrderV); - theD3UVV += aSign * myOffset * CSLib::DNNormal(1, 2, DerNUV, OrderU, OrderV); -} - -gp_Vec GeomEvaluator_OffsetSurface::CalculateDN(const Standard_Real theU, - const Standard_Real theV, - const Standard_Integer theNu, - const Standard_Integer theNv, - const gp_Vec& theD1U, - const gp_Vec& theD1V) const -{ - gp_Dir Normal; - CSLib_NormalStatus NStatus; - CSLib::Normal(theD1U, theD1V, the_D1MagTol, NStatus, Normal); - const Standard_Integer MaxOrder = (NStatus == CSLib_Defined) ? 0 : 3; - Standard_Integer OrderU, OrderV; - TColgp_Array2OfVec DerNUV(0, MaxOrder + theNu, 0, MaxOrder + theNv); - TColgp_Array2OfVec DerSurf(0, MaxOrder + theNu + 1, 0, MaxOrder + theNv + 1); - - Standard_Real Umin = 0, Umax = 0, Vmin = 0, Vmax = 0; - Bounds(Umin, Umax, Vmin, Vmax); - - DerSurf.SetValue(1, 0, theD1U); - DerSurf.SetValue(0, 1, theD1V); - - //********************* - Handle(Geom_BSplineSurface) L; - // Is there any osculatingsurface along U or V; - Standard_Boolean isOpposite = Standard_False; - Standard_Boolean AlongU = Standard_False; - Standard_Boolean AlongV = Standard_False; - if ((NStatus != CSLib_Defined) && !myOscSurf.IsNull()) - { - AlongU = myOscSurf->UOscSurf(theU, theV, isOpposite, L); - AlongV = myOscSurf->VOscSurf(theU, theV, isOpposite, L); - } - const Standard_Real aSign = ((AlongV || AlongU) && isOpposite) ? -1. : 1.; - - if (!myBaseSurf.IsNull()) - derivatives(MaxOrder, - 1, - theU, - theV, - myBaseSurf, - theNu, - theNv, - AlongU, - AlongV, - L, - DerNUV, - DerSurf); - else - derivatives(MaxOrder, - 1, - theU, - theV, - myBaseAdaptor, - theNu, - theNv, - AlongU, - AlongV, - L, - DerNUV, - DerSurf); - - CSLib::Normal(MaxOrder, - DerNUV, - the_D1MagTol, - theU, - theV, - Umin, - Umax, - Vmin, - Vmax, - NStatus, - Normal, - OrderU, - OrderV); - if (NStatus != CSLib_Defined) - throw Geom_UndefinedValue( - "GeomEvaluator_OffsetSurface::CalculateDN(): Unable to calculate normal"); - - gp_Vec D; - if (!myBaseSurf.IsNull()) - D = myBaseSurf->DN(theU, theV, theNu, theNv); - else - D = myBaseAdaptor->DN(theU, theV, theNu, theNv); - - D += aSign * myOffset * CSLib::DNNormal(theNu, theNv, DerNUV, OrderU, OrderV); - return D; -} - -void GeomEvaluator_OffsetSurface::Bounds(Standard_Real& theUMin, - Standard_Real& theUMax, - Standard_Real& theVMin, - Standard_Real& theVMax) const -{ - if (!myBaseSurf.IsNull()) - myBaseSurf->Bounds(theUMin, theUMax, theVMin, theVMax); - else - { - theUMin = myBaseAdaptor->FirstUParameter(); - theUMax = myBaseAdaptor->LastUParameter(); - theVMin = myBaseAdaptor->FirstVParameter(); - theVMax = myBaseAdaptor->LastVParameter(); - } -} - -Standard_Boolean GeomEvaluator_OffsetSurface::ReplaceDerivative( - const Standard_Real theU, - const Standard_Real theV, - gp_Vec& theDU, - gp_Vec& theDV, - const Standard_Real theSquareTol) const -{ - Standard_Boolean isReplaceDU = theDU.SquareMagnitude() < theSquareTol; - Standard_Boolean isReplaceDV = theDV.SquareMagnitude() < theSquareTol; - Standard_Boolean isReplaced = Standard_False; - if (isReplaceDU ^ isReplaceDV) - { - Standard_Real aU = theU; - Standard_Real aV = theV; - Standard_Real aUMin = 0, aUMax = 0, aVMin = 0, aVMax = 0; - Bounds(aUMin, aUMax, aVMin, aVMax); - - // Calculate step along non-zero derivative - Standard_Real aStep; - Handle(Adaptor3d_Surface) aSurfAdapt; - if (!myBaseAdaptor.IsNull()) - aSurfAdapt = myBaseAdaptor; - else - aSurfAdapt = new GeomAdaptor_Surface(myBaseSurf); - if (isReplaceDV) - { - aStep = Precision::Confusion() * theDU.Magnitude(); - if (aStep > aUMax - aUMin) - aStep = (aUMax - aUMin) / 100.; - } - else - { - aStep = Precision::Confusion() * theDV.Magnitude(); - if (aStep > aVMax - aVMin) - aStep = (aVMax - aVMin) / 100.; - } - - gp_Pnt aP; - gp_Vec aDU, aDV; - // Step away from current parametric coordinates and calculate derivatives once again. - // Replace zero derivative by the obtained. - for (Standard_Real aStepSign = -1.0; aStepSign <= 1.0 && !isReplaced; aStepSign += 2.0) - { - if (isReplaceDV) - { - aU = theU + aStepSign * aStep; - if (aU < aUMin || aU > aUMax) - continue; - } - else - { - aV = theV + aStepSign * aStep; - if (aV < aVMin || aV > aVMax) - continue; - } - - BaseD1(aU, aV, aP, aDU, aDV); - - if (isReplaceDU && aDU.SquareMagnitude() > theSquareTol) - { - theDU = aDU; - isReplaced = Standard_True; - } - if (isReplaceDV && aDV.SquareMagnitude() > theSquareTol) - { - theDV = aDV; - isReplaced = Standard_True; - } - } - } - return isReplaced; -} diff --git a/src/ModelingData/TKG3d/GeomEvaluator/GeomEvaluator_OffsetSurface.hxx b/src/ModelingData/TKG3d/GeomEvaluator/GeomEvaluator_OffsetSurface.hxx deleted file mode 100644 index 0bce0b3793..0000000000 --- a/src/ModelingData/TKG3d/GeomEvaluator/GeomEvaluator_OffsetSurface.hxx +++ /dev/null @@ -1,190 +0,0 @@ -// Created on: 2015-09-21 -// Copyright (c) 2015 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 _GeomEvaluator_OffsetSurface_HeaderFile -#define _GeomEvaluator_OffsetSurface_HeaderFile - -#include -#include -#include -#include - -//! Allows to calculate values and derivatives for offset surfaces -class GeomEvaluator_OffsetSurface : public GeomEvaluator_Surface -{ -public: - //! Initialize evaluator by surface - Standard_EXPORT GeomEvaluator_OffsetSurface( - const Handle(Geom_Surface)& theBase, - const Standard_Real theOffset, - const Handle(Geom_OsculatingSurface)& theOscSurf = Handle(Geom_OsculatingSurface)()); - //! Initialize evaluator by surface adaptor - Standard_EXPORT GeomEvaluator_OffsetSurface( - const Handle(GeomAdaptor_Surface)& theBase, - const Standard_Real theOffset, - const Handle(Geom_OsculatingSurface)& theOscSurf = Handle(Geom_OsculatingSurface)()); - - //! Change the offset value - void SetOffsetValue(Standard_Real theOffset) { myOffset = theOffset; } - - //! Value of surface - Standard_EXPORT void D0(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue) const Standard_OVERRIDE; - //! Value and first derivatives of surface - Standard_EXPORT void D1(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V) const Standard_OVERRIDE; - //! Value, first and second derivatives of surface - Standard_EXPORT void D2(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V, - gp_Vec& theD2U, - gp_Vec& theD2V, - gp_Vec& theD2UV) const Standard_OVERRIDE; - //! Value, first, second and third derivatives of surface - Standard_EXPORT void D3(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V, - gp_Vec& theD2U, - gp_Vec& theD2V, - gp_Vec& theD2UV, - gp_Vec& theD3U, - gp_Vec& theD3V, - gp_Vec& theD3UUV, - gp_Vec& theD3UVV) const Standard_OVERRIDE; - //! Calculates N-th derivatives of surface, where N = theDerU + theDerV. - //! - //! Raises if N < 1 or theDerU < 0 or theDerV < 0 - Standard_EXPORT gp_Vec DN(const Standard_Real theU, - const Standard_Real theV, - const Standard_Integer theDerU, - const Standard_Integer theDerV) const Standard_OVERRIDE; - - Standard_EXPORT Handle(GeomEvaluator_Surface) ShallowCopy() const Standard_OVERRIDE; - - DEFINE_STANDARD_RTTIEXT(GeomEvaluator_OffsetSurface, GeomEvaluator_Surface) - -private: - //! Returns bounds of a base surface - void Bounds(Standard_Real& theUMin, - Standard_Real& theUMax, - Standard_Real& theVMin, - Standard_Real& theVMax) const; - - //! Recalculate D1 values of base surface into D0 value of offset surface - void CalculateD0(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - const gp_Vec& theD1U, - const gp_Vec& theD1V) const; - //! Recalculate D2 values of base surface into D1 values of offset surface - void CalculateD1(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V, - const gp_Vec& theD2U, - const gp_Vec& theD2V, - const gp_Vec& theD2UV) const; - //! Recalculate D3 values of base surface into D2 values of offset surface - void CalculateD2(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V, - gp_Vec& theD2U, - gp_Vec& theD2V, - gp_Vec& theD2UV, - const gp_Vec& theD3U, - const gp_Vec& theD3V, - const gp_Vec& theD3UUV, - const gp_Vec& theD3UVV) const; - //! Recalculate D3 values of base surface into D3 values of offset surface - void CalculateD3(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V, - gp_Vec& theD2U, - gp_Vec& theD2V, - gp_Vec& theD2UV, - gp_Vec& theD3U, - gp_Vec& theD3V, - gp_Vec& theD3UUV, - gp_Vec& theD3UVV) const; - //! Calculate DN of offset surface based on derivatives of base surface - gp_Vec CalculateDN(const Standard_Real theU, - const Standard_Real theV, - const Standard_Integer theNu, - const Standard_Integer theNv, - const gp_Vec& theD1U, - const gp_Vec& theD1V) const; - - //! Calculate value of base surface/adaptor - void BaseD0(const Standard_Real theU, const Standard_Real theV, gp_Pnt& theValue) const; - //! Calculate value and first derivatives of base surface/adaptor - void BaseD1(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V) const; - //! Calculate value, first and second derivatives of base surface/adaptor - void BaseD2(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V, - gp_Vec& theD2U, - gp_Vec& theD2V, - gp_Vec& theD2UV) const; - //! Calculate value, first, second and third derivatives of base surface/adaptor - void BaseD3(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V, - gp_Vec& theD2U, - gp_Vec& theD2V, - gp_Vec& theD2UV, - gp_Vec& theD3U, - gp_Vec& theD3V, - gp_Vec& theD3UUV, - gp_Vec& theD3UVV) const; - - //! Replace zero derivative by the corresponding derivative in a near point. - //! Return true, if the derivative was replaced. - Standard_Boolean ReplaceDerivative(const Standard_Real theU, - const Standard_Real theV, - gp_Vec& theDU, - gp_Vec& theDV, - const Standard_Real theSquareTol) const; - -private: - Handle(Geom_Surface) myBaseSurf; - Handle(GeomAdaptor_Surface) myBaseAdaptor; - - Standard_Real myOffset; ///< offset value - Handle(Geom_OsculatingSurface) myOscSurf; ///< auxiliary osculating surface -}; - -DEFINE_STANDARD_HANDLE(GeomEvaluator_OffsetSurface, GeomEvaluator_Surface) - -#endif // _GeomEvaluator_OffsetSurface_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomEvaluator/GeomEvaluator_Surface.hxx b/src/ModelingData/TKG3d/GeomEvaluator/GeomEvaluator_Surface.hxx deleted file mode 100644 index 50d4c2eb9a..0000000000 --- a/src/ModelingData/TKG3d/GeomEvaluator/GeomEvaluator_Surface.hxx +++ /dev/null @@ -1,76 +0,0 @@ -// Created on: 2015-09-21 -// Copyright (c) 2015 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 _GeomEvaluator_Surface_HeaderFile -#define _GeomEvaluator_Surface_HeaderFile - -#include -#include - -class gp_Pnt; -class gp_Vec; - -//! Interface for calculation of values and derivatives for different kinds of surfaces. -//! Works both with adaptors and surfaces. -class GeomEvaluator_Surface : public Standard_Transient -{ -public: - GeomEvaluator_Surface() {} - - //! Value of surface - virtual void D0(const Standard_Real theU, const Standard_Real theV, gp_Pnt& theValue) const = 0; - //! Value and first derivatives of surface - virtual void D1(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V) const = 0; - //! Value, first and second derivatives of surface - virtual void D2(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V, - gp_Vec& theD2U, - gp_Vec& theD2V, - gp_Vec& theD2UV) const = 0; - //! Value, first, second and third derivatives of surface - virtual void D3(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V, - gp_Vec& theD2U, - gp_Vec& theD2V, - gp_Vec& theD2UV, - gp_Vec& theD3U, - gp_Vec& theD3V, - gp_Vec& theD3UUV, - gp_Vec& theD3UVV) const = 0; - //! Calculates N-th derivatives of surface, where N = theDerU + theDerV. - //! - //! Raises if N < 1 or theDerU < 0 or theDerV < 0 - virtual gp_Vec DN(const Standard_Real theU, - const Standard_Real theV, - const Standard_Integer theDerU, - const Standard_Integer theDerV) const = 0; - - virtual Handle(GeomEvaluator_Surface) ShallowCopy() const = 0; - - DEFINE_STANDARD_RTTI_INLINE(GeomEvaluator_Surface, Standard_Transient) -}; - -DEFINE_STANDARD_HANDLE(GeomEvaluator_Surface, Standard_Transient) - -#endif // _GeomEvaluator_Surface_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomEvaluator/GeomEvaluator_SurfaceOfExtrusion.cxx b/src/ModelingData/TKG3d/GeomEvaluator/GeomEvaluator_SurfaceOfExtrusion.cxx deleted file mode 100644 index fa51c2b89d..0000000000 --- a/src/ModelingData/TKG3d/GeomEvaluator/GeomEvaluator_SurfaceOfExtrusion.cxx +++ /dev/null @@ -1,151 +0,0 @@ -// Created on: 2015-09-21 -// Copyright (c) 2015 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. - -#include - -#include - -IMPLEMENT_STANDARD_RTTIEXT(GeomEvaluator_SurfaceOfExtrusion, GeomEvaluator_Surface) - -GeomEvaluator_SurfaceOfExtrusion::GeomEvaluator_SurfaceOfExtrusion( - const Handle(Geom_Curve)& theBase, - const gp_Dir& theExtrusionDir) - : GeomEvaluator_Surface(), - myBaseCurve(theBase), - myDirection(theExtrusionDir) -{ -} - -GeomEvaluator_SurfaceOfExtrusion::GeomEvaluator_SurfaceOfExtrusion( - const Handle(Adaptor3d_Curve)& theBase, - const gp_Dir& theExtrusionDir) - : GeomEvaluator_Surface(), - myBaseAdaptor(theBase), - myDirection(theExtrusionDir) -{ -} - -void GeomEvaluator_SurfaceOfExtrusion::D0(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue) const -{ - if (!myBaseAdaptor.IsNull()) - myBaseAdaptor->D0(theU, theValue); - else - myBaseCurve->D0(theU, theValue); - - Shift(theV, theValue); -} - -void GeomEvaluator_SurfaceOfExtrusion::D1(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V) const -{ - if (!myBaseAdaptor.IsNull()) - myBaseAdaptor->D1(theU, theValue, theD1U); - else - myBaseCurve->D1(theU, theValue, theD1U); - - theD1V = myDirection; - Shift(theV, theValue); -} - -void GeomEvaluator_SurfaceOfExtrusion::D2(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V, - gp_Vec& theD2U, - gp_Vec& theD2V, - gp_Vec& theD2UV) const -{ - if (!myBaseAdaptor.IsNull()) - myBaseAdaptor->D2(theU, theValue, theD1U, theD2U); - else - myBaseCurve->D2(theU, theValue, theD1U, theD2U); - - theD1V = myDirection; - theD2V.SetCoord(0.0, 0.0, 0.0); - theD2UV.SetCoord(0.0, 0.0, 0.0); - - Shift(theV, theValue); -} - -void GeomEvaluator_SurfaceOfExtrusion::D3(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V, - gp_Vec& theD2U, - gp_Vec& theD2V, - gp_Vec& theD2UV, - gp_Vec& theD3U, - gp_Vec& theD3V, - gp_Vec& theD3UUV, - gp_Vec& theD3UVV) const -{ - if (!myBaseAdaptor.IsNull()) - myBaseAdaptor->D3(theU, theValue, theD1U, theD2U, theD3U); - else - myBaseCurve->D3(theU, theValue, theD1U, theD2U, theD3U); - - theD1V = myDirection; - theD2V.SetCoord(0.0, 0.0, 0.0); - theD2UV.SetCoord(0.0, 0.0, 0.0); - theD3V.SetCoord(0.0, 0.0, 0.0); - theD3UUV.SetCoord(0.0, 0.0, 0.0); - theD3UVV.SetCoord(0.0, 0.0, 0.0); - - Shift(theV, theValue); -} - -gp_Vec GeomEvaluator_SurfaceOfExtrusion::DN(const Standard_Real theU, - const Standard_Real, - const Standard_Integer theDerU, - const Standard_Integer theDerV) const -{ - Standard_RangeError_Raise_if(theDerU < 0, "GeomEvaluator_SurfaceOfExtrusion::DN(): theDerU < 0"); - Standard_RangeError_Raise_if(theDerV < 0, "GeomEvaluator_SurfaceOfExtrusion::DN(): theDerV < 0"); - Standard_RangeError_Raise_if(theDerU + theDerV < 1, - "GeomEvaluator_SurfaceOfExtrusion::DN(): theDerU + theDerV < 1"); - - gp_Vec aResult(0.0, 0.0, 0.0); - if (theDerV == 0) - { - if (!myBaseAdaptor.IsNull()) - aResult = myBaseAdaptor->DN(theU, theDerU); - else - aResult = myBaseCurve->DN(theU, theDerU); - } - else if (theDerU == 0 && theDerV == 1) - aResult = gp_Vec(myDirection); - return aResult; -} - -Handle(GeomEvaluator_Surface) GeomEvaluator_SurfaceOfExtrusion::ShallowCopy() const -{ - Handle(GeomEvaluator_SurfaceOfExtrusion) aCopy; - if (!myBaseAdaptor.IsNull()) - { - aCopy = new GeomEvaluator_SurfaceOfExtrusion(myBaseAdaptor->ShallowCopy(), myDirection); - } - else - { - aCopy = new GeomEvaluator_SurfaceOfExtrusion(myBaseCurve, myDirection); - } - - return aCopy; -} diff --git a/src/ModelingData/TKG3d/GeomEvaluator/GeomEvaluator_SurfaceOfExtrusion.hxx b/src/ModelingData/TKG3d/GeomEvaluator/GeomEvaluator_SurfaceOfExtrusion.hxx deleted file mode 100644 index 3cf45007b0..0000000000 --- a/src/ModelingData/TKG3d/GeomEvaluator/GeomEvaluator_SurfaceOfExtrusion.hxx +++ /dev/null @@ -1,97 +0,0 @@ -// Created on: 2015-09-21 -// Copyright (c) 2015 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 _GeomEvaluator_SurfaceOfExtrusion_HeaderFile -#define _GeomEvaluator_SurfaceOfExtrusion_HeaderFile - -#include -#include -#include -#include - -//! Allows to calculate values and derivatives for surfaces of linear extrusion -class GeomEvaluator_SurfaceOfExtrusion : public GeomEvaluator_Surface -{ -public: - //! Initialize evaluator by surface - Standard_EXPORT GeomEvaluator_SurfaceOfExtrusion(const Handle(Geom_Curve)& theBase, - const gp_Dir& theExtrusionDir); - //! Initialize evaluator by surface adaptor - Standard_EXPORT GeomEvaluator_SurfaceOfExtrusion(const Handle(Adaptor3d_Curve)& theBase, - const gp_Dir& theExtrusionDir); - - ///! Changes the direction of extrusion - void SetDirection(const gp_Dir& theDirection) { myDirection = theDirection; } - - //! Value of surface - Standard_EXPORT void D0(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue) const Standard_OVERRIDE; - //! Value and first derivatives of surface - Standard_EXPORT void D1(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V) const Standard_OVERRIDE; - //! Value, first and second derivatives of surface - Standard_EXPORT void D2(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V, - gp_Vec& theD2U, - gp_Vec& theD2V, - gp_Vec& theD2UV) const Standard_OVERRIDE; - //! Value, first, second and third derivatives of surface - Standard_EXPORT void D3(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V, - gp_Vec& theD2U, - gp_Vec& theD2V, - gp_Vec& theD2UV, - gp_Vec& theD3U, - gp_Vec& theD3V, - gp_Vec& theD3UUV, - gp_Vec& theD3UVV) const Standard_OVERRIDE; - //! Calculates N-th derivatives of surface, where N = theDerU + theDerV. - //! - //! Raises if N < 1 or theDerU < 0 or theDerV < 0 - Standard_EXPORT gp_Vec DN(const Standard_Real theU, - const Standard_Real theV, - const Standard_Integer theDerU, - const Standard_Integer theDerV) const Standard_OVERRIDE; - - Standard_EXPORT Handle(GeomEvaluator_Surface) ShallowCopy() const Standard_OVERRIDE; - - DEFINE_STANDARD_RTTIEXT(GeomEvaluator_SurfaceOfExtrusion, GeomEvaluator_Surface) - -private: - //! Shift the point along direction to the given distance (theShift) - void Shift(const Standard_Real theShift, gp_Pnt& thePoint) const - { - thePoint.ChangeCoord() += myDirection.XYZ() * theShift; - } - -private: - Handle(Geom_Curve) myBaseCurve; - Handle(Adaptor3d_Curve) myBaseAdaptor; - - gp_Dir myDirection; -}; - -DEFINE_STANDARD_HANDLE(GeomEvaluator_SurfaceOfExtrusion, GeomEvaluator_Surface) - -#endif // _GeomEvaluator_SurfaceOfExtrusion_HeaderFile diff --git a/src/ModelingData/TKG3d/GeomEvaluator/GeomEvaluator_SurfaceOfRevolution.cxx b/src/ModelingData/TKG3d/GeomEvaluator/GeomEvaluator_SurfaceOfRevolution.cxx deleted file mode 100644 index 1b42d347f8..0000000000 --- a/src/ModelingData/TKG3d/GeomEvaluator/GeomEvaluator_SurfaceOfRevolution.cxx +++ /dev/null @@ -1,237 +0,0 @@ -// Created on: 2015-09-21 -// Copyright (c) 2015 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. - -#include - -#include -#include -#include - -IMPLEMENT_STANDARD_RTTIEXT(GeomEvaluator_SurfaceOfRevolution, GeomEvaluator_Surface) - -GeomEvaluator_SurfaceOfRevolution::GeomEvaluator_SurfaceOfRevolution( - const Handle(Geom_Curve)& theBase, - const gp_Dir& theRevolDir, - const gp_Pnt& theRevolLoc) - : GeomEvaluator_Surface(), - myBaseCurve(theBase), - myRotAxis(theRevolLoc, theRevolDir) -{ -} - -GeomEvaluator_SurfaceOfRevolution::GeomEvaluator_SurfaceOfRevolution( - const Handle(Adaptor3d_Curve)& theBase, - const gp_Dir& theRevolDir, - const gp_Pnt& theRevolLoc) - : GeomEvaluator_Surface(), - myBaseAdaptor(theBase), - myRotAxis(theRevolLoc, theRevolDir) -{ -} - -void GeomEvaluator_SurfaceOfRevolution::D0(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue) const -{ - if (!myBaseAdaptor.IsNull()) - myBaseAdaptor->D0(theV, theValue); - else - myBaseCurve->D0(theV, theValue); - - gp_Trsf aRotation; - aRotation.SetRotation(myRotAxis, theU); - theValue.Transform(aRotation); -} - -void GeomEvaluator_SurfaceOfRevolution::D1(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V) const -{ - if (!myBaseAdaptor.IsNull()) - myBaseAdaptor->D1(theV, theValue, theD1V); - else - myBaseCurve->D1(theV, theValue, theD1V); - - // vector from center of rotation to the point on rotated curve - gp_XYZ aCQ = theValue.XYZ() - myRotAxis.Location().XYZ(); - theD1U = gp_Vec(myRotAxis.Direction().XYZ().Crossed(aCQ)); - // If the point is placed on the axis of revolution then derivatives on U are undefined. - // Manually set them to zero. - if (theD1U.SquareMagnitude() < Precision::SquareConfusion()) - theD1U.SetCoord(0.0, 0.0, 0.0); - - gp_Trsf aRotation; - aRotation.SetRotation(myRotAxis, theU); - theValue.Transform(aRotation); - theD1U.Transform(aRotation); - theD1V.Transform(aRotation); -} - -void GeomEvaluator_SurfaceOfRevolution::D2(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V, - gp_Vec& theD2U, - gp_Vec& theD2V, - gp_Vec& theD2UV) const -{ - if (!myBaseAdaptor.IsNull()) - myBaseAdaptor->D2(theV, theValue, theD1V, theD2V); - else - myBaseCurve->D2(theV, theValue, theD1V, theD2V); - - // vector from center of rotation to the point on rotated curve - gp_XYZ aCQ = theValue.XYZ() - myRotAxis.Location().XYZ(); - const gp_XYZ& aDir = myRotAxis.Direction().XYZ(); - theD1U = gp_Vec(aDir.Crossed(aCQ)); - // If the point is placed on the axis of revolution then derivatives on U are undefined. - // Manually set them to zero. - if (theD1U.SquareMagnitude() < Precision::SquareConfusion()) - theD1U.SetCoord(0.0, 0.0, 0.0); - theD2U = gp_Vec(aDir.Dot(aCQ) * aDir - aCQ); - theD2UV = gp_Vec(aDir.Crossed(theD1V.XYZ())); - - gp_Trsf aRotation; - aRotation.SetRotation(myRotAxis, theU); - theValue.Transform(aRotation); - theD1U.Transform(aRotation); - theD1V.Transform(aRotation); - theD2U.Transform(aRotation); - theD2V.Transform(aRotation); - theD2UV.Transform(aRotation); -} - -void GeomEvaluator_SurfaceOfRevolution::D3(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V, - gp_Vec& theD2U, - gp_Vec& theD2V, - gp_Vec& theD2UV, - gp_Vec& theD3U, - gp_Vec& theD3V, - gp_Vec& theD3UUV, - gp_Vec& theD3UVV) const -{ - if (!myBaseAdaptor.IsNull()) - myBaseAdaptor->D3(theV, theValue, theD1V, theD2V, theD3V); - else - myBaseCurve->D3(theV, theValue, theD1V, theD2V, theD3V); - - // vector from center of rotation to the point on rotated curve - gp_XYZ aCQ = theValue.XYZ() - myRotAxis.Location().XYZ(); - const gp_XYZ& aDir = myRotAxis.Direction().XYZ(); - theD1U = gp_Vec(aDir.Crossed(aCQ)); - // If the point is placed on the axis of revolution then derivatives on U are undefined. - // Manually set them to zero. - if (theD1U.SquareMagnitude() < Precision::SquareConfusion()) - theD1U.SetCoord(0.0, 0.0, 0.0); - theD2U = gp_Vec(aDir.Dot(aCQ) * aDir - aCQ); - theD2UV = gp_Vec(aDir.Crossed(theD1V.XYZ())); - theD3U = -theD1U; - theD3UUV = gp_Vec(aDir.Dot(theD1V.XYZ()) * aDir - theD1V.XYZ()); - theD3UVV = gp_Vec(aDir.Crossed(theD2V.XYZ())); - - gp_Trsf aRotation; - aRotation.SetRotation(myRotAxis, theU); - theValue.Transform(aRotation); - theD1U.Transform(aRotation); - theD1V.Transform(aRotation); - theD2U.Transform(aRotation); - theD2V.Transform(aRotation); - theD2UV.Transform(aRotation); - theD3U.Transform(aRotation); - theD3V.Transform(aRotation); - theD3UUV.Transform(aRotation); - theD3UVV.Transform(aRotation); -} - -gp_Vec GeomEvaluator_SurfaceOfRevolution::DN(const Standard_Real theU, - const Standard_Real theV, - const Standard_Integer theDerU, - const Standard_Integer theDerV) const -{ - Standard_RangeError_Raise_if(theDerU < 0, "GeomEvaluator_SurfaceOfRevolution::DN(): theDerU < 0"); - Standard_RangeError_Raise_if(theDerV < 0, "GeomEvaluator_SurfaceOfRevolution::DN(): theDerV < 0"); - Standard_RangeError_Raise_if(theDerU + theDerV < 1, - "GeomEvaluator_SurfaceOfRevolution::DN(): theDerU + theDerV < 1"); - - gp_Trsf aRotation; - aRotation.SetRotation(myRotAxis, theU); - - gp_Pnt aP; - gp_Vec aDV; - gp_Vec aResult; - if (theDerU == 0) - { - if (!myBaseAdaptor.IsNull()) - aResult = myBaseAdaptor->DN(theV, theDerV); - else - aResult = myBaseCurve->DN(theV, theDerV); - } - else - { - if (theDerV == 0) - { - if (!myBaseAdaptor.IsNull()) - myBaseAdaptor->D0(theV, aP); - else - myBaseCurve->D0(theV, aP); - aDV = gp_Vec(aP.XYZ() - myRotAxis.Location().XYZ()); - } - else - { - if (!myBaseAdaptor.IsNull()) - aDV = myBaseAdaptor->DN(theV, theDerV); - else - aDV = myBaseCurve->DN(theV, theDerV); - } - - const gp_XYZ& aDir = myRotAxis.Direction().XYZ(); - if (theDerU % 4 == 1) - aResult = gp_Vec(aDir.Crossed(aDV.XYZ())); - else if (theDerU % 4 == 2) - aResult = gp_Vec(aDir.Dot(aDV.XYZ()) * aDir - aDV.XYZ()); - else if (theDerU % 4 == 3) - aResult = gp_Vec(aDir.Crossed(aDV.XYZ())) * (-1.0); - else - aResult = gp_Vec(aDV.XYZ() - aDir.Dot(aDV.XYZ()) * aDir); - } - - aResult.Transform(aRotation); - return aResult; -} - -Handle(GeomEvaluator_Surface) GeomEvaluator_SurfaceOfRevolution::ShallowCopy() const -{ - Handle(GeomEvaluator_SurfaceOfRevolution) aCopy; - if (!myBaseAdaptor.IsNull()) - { - aCopy = new GeomEvaluator_SurfaceOfRevolution(myBaseAdaptor->ShallowCopy(), - myRotAxis.Direction(), - myRotAxis.Location()); - } - else - { - aCopy = new GeomEvaluator_SurfaceOfRevolution(myBaseCurve, - myRotAxis.Direction(), - myRotAxis.Location()); - } - - return aCopy; -} diff --git a/src/ModelingData/TKG3d/GeomEvaluator/GeomEvaluator_SurfaceOfRevolution.hxx b/src/ModelingData/TKG3d/GeomEvaluator/GeomEvaluator_SurfaceOfRevolution.hxx deleted file mode 100644 index 1d2b58831a..0000000000 --- a/src/ModelingData/TKG3d/GeomEvaluator/GeomEvaluator_SurfaceOfRevolution.hxx +++ /dev/null @@ -1,99 +0,0 @@ -// Created on: 2015-09-21 -// Copyright (c) 2015 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 _GeomEvaluator_SurfaceOfRevolution_HeaderFile -#define _GeomEvaluator_SurfaceOfRevolution_HeaderFile - -#include -#include -#include -#include -#include -#include - -//! Allows to calculate values and derivatives for surfaces of revolution -class GeomEvaluator_SurfaceOfRevolution : public GeomEvaluator_Surface -{ -public: - //! Initialize evaluator by revolved curve, the axis of revolution and the location - Standard_EXPORT GeomEvaluator_SurfaceOfRevolution(const Handle(Geom_Curve)& theBase, - const gp_Dir& theRevolDir, - const gp_Pnt& theRevolLoc); - //! Initialize evaluator by adaptor of the revolved curve, the axis of revolution and the location - Standard_EXPORT GeomEvaluator_SurfaceOfRevolution(const Handle(Adaptor3d_Curve)& theBase, - const gp_Dir& theRevolDir, - const gp_Pnt& theRevolLoc); - - //! Change direction of the axis of revolution - void SetDirection(const gp_Dir& theDirection) { myRotAxis.SetDirection(theDirection); } - - //! Change location of the axis of revolution - void SetLocation(const gp_Pnt& theLocation) { myRotAxis.SetLocation(theLocation); } - - //! Change the axis of revolution - void SetAxis(const gp_Ax1& theAxis) { myRotAxis = theAxis; } - - //! Value of surface - Standard_EXPORT void D0(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue) const Standard_OVERRIDE; - //! Value and first derivatives of surface - Standard_EXPORT void D1(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V) const Standard_OVERRIDE; - //! Value, first and second derivatives of surface - Standard_EXPORT void D2(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V, - gp_Vec& theD2U, - gp_Vec& theD2V, - gp_Vec& theD2UV) const Standard_OVERRIDE; - //! Value, first, second and third derivatives of surface - Standard_EXPORT void D3(const Standard_Real theU, - const Standard_Real theV, - gp_Pnt& theValue, - gp_Vec& theD1U, - gp_Vec& theD1V, - gp_Vec& theD2U, - gp_Vec& theD2V, - gp_Vec& theD2UV, - gp_Vec& theD3U, - gp_Vec& theD3V, - gp_Vec& theD3UUV, - gp_Vec& theD3UVV) const Standard_OVERRIDE; - //! Calculates N-th derivatives of surface, where N = theDerU + theDerV. - //! - //! Raises if N < 1 or theDerU < 0 or theDerV < 0 - Standard_EXPORT gp_Vec DN(const Standard_Real theU, - const Standard_Real theV, - const Standard_Integer theDerU, - const Standard_Integer theDerV) const Standard_OVERRIDE; - - Standard_EXPORT Handle(GeomEvaluator_Surface) ShallowCopy() const Standard_OVERRIDE; - - DEFINE_STANDARD_RTTIEXT(GeomEvaluator_SurfaceOfRevolution, GeomEvaluator_Surface) - -private: - Handle(Geom_Curve) myBaseCurve; - Handle(Adaptor3d_Curve) myBaseAdaptor; - gp_Ax1 myRotAxis; -}; - -DEFINE_STANDARD_HANDLE(GeomEvaluator_SurfaceOfRevolution, GeomEvaluator_Surface) - -#endif // _GeomEvaluator_SurfaceOfRevolution_HeaderFile diff --git a/src/ModelingData/TKG3d/PACKAGES.cmake b/src/ModelingData/TKG3d/PACKAGES.cmake index f3e1b72791..89f021d1a7 100644 --- a/src/ModelingData/TKG3d/PACKAGES.cmake +++ b/src/ModelingData/TKG3d/PACKAGES.cmake @@ -8,7 +8,6 @@ set(OCCT_TKG3d_LIST_OF_PACKAGES Adaptor3d LProp3d TopAbs - GeomEvaluator GProp GeomHash ) diff --git a/src/ModelingData/TKGeomBase/Extrema/Extrema_GenExtPS.cxx b/src/ModelingData/TKGeomBase/Extrema/Extrema_GenExtPS.cxx index bb07405607..eec0d02b26 100644 --- a/src/ModelingData/TKGeomBase/Extrema/Extrema_GenExtPS.cxx +++ b/src/ModelingData/TKGeomBase/Extrema/Extrema_GenExtPS.cxx @@ -18,12 +18,16 @@ #include +#include #include #include #include #include #include #include +#include +#include +#include #include #include #include