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 <ExtremaPC_Circle.hxx>
#include <ExtremaPC_Curve.hxx>
#include <ExtremaPC_Line.hxx>
#include <Geom_BezierCurve.hxx>
#include <Geom_BSplineCurve.hxx>
#include <Geom_Circle.hxx>
#include <Geom_Ellipse.hxx>
#include <Geom_Hyperbola.hxx>
#include <Geom_Line.hxx>
#include <Geom_OffsetCurve.hxx>
#include <Geom_Parabola.hxx>
#include <Geom_TrimmedCurve.hxx>
#include <GeomAdaptor_Curve.hxx>
#include <GeomAdaptor_TransformedCurve.hxx>
#include <gp_Ax1.hxx>
#include <gp_Ax2.hxx>
#include <gp_Circ.hxx>
#include <gp_Dir.hxx>
#include <gp_Lin.hxx>
#include <gp_Pnt.hxx>
#include <gp_Trsf.hxx>
#include <NCollection_Array1.hxx>
#include <gtest/gtest.h>
#include <cmath>
//! Test fixture for ExtremaPC tests.
class ExtremaPC_CurveTest : public testing::Test
{
protected:
static constexpr double THE_TOL = 1.0e-6;
};
//==================================================================================================
// Line tests
//==================================================================================================
TEST_F(ExtremaPC_CurveTest, Line_PointOnLine)
{
// Line along X axis with bounded domain
gp_Lin aLine(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0));
// Point on line
gp_Pnt aPoint(5.0, 0.0, 0.0);
ExtremaPC_Line anEval(aLine, ExtremaPC::Domain1D{0.0, 100.0});
const ExtremaPC::Result& aResult = anEval.PerformWithEndpoints(aPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
ASSERT_EQ(aResult.NbExt(), 3); // 1 min + 2 max (endpoints)
size_t aMinIdx = aResult.MinIndex();
EXPECT_NEAR(aResult[aMinIdx].Parameter, 5.0, THE_TOL);
EXPECT_NEAR(aResult[aMinIdx].SquareDistance, 0.0, THE_TOL);
EXPECT_TRUE(aResult[aMinIdx].IsMinimum);
}
TEST_F(ExtremaPC_CurveTest, Line_PointOffLine)
{
// Line along X axis with bounded domain
gp_Lin aLine(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0));
// Point off line
gp_Pnt aPoint(5.0, 3.0, 4.0);
ExtremaPC_Line anEval(aLine, ExtremaPC::Domain1D{0.0, 100.0});
const ExtremaPC::Result& aResult = anEval.PerformWithEndpoints(aPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
ASSERT_EQ(aResult.NbExt(), 3); // 1 min + 2 max (endpoints)
size_t aMinIdx = aResult.MinIndex();
EXPECT_NEAR(aResult[aMinIdx].Parameter, 5.0, THE_TOL);
EXPECT_NEAR(aResult[aMinIdx].SquareDistance, 25.0, THE_TOL); // 3^2 + 4^2 = 25
EXPECT_TRUE(aResult[aMinIdx].IsMinimum);
}
TEST_F(ExtremaPC_CurveTest, Line_ProjectionOutsideBounds)
{
// Line along X axis with bounded domain
gp_Lin aLine(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0));
// Point that projects outside bounds
gp_Pnt aPoint(-50.0, 3.0, 4.0);
ExtremaPC_Line anEval(aLine, ExtremaPC::Domain1D{0.0, 100.0});
const ExtremaPC::Result& aResult = anEval.PerformWithEndpoints(aPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
ASSERT_EQ(aResult.NbExt(), 2); // min at 0, max at 100
size_t aMinIdx = aResult.MinIndex();
// Should be at endpoint U=0
EXPECT_NEAR(aResult[aMinIdx].Parameter, 0.0, THE_TOL);
}
//==================================================================================================
// Circle tests
//==================================================================================================
TEST_F(ExtremaPC_CurveTest, Circle_PointOutside)
{
// Circle with radius 10 in XY plane
gp_Circ aCircle(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 10.0);
// Point outside circle
gp_Pnt aPoint(20.0, 0.0, 0.0);
ExtremaPC_Circle anEval(aCircle, ExtremaPC::Domain1D{0.0, 2.0 * M_PI});
const ExtremaPC::Result& aResult = anEval.PerformWithEndpoints(aPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
ASSERT_EQ(aResult.NbExt(), 2);
// Find min and max
int aMinIdx = aResult[0].SquareDistance < aResult[1].SquareDistance ? 0 : 1;
int aMaxIdx = 1 - aMinIdx;
// Minimum should be at (10, 0, 0), distance = 10
EXPECT_NEAR(std::sqrt(aResult[aMinIdx].SquareDistance), 10.0, THE_TOL);
EXPECT_TRUE(aResult[aMinIdx].IsMinimum);
// Maximum should be at (-10, 0, 0), distance = 30
EXPECT_NEAR(std::sqrt(aResult[aMaxIdx].SquareDistance), 30.0, THE_TOL);
EXPECT_FALSE(aResult[aMaxIdx].IsMinimum);
}
TEST_F(ExtremaPC_CurveTest, Circle_PointInside)
{
// Circle with radius 10 in XY plane
gp_Circ aCircle(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 10.0);
// Point inside circle
gp_Pnt aPoint(3.0, 0.0, 0.0);
ExtremaPC_Circle anEval(aCircle);
const ExtremaPC::Result& aResult = anEval.PerformWithEndpoints(aPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
ASSERT_EQ(aResult.NbExt(), 2);
// Find min distance
double aMinDist =
std::min(std::sqrt(aResult[0].SquareDistance), std::sqrt(aResult[1].SquareDistance));
// Minimum distance should be 10 - 3 = 7
EXPECT_NEAR(aMinDist, 7.0, THE_TOL);
}
TEST_F(ExtremaPC_CurveTest, Circle_PointAtCenter_Degenerate)
{
// Circle with radius 10 in XY plane
gp_Circ aCircle(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 10.0);
// Point at center - degenerate case
gp_Pnt aPoint(0.0, 0.0, 0.0);
ExtremaPC_Circle anEval(aCircle);
const ExtremaPC::Result& aResult = anEval.PerformWithEndpoints(aPoint, THE_TOL);
// Should return infinite solutions
EXPECT_TRUE(aResult.IsInfinite());
EXPECT_NEAR(std::sqrt(aResult.InfiniteSquareDistance), 10.0, THE_TOL);
}
//==================================================================================================
// Aggregator tests with GeomAdaptor_Curve
//==================================================================================================
TEST_F(ExtremaPC_CurveTest, Aggregator_Line)
{
occ::handle<Geom_Line> aGeomLine = new Geom_Line(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0));
GeomAdaptor_Curve anAdaptor(aGeomLine, -100.0, 100.0);
ExtremaPC_Curve anExtPC(anAdaptor);
EXPECT_TRUE(anExtPC.IsInitialized());
gp_Pnt aPoint(5.0, 3.0, 4.0);
const ExtremaPC::Result& aResult = anExtPC.PerformWithEndpoints(aPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
ASSERT_EQ(aResult.NbExt(), 3); // 1 min + 2 max (endpoints)
size_t aMinIdx = aResult.MinIndex();
EXPECT_NEAR(aResult[aMinIdx].Parameter, 5.0, THE_TOL);
EXPECT_NEAR(aResult[aMinIdx].SquareDistance, 25.0, THE_TOL);
}
TEST_F(ExtremaPC_CurveTest, Aggregator_Circle)
{
occ::handle<Geom_Circle> aGeomCircle =
new Geom_Circle(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 10.0);
GeomAdaptor_Curve anAdaptor(aGeomCircle);
ExtremaPC_Curve anExtPC(anAdaptor);
EXPECT_TRUE(anExtPC.IsInitialized());
gp_Pnt aPoint(20.0, 0.0, 0.0);
const ExtremaPC::Result& aResult = anExtPC.Perform(aPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
EXPECT_GE(aResult.NbExt(), 1);
// Check minimum distance
double aMinSqDist = aResult.MinSquareDistance();
EXPECT_NEAR(std::sqrt(aMinSqDist), 10.0, THE_TOL);
}
TEST_F(ExtremaPC_CurveTest, Aggregator_BSpline)
{
// Create a simple cubic BSpline
NCollection_Array1<gp_Pnt> 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);
NCollection_Array1<double> aKnots(1, 2);
aKnots(1) = 0.0;
aKnots(2) = 1.0;
NCollection_Array1<int> aMults(1, 2);
aMults(1) = 4;
aMults(2) = 4;
occ::handle<Geom_BSplineCurve> aBSpline = new Geom_BSplineCurve(aPoles, aKnots, aMults, 3);
GeomAdaptor_Curve anAdaptor(aBSpline);
ExtremaPC_Curve anExtPC(anAdaptor);
EXPECT_TRUE(anExtPC.IsInitialized());
// Point near the curve
gp_Pnt aPoint(1.5, 3.0, 0.0);
const ExtremaPC::Result& aResult = anExtPC.Perform(aPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
// Should find at least one extremum
EXPECT_GE(aResult.NbExt(), 0);
}
TEST_F(ExtremaPC_CurveTest, Aggregator_Ellipse)
{
occ::handle<Geom_Ellipse> aGeomEllipse =
new Geom_Ellipse(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 20.0, 10.0);
GeomAdaptor_Curve anAdaptor(aGeomEllipse);
ExtremaPC_Curve anExtPC(anAdaptor);
EXPECT_TRUE(anExtPC.IsInitialized());
// Point on major axis
gp_Pnt aPoint(30.0, 0.0, 0.0);
const ExtremaPC::Result& aResult = anExtPC.Perform(aPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
EXPECT_GE(aResult.NbExt(), 1);
// Minimum distance should be 30 - 20 = 10
double aMinDist = std::sqrt(aResult.MinSquareDistance());
EXPECT_NEAR(aMinDist, 10.0, THE_TOL);
}
TEST_F(ExtremaPC_CurveTest, Aggregator_Parabola)
{
// y^2 = 4*p*x with p = 2, so y^2 = 8x
occ::handle<Geom_Parabola> aGeomParabola =
new Geom_Parabola(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 2.0);
GeomAdaptor_Curve anAdaptor(aGeomParabola, -10.0, 10.0);
ExtremaPC_Curve anExtPC(anAdaptor);
EXPECT_TRUE(anExtPC.IsInitialized());
// Point on X axis at focus
gp_Pnt aPoint(2.0, 0.0, 0.0);
const ExtremaPC::Result& aResult = anExtPC.Perform(aPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
EXPECT_GE(aResult.NbExt(), 1);
// Focus is at (p, 0) = (2, 0), vertex is at origin
// Minimum distance should be 2.0
double aMinDist = std::sqrt(aResult.MinSquareDistance());
EXPECT_NEAR(aMinDist, 2.0, THE_TOL);
}
TEST_F(ExtremaPC_CurveTest, Aggregator_Hyperbola)
{
// Hyperbola with major radius 10, minor radius 5
// x = a*cosh(u), y = b*sinh(u)
occ::handle<Geom_Hyperbola> aGeomHyperbola =
new Geom_Hyperbola(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 10.0, 5.0);
GeomAdaptor_Curve anAdaptor(aGeomHyperbola, -2.0, 2.0);
ExtremaPC_Curve anExtPC(anAdaptor);
EXPECT_TRUE(anExtPC.IsInitialized());
// Point near the vertex
gp_Pnt aPoint(10.0, 5.0, 0.0);
const ExtremaPC::Result& aResult = anExtPC.Perform(aPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
EXPECT_GE(aResult.NbExt(), 1);
// Verify that stored distance matches computed distance
for (size_t i = 0; i < aResult.NbExt(); ++i)
{
double aStoredSqDist = aResult[i].SquareDistance;
double aComputedSqDist = aPoint.SquareDistance(aResult[i].Point);
EXPECT_NEAR(aStoredSqDist, aComputedSqDist, THE_TOL);
}
}
TEST_F(ExtremaPC_CurveTest, Aggregator_Bezier)
{
// Create a cubic Bezier curve
NCollection_Array1<gp_Pnt> aPoles(1, 4);
aPoles(1) = gp_Pnt(0, 0, 0);
aPoles(2) = gp_Pnt(1, 3, 0);
aPoles(3) = gp_Pnt(3, 3, 0);
aPoles(4) = gp_Pnt(4, 0, 0);
occ::handle<Geom_BezierCurve> aBezier = new Geom_BezierCurve(aPoles);
GeomAdaptor_Curve anAdaptor(aBezier);
ExtremaPC_Curve anExtPC(anAdaptor);
EXPECT_TRUE(anExtPC.IsInitialized());
// Point above the curve's peak
gp_Pnt aPoint(2.0, 5.0, 0.0);
const ExtremaPC::Result& aResult = anExtPC.Perform(aPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
EXPECT_GE(aResult.NbExt(), 1);
// Verify that stored distance matches computed distance
for (size_t i = 0; i < aResult.NbExt(); ++i)
{
double aStoredSqDist = aResult[i].SquareDistance;
double aComputedSqDist = aPoint.SquareDistance(aResult[i].Point);
EXPECT_NEAR(aStoredSqDist, aComputedSqDist, THE_TOL);
}
}
TEST_F(ExtremaPC_CurveTest, Aggregator_OffsetCurve)
{
// Create a circle and its offset
occ::handle<Geom_Circle> aCircle =
new Geom_Circle(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 10.0);
occ::handle<Geom_OffsetCurve> anOffsetCurve = new Geom_OffsetCurve(aCircle, 5.0, gp_Dir(0, 0, 1));
GeomAdaptor_Curve anAdaptor(anOffsetCurve);
ExtremaPC_Curve anExtPC(anAdaptor);
EXPECT_TRUE(anExtPC.IsInitialized());
// Point outside the offset circle
gp_Pnt aPoint(25.0, 0.0, 0.0);
const ExtremaPC::Result& aResult = anExtPC.Perform(aPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
EXPECT_GE(aResult.NbExt(), 1);
// Offset circle has radius 15, distance should be 25 - 15 = 10
double aMinDist = std::sqrt(aResult.MinSquareDistance());
EXPECT_NEAR(aMinDist, 10.0, THE_TOL);
}
TEST_F(ExtremaPC_CurveTest, Aggregator_TrimmedCurve)
{
// Create a trimmed circle (quarter arc)
occ::handle<Geom_Circle> aCircle =
new Geom_Circle(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 10.0);
occ::handle<Geom_TrimmedCurve> aTrimmedCurve = new Geom_TrimmedCurve(aCircle, 0.0, M_PI / 2.0);
GeomAdaptor_Curve anAdaptor(aTrimmedCurve);
ExtremaPC_Curve anExtPC(anAdaptor);
EXPECT_TRUE(anExtPC.IsInitialized());
// Point in first quadrant
gp_Pnt aPoint(15.0, 15.0, 0.0);
const ExtremaPC::Result& aResult = anExtPC.Perform(aPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
EXPECT_GE(aResult.NbExt(), 1);
// Check that all extrema are within bounds
for (size_t i = 0; i < aResult.NbExt(); ++i)
{
EXPECT_GE(aResult[i].Parameter, -THE_TOL);
EXPECT_LE(aResult[i].Parameter, M_PI / 2.0 + THE_TOL);
}
}
//==================================================================================================
// Edge case tests
//==================================================================================================
TEST_F(ExtremaPC_CurveTest, Aggregator_PointOnCurve)
{
occ::handle<Geom_Circle> aGeomCircle =
new Geom_Circle(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 10.0);
GeomAdaptor_Curve anAdaptor(aGeomCircle);
ExtremaPC_Curve anExtPC(anAdaptor);
// Point exactly on the circle
gp_Pnt aPoint(10.0, 0.0, 0.0);
const ExtremaPC::Result& aResult = anExtPC.Perform(aPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
EXPECT_GE(aResult.NbExt(), 1);
// Distance should be ~0
double aMinDist = std::sqrt(aResult.MinSquareDistance());
EXPECT_NEAR(aMinDist, 0.0, THE_TOL);
}
TEST_F(ExtremaPC_CurveTest, Aggregator_IsInitialized)
{
occ::handle<Geom_Line> aGeomLine = new Geom_Line(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0));
GeomAdaptor_Curve anAdaptor(aGeomLine, 0.0, 100.0);
ExtremaPC_Curve anExtPC(anAdaptor);
EXPECT_TRUE(anExtPC.IsInitialized());
gp_Pnt aPoint(50.0, 5.0, 0.0);
const ExtremaPC::Result& aResult = anExtPC.Perform(aPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
EXPECT_GE(aResult.NbExt(), 1);
EXPECT_NEAR(aResult[0].Parameter, 50.0, THE_TOL);
}
//==================================================================================================
// Result utility tests
//==================================================================================================
TEST_F(ExtremaPC_CurveTest, Result_MinSquareDistance)
{
ExtremaPC::Result aResult;
aResult.Status = ExtremaPC::Status::OK;
// Add some extrema
ExtremaPC::ExtremumResult anExt1;
anExt1.SquareDistance = 100.0;
aResult.Extrema.Append(anExt1);
ExtremaPC::ExtremumResult anExt2;
anExt2.SquareDistance = 50.0;
aResult.Extrema.Append(anExt2);
ExtremaPC::ExtremumResult anExt3;
anExt3.SquareDistance = 75.0;
aResult.Extrema.Append(anExt3);
EXPECT_NEAR(aResult.MinSquareDistance(), 50.0, THE_TOL);
EXPECT_EQ(aResult.MinIndex(), 1);
}
TEST_F(ExtremaPC_CurveTest, Result_Empty)
{
ExtremaPC::Result aResult;
EXPECT_FALSE(aResult.IsDone());
EXPECT_EQ(aResult.NbExt(), 0);
EXPECT_EQ(aResult.MinIndex(), ExtremaPC::Result::INVALID_INDEX);
EXPECT_TRUE(std::isinf(aResult.MinSquareDistance()));
}
//==================================================================================================
// SearchMode tests
//==================================================================================================
TEST_F(ExtremaPC_CurveTest, Circle_SearchMode_MinMax)
{
// Circle with radius 10 in XY plane
gp_Circ aCircle(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 10.0);
// Point outside circle
gp_Pnt aPoint(20.0, 0.0, 0.0);
ExtremaPC_Circle anEval(aCircle);
const ExtremaPC::Result& aResult =
anEval.PerformWithEndpoints(aPoint, THE_TOL, ExtremaPC::SearchMode::MinMax);
ASSERT_TRUE(aResult.IsDone());
EXPECT_EQ(aResult.NbExt(), 2); // Both min and max
}
TEST_F(ExtremaPC_CurveTest, Circle_SearchMode_Min)
{
// Circle with radius 10 in XY plane
gp_Circ aCircle(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 10.0);
// Point outside circle
gp_Pnt aPoint(20.0, 0.0, 0.0);
ExtremaPC_Circle anEval(aCircle);
const ExtremaPC::Result& aResult =
anEval.PerformWithEndpoints(aPoint, THE_TOL, ExtremaPC::SearchMode::Min);
ASSERT_TRUE(aResult.IsDone());
EXPECT_EQ(aResult.NbExt(), 1); // Only min
EXPECT_TRUE(aResult[0].IsMinimum);
EXPECT_NEAR(std::sqrt(aResult[0].SquareDistance), 10.0, THE_TOL); // 20 - 10 = 10
}
TEST_F(ExtremaPC_CurveTest, Circle_SearchMode_Max)
{
// Circle with radius 10 in XY plane
gp_Circ aCircle(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 10.0);
// Point outside circle
gp_Pnt aPoint(20.0, 0.0, 0.0);
ExtremaPC_Circle anEval(aCircle);
const ExtremaPC::Result& aResult =
anEval.PerformWithEndpoints(aPoint, THE_TOL, ExtremaPC::SearchMode::Max);
ASSERT_TRUE(aResult.IsDone());
EXPECT_EQ(aResult.NbExt(), 1); // Only max
EXPECT_FALSE(aResult[0].IsMinimum);
EXPECT_NEAR(std::sqrt(aResult[0].SquareDistance), 30.0, THE_TOL); // 20 + 10 = 30
}
TEST_F(ExtremaPC_CurveTest, Aggregator_SearchMode_Min)
{
occ::handle<Geom_Circle> aGeomCircle =
new Geom_Circle(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 10.0);
GeomAdaptor_Curve anAdaptor(aGeomCircle);
ExtremaPC_Curve anExtPC(anAdaptor);
gp_Pnt aPoint(20.0, 0.0, 0.0);
const ExtremaPC::Result& aResult = anExtPC.Perform(aPoint, THE_TOL, ExtremaPC::SearchMode::Min);
ASSERT_TRUE(aResult.IsDone());
EXPECT_EQ(aResult.NbExt(), 1); // Only min
EXPECT_TRUE(aResult[0].IsMinimum);
EXPECT_NEAR(std::sqrt(aResult[0].SquareDistance), 10.0, THE_TOL);
}
TEST_F(ExtremaPC_CurveTest, Aggregator_SearchMode_Max)
{
occ::handle<Geom_Circle> aGeomCircle =
new Geom_Circle(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 10.0);
GeomAdaptor_Curve anAdaptor(aGeomCircle);
ExtremaPC_Curve anExtPC(anAdaptor);
gp_Pnt aPoint(20.0, 0.0, 0.0);
const ExtremaPC::Result& aResult = anExtPC.Perform(aPoint, THE_TOL, ExtremaPC::SearchMode::Max);
ASSERT_TRUE(aResult.IsDone());
EXPECT_EQ(aResult.NbExt(), 1); // Only max
EXPECT_FALSE(aResult[0].IsMinimum);
EXPECT_NEAR(std::sqrt(aResult[0].SquareDistance), 30.0, THE_TOL);
}
TEST_F(ExtremaPC_CurveTest, Aggregator_BSpline_SearchMode_Min)
{
// Create a simple cubic BSpline
NCollection_Array1<gp_Pnt> 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);
NCollection_Array1<double> aKnots(1, 2);
aKnots(1) = 0.0;
aKnots(2) = 1.0;
NCollection_Array1<int> aMults(1, 2);
aMults(1) = 4;
aMults(2) = 4;
occ::handle<Geom_BSplineCurve> aBSpline = new Geom_BSplineCurve(aPoles, aKnots, aMults, 3);
GeomAdaptor_Curve anAdaptor(aBSpline);
ExtremaPC_Curve anExtPC(anAdaptor);
// Point near the curve
gp_Pnt aPoint(1.5, 3.0, 0.0);
const ExtremaPC::Result& aResult = anExtPC.Perform(aPoint, THE_TOL, ExtremaPC::SearchMode::Min);
ASSERT_TRUE(aResult.IsDone());
// In Min mode, should only return minimum extrema
for (size_t i = 0; i < aResult.NbExt(); ++i)
{
EXPECT_TRUE(aResult[i].IsMinimum);
}
}
TEST_F(ExtremaPC_CurveTest, Result_MaxSquareDistance)
{
ExtremaPC::Result aResult;
aResult.Status = ExtremaPC::Status::OK;
// Add some extrema
ExtremaPC::ExtremumResult anExt1;
anExt1.SquareDistance = 100.0;
anExt1.IsMinimum = false;
aResult.Extrema.Append(anExt1);
ExtremaPC::ExtremumResult anExt2;
anExt2.SquareDistance = 50.0;
anExt2.IsMinimum = true;
aResult.Extrema.Append(anExt2);
ExtremaPC::ExtremumResult anExt3;
anExt3.SquareDistance = 75.0;
anExt3.IsMinimum = false;
aResult.Extrema.Append(anExt3);
EXPECT_NEAR(aResult.MinSquareDistance(), 50.0, THE_TOL);
EXPECT_EQ(aResult.MinIndex(), 1);
EXPECT_NEAR(aResult.MaxSquareDistance(), 100.0, THE_TOL);
EXPECT_EQ(aResult.MaxIndex(), 0);
}
//==================================================================================================
// GeomAdaptor_TransformedCurve tests - helpers
//==================================================================================================
namespace
{
//! Helper: create a cubic BSpline for reuse across tests.
occ::handle<Geom_BSplineCurve> MakeTestBSpline()
{
NCollection_Array1<gp_Pnt> 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);
NCollection_Array1<double> aKnots(1, 2);
aKnots(1) = 0.0;
aKnots(2) = 1.0;
NCollection_Array1<int> aMults(1, 2);
aMults(1) = 4;
aMults(2) = 4;
return new Geom_BSplineCurve(aPoles, aKnots, aMults, 3);
}
//! Helper: create a cubic Bezier for reuse across tests.
occ::handle<Geom_BezierCurve> MakeTestBezier()
{
NCollection_Array1<gp_Pnt> aPoles(1, 4);
aPoles(1) = gp_Pnt(0, 0, 0);
aPoles(2) = gp_Pnt(1, 3, 0);
aPoles(3) = gp_Pnt(3, 3, 0);
aPoles(4) = gp_Pnt(4, 0, 0);
return new Geom_BezierCurve(aPoles);
}
} // namespace
//==================================================================================================
// GeomAdaptor_TransformedCurve - Identity transform
//==================================================================================================
TEST_F(ExtremaPC_CurveTest, TransformedCurve_Identity_LineMatchesNonTransformed)
{
occ::handle<Geom_Line> aGeomLine = new Geom_Line(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0));
GeomAdaptor_Curve anAdaptor(aGeomLine, -100.0, 100.0);
ExtremaPC_Curve anExtPC(anAdaptor);
gp_Trsf anIdentity;
GeomAdaptor_TransformedCurve aTransformed(aGeomLine, -100.0, 100.0, anIdentity);
ExtremaPC_Curve anExtPCTr(aTransformed);
EXPECT_TRUE(anExtPC.IsInitialized());
EXPECT_TRUE(anExtPCTr.IsInitialized());
gp_Pnt aPoint(5.0, 3.0, 4.0);
const ExtremaPC::Result& aResult = anExtPC.Perform(aPoint, THE_TOL);
const ExtremaPC::Result& aResultTr = anExtPCTr.Perform(aPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
ASSERT_TRUE(aResultTr.IsDone());
ASSERT_EQ(aResult.NbExt(), aResultTr.NbExt());
EXPECT_NEAR(aResult.MinSquareDistance(), aResultTr.MinSquareDistance(), THE_TOL);
}
TEST_F(ExtremaPC_CurveTest, TransformedCurve_Identity_BSplineMatchesNonTransformed)
{
occ::handle<Geom_BSplineCurve> aBSpline = MakeTestBSpline();
GeomAdaptor_Curve anAdaptor(aBSpline);
ExtremaPC_Curve anExtPC(anAdaptor);
gp_Trsf anIdentity;
GeomAdaptor_TransformedCurve aTransformed(aBSpline, anIdentity);
ExtremaPC_Curve anExtPCTr(aTransformed);
gp_Pnt aPoint(1.5, 3.0, 0.0);
const ExtremaPC::Result& aResult = anExtPC.Perform(aPoint, THE_TOL);
const ExtremaPC::Result& aResultTr = anExtPCTr.Perform(aPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
ASSERT_TRUE(aResultTr.IsDone());
EXPECT_NEAR(aResult.MinSquareDistance(), aResultTr.MinSquareDistance(), THE_TOL);
}
//==================================================================================================
// GeomAdaptor_TransformedCurve - Translation
//==================================================================================================
TEST_F(ExtremaPC_CurveTest, TransformedCurve_Translation_Line)
{
occ::handle<Geom_Line> aGeomLine = new Geom_Line(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0));
gp_Trsf aTrsf;
aTrsf.SetTranslation(gp_Vec(0, 10, 0));
GeomAdaptor_TransformedCurve aTransformed(aGeomLine, -100.0, 100.0, aTrsf);
ExtremaPC_Curve anExtPC(aTransformed);
EXPECT_TRUE(anExtPC.IsInitialized());
// Point at (5, 13, 4): closest to translated line at (5, 10, 0), distance = sqrt(9+16) = 5
gp_Pnt aPoint(5.0, 13.0, 4.0);
const ExtremaPC::Result& aResult = anExtPC.Perform(aPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
EXPECT_GE(aResult.NbExt(), 1);
size_t aMinIdx = aResult.MinIndex();
EXPECT_NEAR(aResult[aMinIdx].Parameter, 5.0, THE_TOL);
EXPECT_NEAR(aResult[aMinIdx].SquareDistance, 25.0, THE_TOL);
EXPECT_NEAR(aResult[aMinIdx].Point.Y(), 10.0, THE_TOL);
}
TEST_F(ExtremaPC_CurveTest, TransformedCurve_Translation_Circle)
{
occ::handle<Geom_Circle> aGeomCircle =
new Geom_Circle(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 10.0);
gp_Trsf aTrsf;
aTrsf.SetTranslation(gp_Vec(100, 0, 0));
GeomAdaptor_TransformedCurve aTransformed(aGeomCircle, aTrsf);
ExtremaPC_Curve anExtPC(aTransformed);
// Circle center at (100,0,0), closest at (110,0,0), dist = 10
gp_Pnt aPoint(120.0, 0.0, 0.0);
const ExtremaPC::Result& aResult = anExtPC.Perform(aPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
EXPECT_GE(aResult.NbExt(), 1);
EXPECT_NEAR(std::sqrt(aResult.MinSquareDistance()), 10.0, THE_TOL);
}
TEST_F(ExtremaPC_CurveTest, TransformedCurve_Translation_BSpline)
{
occ::handle<Geom_BSplineCurve> aBSpline = MakeTestBSpline();
GeomAdaptor_Curve anAdaptor(aBSpline);
ExtremaPC_Curve anExtPC(anAdaptor);
gp_Trsf aTrsf;
aTrsf.SetTranslation(gp_Vec(10, 20, 30));
GeomAdaptor_TransformedCurve aTransformed(aBSpline, aTrsf);
ExtremaPC_Curve anExtPCTr(aTransformed);
gp_Pnt aOrigPoint(1.5, 3.0, 0.0);
gp_Pnt aTransPoint(11.5, 23.0, 30.0);
const ExtremaPC::Result& aResult = anExtPC.Perform(aOrigPoint, THE_TOL);
const ExtremaPC::Result& aResultTr = anExtPCTr.Perform(aTransPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
ASSERT_TRUE(aResultTr.IsDone());
ASSERT_EQ(aResult.NbExt(), aResultTr.NbExt());
EXPECT_NEAR(aResult.MinSquareDistance(), aResultTr.MinSquareDistance(), THE_TOL);
size_t aMinIdx = aResult.MinIndex();
size_t aMinIdxTr = aResultTr.MinIndex();
EXPECT_NEAR(aResult[aMinIdx].Parameter, aResultTr[aMinIdxTr].Parameter, THE_TOL);
EXPECT_NEAR(aResultTr[aMinIdxTr].Point.X(), aResult[aMinIdx].Point.X() + 10.0, THE_TOL);
EXPECT_NEAR(aResultTr[aMinIdxTr].Point.Y(), aResult[aMinIdx].Point.Y() + 20.0, THE_TOL);
EXPECT_NEAR(aResultTr[aMinIdxTr].Point.Z(), aResult[aMinIdx].Point.Z() + 30.0, THE_TOL);
}
//==================================================================================================
// GeomAdaptor_TransformedCurve - Rotation
//==================================================================================================
TEST_F(ExtremaPC_CurveTest, TransformedCurve_Rotation_Line)
{
occ::handle<Geom_Line> aGeomLine = new Geom_Line(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0));
// Rotate 90 degrees around Z -> line becomes along Y
gp_Trsf aTrsf;
aTrsf.SetRotation(gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), M_PI / 2.0);
GeomAdaptor_TransformedCurve aTransformed(aGeomLine, 0.0, 100.0, aTrsf);
ExtremaPC_Curve anExtPC(aTransformed);
// Point at (3, 5, 0): rotated line along Y, closest at (0, 5, 0), dist = 3
gp_Pnt aPoint(3.0, 5.0, 0.0);
const ExtremaPC::Result& aResult = anExtPC.Perform(aPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
EXPECT_GE(aResult.NbExt(), 1);
size_t aMinIdx = aResult.MinIndex();
EXPECT_NEAR(std::sqrt(aResult[aMinIdx].SquareDistance), 3.0, THE_TOL);
EXPECT_NEAR(aResult[aMinIdx].Point.X(), 0.0, THE_TOL);
EXPECT_NEAR(aResult[aMinIdx].Point.Y(), 5.0, THE_TOL);
}
TEST_F(ExtremaPC_CurveTest, TransformedCurve_Rotation_BSpline)
{
occ::handle<Geom_BSplineCurve> aBSpline = MakeTestBSpline();
GeomAdaptor_Curve anAdaptor(aBSpline);
ExtremaPC_Curve anExtPC(anAdaptor);
gp_Trsf aTrsf;
aTrsf.SetRotation(gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), M_PI / 2.0);
GeomAdaptor_TransformedCurve aTransformed(aBSpline, aTrsf);
ExtremaPC_Curve anExtPCTr(aTransformed);
gp_Pnt aOrigPoint(1.5, 3.0, 0.0);
gp_Pnt aTransPoint(-3.0, 1.5, 0.0);
const ExtremaPC::Result& aResult = anExtPC.Perform(aOrigPoint, THE_TOL);
const ExtremaPC::Result& aResultTr = anExtPCTr.Perform(aTransPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
ASSERT_TRUE(aResultTr.IsDone());
EXPECT_NEAR(aResult.MinSquareDistance(), aResultTr.MinSquareDistance(), THE_TOL);
size_t aMinIdx = aResult.MinIndex();
size_t aMinIdxTr = aResultTr.MinIndex();
EXPECT_NEAR(aResult[aMinIdx].Parameter, aResultTr[aMinIdxTr].Parameter, THE_TOL);
}
//==================================================================================================
// GeomAdaptor_TransformedCurve - Compound transform (rotation + translation)
//==================================================================================================
TEST_F(ExtremaPC_CurveTest, TransformedCurve_Compound_BSpline)
{
occ::handle<Geom_BSplineCurve> aBSpline = MakeTestBSpline();
GeomAdaptor_Curve anAdaptor(aBSpline);
ExtremaPC_Curve anExtPC(anAdaptor);
gp_Trsf aTrsf;
aTrsf.SetRotation(gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), M_PI / 4.0);
gp_Trsf aTransl;
aTransl.SetTranslation(gp_Vec(5, 5, 0));
aTrsf.Multiply(aTransl);
GeomAdaptor_TransformedCurve aTransformed(aBSpline, aTrsf);
ExtremaPC_Curve anExtPCTr(aTransformed);
gp_Pnt aOrigPoint(1.5, 3.0, 0.0);
gp_Pnt aTransPoint = aOrigPoint.Transformed(aTrsf);
const ExtremaPC::Result& aResult = anExtPC.Perform(aOrigPoint, THE_TOL);
const ExtremaPC::Result& aResultTr = anExtPCTr.Perform(aTransPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
ASSERT_TRUE(aResultTr.IsDone());
EXPECT_NEAR(aResult.MinSquareDistance(), aResultTr.MinSquareDistance(), THE_TOL);
size_t aMinIdx = aResult.MinIndex();
size_t aMinIdxTr = aResultTr.MinIndex();
EXPECT_NEAR(aResult[aMinIdx].Parameter, aResultTr[aMinIdxTr].Parameter, THE_TOL);
gp_Pnt aExpectedPt = aResult[aMinIdx].Point.Transformed(aTrsf);
EXPECT_NEAR(aResultTr[aMinIdxTr].Point.X(), aExpectedPt.X(), THE_TOL);
EXPECT_NEAR(aResultTr[aMinIdxTr].Point.Y(), aExpectedPt.Y(), THE_TOL);
EXPECT_NEAR(aResultTr[aMinIdxTr].Point.Z(), aExpectedPt.Z(), THE_TOL);
}
//==================================================================================================
// GeomAdaptor_TransformedCurve - Uniform scale
//==================================================================================================
TEST_F(ExtremaPC_CurveTest, TransformedCurve_Scale_BSpline)
{
occ::handle<Geom_BSplineCurve> aBSpline = MakeTestBSpline();
GeomAdaptor_Curve anAdaptor(aBSpline);
ExtremaPC_Curve anExtPC(anAdaptor);
gp_Trsf aTrsf;
aTrsf.SetScale(gp_Pnt(0, 0, 0), 2.0);
GeomAdaptor_TransformedCurve aTransformed(aBSpline, aTrsf);
ExtremaPC_Curve anExtPCTr(aTransformed);
gp_Pnt aOrigPoint(1.5, 3.0, 0.0);
gp_Pnt aScaledPoint(3.0, 6.0, 0.0);
const ExtremaPC::Result& aResult = anExtPC.Perform(aOrigPoint, THE_TOL);
const ExtremaPC::Result& aResultTr = anExtPCTr.Perform(aScaledPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
ASSERT_TRUE(aResultTr.IsDone());
// Distances scale by s^2 = 4
EXPECT_NEAR(aResultTr.MinSquareDistance(), aResult.MinSquareDistance() * 4.0, THE_TOL);
size_t aMinIdx = aResult.MinIndex();
size_t aMinIdxTr = aResultTr.MinIndex();
EXPECT_NEAR(aResult[aMinIdx].Parameter, aResultTr[aMinIdxTr].Parameter, THE_TOL);
}
TEST_F(ExtremaPC_CurveTest, TransformedCurve_Scale_Bezier)
{
occ::handle<Geom_BezierCurve> aBezier = MakeTestBezier();
GeomAdaptor_Curve anAdaptor(aBezier);
ExtremaPC_Curve anExtPC(anAdaptor);
gp_Trsf aTrsf;
aTrsf.SetScale(gp_Pnt(0, 0, 0), 3.0);
GeomAdaptor_TransformedCurve aTransformed(aBezier, aTrsf);
ExtremaPC_Curve anExtPCTr(aTransformed);
gp_Pnt aOrigPoint(2.0, 5.0, 0.0);
gp_Pnt aScaledPoint(6.0, 15.0, 0.0);
const ExtremaPC::Result& aResult = anExtPC.Perform(aOrigPoint, THE_TOL);
const ExtremaPC::Result& aResultTr = anExtPCTr.Perform(aScaledPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
ASSERT_TRUE(aResultTr.IsDone());
// Distances scale by s^2 = 9
EXPECT_NEAR(aResultTr.MinSquareDistance(), aResult.MinSquareDistance() * 9.0, 1.0e-4);
}
TEST_F(ExtremaPC_CurveTest, TransformedCurve_CompoundScale_LinePreservesParameter)
{
occ::handle<Geom_Line> aLine = new Geom_Line(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0));
gp_Trsf aTrsf;
aTrsf.SetScale(gp_Pnt(0, 0, 0), 2.0);
gp_Trsf aRotation;
aRotation.SetRotation(gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), M_PI / 2.0);
aTrsf.PreMultiply(aRotation);
gp_Trsf aTranslation;
aTranslation.SetTranslation(gp_Vec(5, 7, 0));
aTrsf.PreMultiply(aTranslation);
GeomAdaptor_TransformedCurve aTransformed(aLine, 0.0, 10.0, aTrsf);
ExtremaPC_Curve anExtPC(aTransformed);
const gp_Pnt aQuery = gp_Pnt(4, 7, 0).Transformed(aTrsf);
const ExtremaPC::Result& aResult = anExtPC.Perform(aQuery, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
ASSERT_EQ(aResult.NbExt(), 1);
EXPECT_NEAR(aResult[0].Parameter, 4.0, THE_TOL);
EXPECT_NEAR(aResult[0].Point.Distance(aTransformed.Value(aResult[0].Parameter)), 0.0, THE_TOL);
}
TEST_F(ExtremaPC_CurveTest, TransformedCurve_CompoundScale_ParabolaPreservesParameter)
{
occ::handle<Geom_Parabola> aParabola =
new Geom_Parabola(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 5.0);
gp_Trsf aTrsf;
aTrsf.SetScale(gp_Pnt(0, 0, 0), 3.0);
gp_Trsf aTranslation;
aTranslation.SetTranslation(gp_Vec(10, -4, 2));
aTrsf.PreMultiply(aTranslation);
GeomAdaptor_TransformedCurve aTransformed(aParabola, -10.0, 10.0, aTrsf);
ExtremaPC_Curve anExtPC(aTransformed);
constexpr double aParameter = 3.0;
const gp_Pnt aQuery = aTransformed.Value(aParameter);
const ExtremaPC::Result& aResult = anExtPC.Perform(aQuery, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
ASSERT_GE(aResult.NbExt(), 1);
const size_t aMinIndex = aResult.MinIndex();
EXPECT_NEAR(aResult[aMinIndex].Parameter, aParameter, THE_TOL);
EXPECT_NEAR(aResult[aMinIndex].Point.Distance(aQuery), 0.0, THE_TOL);
}
//==================================================================================================
// GeomAdaptor_TransformedCurve - Offset curve via Adaptor3d_Curve interface
//==================================================================================================
TEST_F(ExtremaPC_CurveTest, TransformedCurve_Translation_OffsetCurve)
{
occ::handle<Geom_Circle> aCircle =
new Geom_Circle(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 10.0);
occ::handle<Geom_OffsetCurve> anOffsetCurve = new Geom_OffsetCurve(aCircle, 5.0, gp_Dir(0, 0, 1));
gp_Trsf aTrsf;
aTrsf.SetTranslation(gp_Vec(50, 0, 0));
GeomAdaptor_TransformedCurve aTransformed(anOffsetCurve, aTrsf);
ExtremaPC_Curve anExtPC(aTransformed);
EXPECT_TRUE(anExtPC.IsInitialized());
// Offset circle radius 15, center (50, 0, 0), point at (75, 0, 0): dist = 10
gp_Pnt aPoint(75.0, 0.0, 0.0);
const ExtremaPC::Result& aResult = anExtPC.Perform(aPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
EXPECT_GE(aResult.NbExt(), 1);
EXPECT_NEAR(std::sqrt(aResult.MinSquareDistance()), 10.0, THE_TOL);
}
//==================================================================================================
// GeomAdaptor_TransformedCurve - Domain-limited
//==================================================================================================
TEST_F(ExtremaPC_CurveTest, TransformedCurve_DomainLimited_Circle)
{
occ::handle<Geom_Circle> aGeomCircle =
new Geom_Circle(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 10.0);
gp_Trsf aTrsf;
aTrsf.SetTranslation(gp_Vec(0, 0, 50));
GeomAdaptor_TransformedCurve aTransformed(aGeomCircle, 0.0, M_PI / 2.0, aTrsf);
ExtremaPC_Curve anExtPC(aTransformed);
gp_Pnt aPoint(15.0, 15.0, 50.0);
const ExtremaPC::Result& aResult = anExtPC.Perform(aPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
for (size_t i = 0; i < aResult.NbExt(); ++i)
{
EXPECT_GE(aResult[i].Parameter, -THE_TOL);
EXPECT_LE(aResult[i].Parameter, M_PI / 2.0 + THE_TOL);
EXPECT_NEAR(aResult[i].Point.Z(), 50.0, THE_TOL);
}
}
TEST_F(ExtremaPC_CurveTest, TransformedCurve_DomainLimited_Constructor)
{
occ::handle<Geom_Line> aGeomLine = new Geom_Line(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0));
gp_Trsf aTrsf;
aTrsf.SetTranslation(gp_Vec(0, 5, 0));
GeomAdaptor_TransformedCurve aTransformed(aGeomLine, aTrsf);
ExtremaPC_Curve anExtPC(aTransformed, 0.0, 10.0);
gp_Pnt aPoint(5.0, 8.0, 0.0);
const ExtremaPC::Result& aResult = anExtPC.PerformWithEndpoints(aPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
EXPECT_GE(aResult.NbExt(), 1);
size_t aMinIdx = aResult.MinIndex();
EXPECT_NEAR(aResult[aMinIdx].Parameter, 5.0, THE_TOL);
EXPECT_NEAR(aResult[aMinIdx].SquareDistance, 9.0, THE_TOL);
EXPECT_NEAR(aResult[aMinIdx].Point.Y(), 5.0, THE_TOL);
}
//==================================================================================================
// GeomAdaptor_TransformedCurve - PerformWithEndpoints
//==================================================================================================
TEST_F(ExtremaPC_CurveTest, TransformedCurve_PerformWithEndpoints_BSpline)
{
occ::handle<Geom_BSplineCurve> aBSpline = MakeTestBSpline();
GeomAdaptor_Curve anAdaptor(aBSpline);
ExtremaPC_Curve anExtPC(anAdaptor);
gp_Trsf aTrsf;
aTrsf.SetTranslation(gp_Vec(0, 0, 10));
GeomAdaptor_TransformedCurve aTransformed(aBSpline, aTrsf);
ExtremaPC_Curve anExtPCTr(aTransformed);
gp_Pnt aOrigPoint(1.5, 3.0, 0.0);
gp_Pnt aTransPoint(1.5, 3.0, 10.0);
const ExtremaPC::Result& aResult = anExtPC.PerformWithEndpoints(aOrigPoint, THE_TOL);
const ExtremaPC::Result& aResultTr = anExtPCTr.PerformWithEndpoints(aTransPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
ASSERT_TRUE(aResultTr.IsDone());
ASSERT_EQ(aResult.NbExt(), aResultTr.NbExt());
EXPECT_NEAR(aResult.MinSquareDistance(), aResultTr.MinSquareDistance(), THE_TOL);
}
TEST_F(ExtremaPC_CurveTest, TransformedCurve_PerformWithEndpoints_ScaledBSpline)
{
occ::handle<Geom_BSplineCurve> aBSpline = MakeTestBSpline();
GeomAdaptor_Curve anAdaptor(aBSpline);
ExtremaPC_Curve anExtPC(anAdaptor);
gp_Trsf aTrsf;
aTrsf.SetScale(gp_Pnt(0, 0, 0), 2.0);
GeomAdaptor_TransformedCurve aTransformed(aBSpline, aTrsf);
ExtremaPC_Curve anExtPCTr(aTransformed);
gp_Pnt aOrigPoint(1.5, 3.0, 0.0);
gp_Pnt aScaledPoint(3.0, 6.0, 0.0);
const ExtremaPC::Result& aResult = anExtPC.PerformWithEndpoints(aOrigPoint, THE_TOL);
const ExtremaPC::Result& aResultTr = anExtPCTr.PerformWithEndpoints(aScaledPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
ASSERT_TRUE(aResultTr.IsDone());
EXPECT_NEAR(aResultTr.MinSquareDistance(), aResult.MinSquareDistance() * 4.0, THE_TOL);
}
//==================================================================================================
// GeomAdaptor_TransformedCurve - Bezier with transform
//==================================================================================================
TEST_F(ExtremaPC_CurveTest, TransformedCurve_Translation_Bezier)
{
occ::handle<Geom_BezierCurve> aBezier = MakeTestBezier();
GeomAdaptor_Curve anAdaptor(aBezier);
ExtremaPC_Curve anExtPC(anAdaptor);
gp_Trsf aTrsf;
aTrsf.SetTranslation(gp_Vec(10, 20, 30));
GeomAdaptor_TransformedCurve aTransformed(aBezier, aTrsf);
ExtremaPC_Curve anExtPCTr(aTransformed);
gp_Pnt aOrigPoint(2.0, 5.0, 0.0);
gp_Pnt aTransPoint(12.0, 25.0, 30.0);
const ExtremaPC::Result& aResult = anExtPC.Perform(aOrigPoint, THE_TOL);
const ExtremaPC::Result& aResultTr = anExtPCTr.Perform(aTransPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
ASSERT_TRUE(aResultTr.IsDone());
EXPECT_NEAR(aResult.MinSquareDistance(), aResultTr.MinSquareDistance(), THE_TOL);
size_t aMinIdx = aResult.MinIndex();
size_t aMinIdxTr = aResultTr.MinIndex();
gp_Pnt aExpectedPt = aResult[aMinIdx].Point.Transformed(aTrsf);
EXPECT_NEAR(aResultTr[aMinIdxTr].Point.X(), aExpectedPt.X(), THE_TOL);
EXPECT_NEAR(aResultTr[aMinIdxTr].Point.Y(), aExpectedPt.Y(), THE_TOL);
EXPECT_NEAR(aResultTr[aMinIdxTr].Point.Z(), aExpectedPt.Z(), THE_TOL);
}
TEST_F(ExtremaPC_CurveTest, TransformedCurve_Compound_Bezier)
{
occ::handle<Geom_BezierCurve> aBezier = MakeTestBezier();
GeomAdaptor_Curve anAdaptor(aBezier);
ExtremaPC_Curve anExtPC(anAdaptor);
gp_Trsf aTrsf;
aTrsf.SetRotation(gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), M_PI / 3.0);
gp_Trsf aTransl;
aTransl.SetTranslation(gp_Vec(10, 0, 0));
aTrsf.Multiply(aTransl);
GeomAdaptor_TransformedCurve aTransformed(aBezier, aTrsf);
ExtremaPC_Curve anExtPCTr(aTransformed);
gp_Pnt aOrigPoint(2.0, 5.0, 0.0);
gp_Pnt aTransPoint = aOrigPoint.Transformed(aTrsf);
const ExtremaPC::Result& aResult = anExtPC.Perform(aOrigPoint, THE_TOL);
const ExtremaPC::Result& aResultTr = anExtPCTr.Perform(aTransPoint, THE_TOL);
ASSERT_TRUE(aResult.IsDone());
ASSERT_TRUE(aResultTr.IsDone());
EXPECT_NEAR(aResult.MinSquareDistance(), aResultTr.MinSquareDistance(), THE_TOL);
}
//==================================================================================================
// GeomAdaptor_TransformedCurve - SearchMode with transform
//==================================================================================================
TEST_F(ExtremaPC_CurveTest, TransformedCurve_SearchMode_Min)
{
occ::handle<Geom_Circle> aGeomCircle =
new Geom_Circle(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 10.0);
gp_Trsf aTrsf;
aTrsf.SetTranslation(gp_Vec(0, 0, 5));
GeomAdaptor_TransformedCurve aTransformed(aGeomCircle, aTrsf);
ExtremaPC_Curve anExtPC(aTransformed);
gp_Pnt aPoint(20.0, 0.0, 5.0);
const ExtremaPC::Result& aResult = anExtPC.Perform(aPoint, THE_TOL, ExtremaPC::SearchMode::Min);
ASSERT_TRUE(aResult.IsDone());
EXPECT_EQ(aResult.NbExt(), 1);
EXPECT_TRUE(aResult[0].IsMinimum);
EXPECT_NEAR(std::sqrt(aResult[0].SquareDistance), 10.0, THE_TOL);
}
TEST_F(ExtremaPC_CurveTest, TransformedCurve_SearchMode_Max)
{
occ::handle<Geom_Circle> aGeomCircle =
new Geom_Circle(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 10.0);
gp_Trsf aTrsf;
aTrsf.SetTranslation(gp_Vec(0, 0, 5));
GeomAdaptor_TransformedCurve aTransformed(aGeomCircle, aTrsf);
ExtremaPC_Curve anExtPC(aTransformed);
gp_Pnt aPoint(20.0, 0.0, 5.0);
const ExtremaPC::Result& aResult = anExtPC.Perform(aPoint, THE_TOL, ExtremaPC::SearchMode::Max);
ASSERT_TRUE(aResult.IsDone());
EXPECT_EQ(aResult.NbExt(), 1);
EXPECT_FALSE(aResult[0].IsMinimum);
EXPECT_NEAR(std::sqrt(aResult[0].SquareDistance), 30.0, THE_TOL);
}