Loading files
3d097a032This file is pinned to the selected release. Download the original to open it in your design tool.
// 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 <BSplSLib.hxx>
#include <BSplSLib_Cache.hxx>
#include <gp_Pnt.hxx>
#include <gp_Vec.hxx>
#include <NCollection_Array2.hxx>
#include <Standard_Integer.hxx>
#include <NCollection_Array1.hxx>
#include <gtest/gtest.h>
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 NCollection_Array1<double>& theKnots,
const NCollection_Array1<int>& theMults,
NCollection_Array1<double>& 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
NCollection_Array2<gp_Pnt> 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);
NCollection_Array1<double> aKnotsU(1, 2);
aKnotsU(1) = 0.0;
aKnotsU(2) = 1.0;
NCollection_Array1<double> aKnotsV(1, 2);
aKnotsV(1) = 0.0;
aKnotsV(2) = 1.0;
NCollection_Array1<int> aMultsU(1, 2);
aMultsU(1) = 3;
aMultsU(2) = 3;
NCollection_Array1<int> aMultsV(1, 2);
aMultsV(1) = 3;
aMultsV(2) = 3;
NCollection_Array1<double> aFlatKnotsU(1, 6);
createFlatKnots(aKnotsU, aMultsU, aFlatKnotsU);
NCollection_Array1<double> aFlatKnotsV(1, 6);
createFlatKnots(aKnotsV, aMultsV, aFlatKnotsV);
const int aDegreeU = 2;
const int aDegreeV = 2;
// Create cache
occ::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
NCollection_Array2<gp_Pnt> 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);
NCollection_Array1<double> aKnotsU(1, 2);
aKnotsU(1) = 0.0;
aKnotsU(2) = 1.0;
NCollection_Array1<double> aKnotsV(1, 2);
aKnotsV(1) = 0.0;
aKnotsV(2) = 1.0;
NCollection_Array1<int> aMultsU(1, 2);
aMultsU(1) = 3;
aMultsU(2) = 3;
NCollection_Array1<int> aMultsV(1, 2);
aMultsV(1) = 3;
aMultsV(2) = 3;
NCollection_Array1<double> aFlatKnotsU(1, 6);
createFlatKnots(aKnotsU, aMultsU, aFlatKnotsU);
NCollection_Array1<double> aFlatKnotsV(1, 6);
createFlatKnots(aKnotsV, aMultsV, aFlatKnotsV);
const int aDegreeU = 2;
const int aDegreeV = 2;
// Create cache
occ::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
NCollection_Array2<gp_Pnt> 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);
NCollection_Array1<double> aKnotsU(1, 2);
aKnotsU(1) = 0.0;
aKnotsU(2) = 1.0;
NCollection_Array1<double> aKnotsV(1, 2);
aKnotsV(1) = 0.0;
aKnotsV(2) = 1.0;
NCollection_Array1<int> aMultsU(1, 2);
aMultsU(1) = 3;
aMultsU(2) = 3;
NCollection_Array1<int> aMultsV(1, 2);
aMultsV(1) = 3;
aMultsV(2) = 3;
NCollection_Array1<double> aFlatKnotsU(1, 6);
createFlatKnots(aKnotsU, aMultsU, aFlatKnotsU);
NCollection_Array1<double> aFlatKnotsV(1, 6);
createFlatKnots(aKnotsV, aMultsV, aFlatKnotsV);
const int aDegreeU = 2;
const int aDegreeV = 2;
// Create cache
occ::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)
NCollection_Array2<gp_Pnt> 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);
NCollection_Array2<double> 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;
NCollection_Array1<double> aKnotsU(1, 2);
aKnotsU(1) = 0.0;
aKnotsU(2) = 1.0;
NCollection_Array1<double> aKnotsV(1, 2);
aKnotsV(1) = 0.0;
aKnotsV(2) = 1.0;
NCollection_Array1<int> aMultsU(1, 2);
aMultsU(1) = 3;
aMultsU(2) = 3;
NCollection_Array1<int> aMultsV(1, 2);
aMultsV(1) = 3;
aMultsV(2) = 3;
NCollection_Array1<double> aFlatKnotsU(1, 6);
createFlatKnots(aKnotsU, aMultsU, aFlatKnotsU);
NCollection_Array1<double> aFlatKnotsV(1, 6);
createFlatKnots(aKnotsV, aMultsV, aFlatKnotsV);
const int aDegreeU = 2;
const int aDegreeV = 2;
// Create cache
occ::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
NCollection_Array2<gp_Pnt> 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);
NCollection_Array2<double> 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;
NCollection_Array1<double> aKnotsU(1, 2);
aKnotsU(1) = 0.0;
aKnotsU(2) = 1.0;
NCollection_Array1<double> aKnotsV(1, 2);
aKnotsV(1) = 0.0;
aKnotsV(2) = 1.0;
NCollection_Array1<int> aMultsU(1, 2);
aMultsU(1) = 3;
aMultsU(2) = 3;
NCollection_Array1<int> aMultsV(1, 2);
aMultsV(1) = 3;
aMultsV(2) = 3;
NCollection_Array1<double> aFlatKnotsU(1, 6);
createFlatKnots(aKnotsU, aMultsU, aFlatKnotsU);
NCollection_Array1<double> aFlatKnotsV(1, 6);
createFlatKnots(aKnotsV, aMultsV, aFlatKnotsV);
const int aDegreeU = 2;
const int aDegreeV = 2;
// Create cache
occ::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
NCollection_Array2<gp_Pnt> 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);
NCollection_Array2<double> 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;
NCollection_Array1<double> aKnotsU(1, 2);
aKnotsU(1) = 0.0;
aKnotsU(2) = 1.0;
NCollection_Array1<double> aKnotsV(1, 2);
aKnotsV(1) = 0.0;
aKnotsV(2) = 1.0;
NCollection_Array1<int> aMultsU(1, 2);
aMultsU(1) = 3;
aMultsU(2) = 3;
NCollection_Array1<int> aMultsV(1, 2);
aMultsV(1) = 3;
aMultsV(2) = 3;
NCollection_Array1<double> aFlatKnotsU(1, 6);
createFlatKnots(aKnotsU, aMultsU, aFlatKnotsU);
NCollection_Array1<double> aFlatKnotsV(1, 6);
createFlatKnots(aKnotsV, aMultsV, aFlatKnotsV);
const int aDegreeU = 2;
const int aDegreeV = 2;
// Create cache
occ::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
NCollection_Array2<gp_Pnt> 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));
}
}
NCollection_Array1<double> aKnotsU(1, 2);
aKnotsU(1) = 0.0;
aKnotsU(2) = 1.0;
NCollection_Array1<double> aKnotsV(1, 2);
aKnotsV(1) = 0.0;
aKnotsV(2) = 1.0;
NCollection_Array1<int> aMultsU(1, 2);
aMultsU(1) = 4;
aMultsU(2) = 4;
NCollection_Array1<int> aMultsV(1, 2);
aMultsV(1) = 3;
aMultsV(2) = 3;
NCollection_Array1<double> aFlatKnotsU(1, 8);
createFlatKnots(aKnotsU, aMultsU, aFlatKnotsU);
NCollection_Array1<double> aFlatKnotsV(1, 6);
createFlatKnots(aKnotsV, aMultsV, aFlatKnotsV);
const int aDegreeU = 3;
const int aDegreeV = 2;
// Create cache
occ::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
NCollection_Array2<gp_Pnt> 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));
}
}
NCollection_Array1<double> aKnotsU(1, 2);
aKnotsU(1) = 0.0;
aKnotsU(2) = 1.0;
NCollection_Array1<double> aKnotsV(1, 2);
aKnotsV(1) = 0.0;
aKnotsV(2) = 1.0;
NCollection_Array1<int> aMultsU(1, 2);
aMultsU(1) = 3;
aMultsU(2) = 3;
NCollection_Array1<int> aMultsV(1, 2);
aMultsV(1) = 4;
aMultsV(2) = 4;
NCollection_Array1<double> aFlatKnotsU(1, 6);
createFlatKnots(aKnotsU, aMultsU, aFlatKnotsU);
NCollection_Array1<double> aFlatKnotsV(1, 8);
createFlatKnots(aKnotsV, aMultsV, aFlatKnotsV);
const int aDegreeU = 2;
const int aDegreeV = 3;
// Create cache
occ::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;
}
}
}