Modeling Data - Add GeomHash and Geom2dHash packages (#845)

- Implementation of hashers for analytic curves (Line, Circle, Ellipse, Hyperbola, Parabola) and freeform curves (Bezier, BSpline, Trimmed, Offset) in both 2D and 3D
- Implementation of hashers for surfaces including elementary surfaces (Plane, Cylinder, Cone, Sphere, Torus) and derived surfaces (Revolution, LinearExtrusion, RectangularTrimmed, Offset)
- Comprehensive test coverage for all hasher implementations
This commit is contained in:
Pasukhin Dmitry
2025-11-22 11:16:25 +00:00
committed by GitHub
parent 851ac10c12
commit 052c37669b
51 changed files with 4875 additions and 0 deletions

View File

@@ -8,4 +8,5 @@ set(OCCT_TKG2d_GTests_FILES
Geom2dAPI_InterCurveCurve_Test.cxx
Geom2dGcc_Circ2d2TanOn_Test.cxx
Geom2dGcc_Circ2d2TanRad_Test.cxx
Geom2dHash_CurveHasher_Test.cxx
)

View File

@@ -0,0 +1,654 @@
// 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 <gtest/gtest.h>
#include <Geom2dHash_CurveHasher.hxx>
#include <Geom2d_Line.hxx>
#include <Geom2d_Circle.hxx>
#include <Geom2d_Ellipse.hxx>
#include <Geom2d_Hyperbola.hxx>
#include <Geom2d_Parabola.hxx>
#include <Geom2d_BezierCurve.hxx>
#include <Geom2d_BSplineCurve.hxx>
#include <Geom2d_TrimmedCurve.hxx>
#include <Geom2d_OffsetCurve.hxx>
#include <gp_Ax2d.hxx>
#include <gp_Ax22d.hxx>
#include <gp_Pnt2d.hxx>
#include <gp_Dir2d.hxx>
#include <gp_Vec2d.hxx>
#include <TColgp_Array1OfPnt2d.hxx>
#include <TColStd_Array1OfReal.hxx>
#include <TColStd_Array1OfInteger.hxx>
class Geom2dHash_CurveHasherTest : public ::testing::Test
{
protected:
Geom2dHash_CurveHasher myHasher;
};
// ============================================================================
// Line Tests
// ============================================================================
TEST_F(Geom2dHash_CurveHasherTest, Line_CopiedLines_SameHash)
{
gp_Pnt2d aLoc(1.0, 2.0);
gp_Dir2d aDir(1.0, 0.0);
Handle(Geom2d_Line) aLine1 = new Geom2d_Line(aLoc, aDir);
Handle(Geom2d_Line) aLine2 = Handle(Geom2d_Line)::DownCast(aLine1->Copy());
EXPECT_EQ(myHasher(aLine1), myHasher(aLine2));
EXPECT_TRUE(myHasher(aLine1, aLine2));
}
TEST_F(Geom2dHash_CurveHasherTest, Line_DifferentLines_DifferentHash)
{
Handle(Geom2d_Line) aLine1 = new Geom2d_Line(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0));
Handle(Geom2d_Line) aLine2 = new Geom2d_Line(gp_Pnt2d(1.0, 0.0), gp_Dir2d(1.0, 0.0));
EXPECT_NE(myHasher(aLine1), myHasher(aLine2));
EXPECT_FALSE(myHasher(aLine1, aLine2));
}
// ============================================================================
// Circle Tests
// ============================================================================
TEST_F(Geom2dHash_CurveHasherTest, Circle_CopiedCircles_SameHash)
{
gp_Ax22d anAxis(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0), gp_Dir2d(0.0, 1.0));
Handle(Geom2d_Circle) aCircle1 = new Geom2d_Circle(anAxis, 5.0);
Handle(Geom2d_Circle) aCircle2 = Handle(Geom2d_Circle)::DownCast(aCircle1->Copy());
EXPECT_EQ(myHasher(aCircle1), myHasher(aCircle2));
EXPECT_TRUE(myHasher(aCircle1, aCircle2));
}
TEST_F(Geom2dHash_CurveHasherTest, Circle_DifferentRadius_DifferentHash)
{
gp_Ax22d anAxis(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0), gp_Dir2d(0.0, 1.0));
Handle(Geom2d_Circle) aCircle1 = new Geom2d_Circle(anAxis, 5.0);
Handle(Geom2d_Circle) aCircle2 = new Geom2d_Circle(anAxis, 10.0);
EXPECT_NE(myHasher(aCircle1), myHasher(aCircle2));
EXPECT_FALSE(myHasher(aCircle1, aCircle2));
}
// ============================================================================
// Ellipse Tests
// ============================================================================
TEST_F(Geom2dHash_CurveHasherTest, Ellipse_CopiedEllipses_SameHash)
{
gp_Ax22d anAxis(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0), gp_Dir2d(0.0, 1.0));
Handle(Geom2d_Ellipse) anEllipse1 = new Geom2d_Ellipse(anAxis, 10.0, 5.0);
Handle(Geom2d_Ellipse) anEllipse2 = Handle(Geom2d_Ellipse)::DownCast(anEllipse1->Copy());
EXPECT_EQ(myHasher(anEllipse1), myHasher(anEllipse2));
EXPECT_TRUE(myHasher(anEllipse1, anEllipse2));
}
TEST_F(Geom2dHash_CurveHasherTest, Ellipse_DifferentRadii_DifferentHash)
{
gp_Ax22d anAxis(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0), gp_Dir2d(0.0, 1.0));
Handle(Geom2d_Ellipse) anEllipse1 = new Geom2d_Ellipse(anAxis, 10.0, 5.0);
Handle(Geom2d_Ellipse) anEllipse2 = new Geom2d_Ellipse(anAxis, 10.0, 7.0);
EXPECT_NE(myHasher(anEllipse1), myHasher(anEllipse2));
EXPECT_FALSE(myHasher(anEllipse1, anEllipse2));
}
// ============================================================================
// Hyperbola Tests
// ============================================================================
TEST_F(Geom2dHash_CurveHasherTest, Hyperbola_CopiedHyperbolas_SameHash)
{
gp_Ax22d anAxis(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0), gp_Dir2d(0.0, 1.0));
Handle(Geom2d_Hyperbola) aHyp1 = new Geom2d_Hyperbola(anAxis, 5.0, 3.0);
Handle(Geom2d_Hyperbola) aHyp2 = Handle(Geom2d_Hyperbola)::DownCast(aHyp1->Copy());
EXPECT_EQ(myHasher(aHyp1), myHasher(aHyp2));
EXPECT_TRUE(myHasher(aHyp1, aHyp2));
}
TEST_F(Geom2dHash_CurveHasherTest, Hyperbola_DifferentRadii_DifferentHash)
{
gp_Ax22d anAxis(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0), gp_Dir2d(0.0, 1.0));
Handle(Geom2d_Hyperbola) aHyp1 = new Geom2d_Hyperbola(anAxis, 5.0, 3.0);
Handle(Geom2d_Hyperbola) aHyp2 = new Geom2d_Hyperbola(anAxis, 5.0, 4.0);
EXPECT_NE(myHasher(aHyp1), myHasher(aHyp2));
EXPECT_FALSE(myHasher(aHyp1, aHyp2));
}
// ============================================================================
// Parabola Tests
// ============================================================================
TEST_F(Geom2dHash_CurveHasherTest, Parabola_CopiedParabolas_SameHash)
{
gp_Ax22d anAxis(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0), gp_Dir2d(0.0, 1.0));
Handle(Geom2d_Parabola) aPar1 = new Geom2d_Parabola(anAxis, 2.0);
Handle(Geom2d_Parabola) aPar2 = Handle(Geom2d_Parabola)::DownCast(aPar1->Copy());
EXPECT_EQ(myHasher(aPar1), myHasher(aPar2));
EXPECT_TRUE(myHasher(aPar1, aPar2));
}
TEST_F(Geom2dHash_CurveHasherTest, Parabola_DifferentFocal_DifferentHash)
{
gp_Ax22d anAxis(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0), gp_Dir2d(0.0, 1.0));
Handle(Geom2d_Parabola) aPar1 = new Geom2d_Parabola(anAxis, 2.0);
Handle(Geom2d_Parabola) aPar2 = new Geom2d_Parabola(anAxis, 3.0);
EXPECT_NE(myHasher(aPar1), myHasher(aPar2));
EXPECT_FALSE(myHasher(aPar1, aPar2));
}
// ============================================================================
// BezierCurve Tests
// ============================================================================
TEST_F(Geom2dHash_CurveHasherTest, BezierCurve_CopiedCurves_SameHash)
{
TColgp_Array1OfPnt2d aPoles(1, 4);
aPoles(1) = gp_Pnt2d(0.0, 0.0);
aPoles(2) = gp_Pnt2d(1.0, 2.0);
aPoles(3) = gp_Pnt2d(3.0, 2.0);
aPoles(4) = gp_Pnt2d(4.0, 0.0);
Handle(Geom2d_BezierCurve) aCurve1 = new Geom2d_BezierCurve(aPoles);
Handle(Geom2d_BezierCurve) aCurve2 = Handle(Geom2d_BezierCurve)::DownCast(aCurve1->Copy());
EXPECT_EQ(myHasher(aCurve1), myHasher(aCurve2));
EXPECT_TRUE(myHasher(aCurve1, aCurve2));
}
TEST_F(Geom2dHash_CurveHasherTest, BezierCurve_DifferentPoles_DifferentComparison)
{
TColgp_Array1OfPnt2d aPoles1(1, 3);
aPoles1(1) = gp_Pnt2d(0.0, 0.0);
aPoles1(2) = gp_Pnt2d(1.0, 2.0);
aPoles1(3) = gp_Pnt2d(2.0, 0.0);
TColgp_Array1OfPnt2d aPoles2(1, 3);
aPoles2(1) = gp_Pnt2d(0.0, 0.0);
aPoles2(2) = gp_Pnt2d(1.0, 3.0); // Different
aPoles2(3) = gp_Pnt2d(2.0, 0.0);
Handle(Geom2d_BezierCurve) aCurve1 = new Geom2d_BezierCurve(aPoles1);
Handle(Geom2d_BezierCurve) aCurve2 = new Geom2d_BezierCurve(aPoles2);
// Hash may be same (metadata only), but comparison should differ
EXPECT_FALSE(myHasher(aCurve1, aCurve2));
}
// ============================================================================
// BSplineCurve Tests
// ============================================================================
TEST_F(Geom2dHash_CurveHasherTest, BSplineCurve_CopiedCurves_SameHash)
{
TColgp_Array1OfPnt2d aPoles(1, 4);
aPoles(1) = gp_Pnt2d(0.0, 0.0);
aPoles(2) = gp_Pnt2d(1.0, 1.0);
aPoles(3) = gp_Pnt2d(2.0, 1.0);
aPoles(4) = gp_Pnt2d(3.0, 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;
Handle(Geom2d_BSplineCurve) aCurve1 = new Geom2d_BSplineCurve(aPoles, aKnots, aMults, 3);
Handle(Geom2d_BSplineCurve) aCurve2 = Handle(Geom2d_BSplineCurve)::DownCast(aCurve1->Copy());
EXPECT_EQ(myHasher(aCurve1), myHasher(aCurve2));
EXPECT_TRUE(myHasher(aCurve1, aCurve2));
}
// ============================================================================
// TrimmedCurve Tests
// ============================================================================
TEST_F(Geom2dHash_CurveHasherTest, TrimmedCurve_CopiedCurves_SameHash)
{
Handle(Geom2d_Line) aLine = new Geom2d_Line(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0));
Handle(Geom2d_TrimmedCurve) aTrimmed1 = new Geom2d_TrimmedCurve(aLine, 0.0, 10.0);
Handle(Geom2d_TrimmedCurve) aTrimmed2 = Handle(Geom2d_TrimmedCurve)::DownCast(aTrimmed1->Copy());
EXPECT_EQ(myHasher(aTrimmed1), myHasher(aTrimmed2));
EXPECT_TRUE(myHasher(aTrimmed1, aTrimmed2));
}
TEST_F(Geom2dHash_CurveHasherTest, TrimmedCurve_DifferentBounds_DifferentHash)
{
Handle(Geom2d_Line) aLine = new Geom2d_Line(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0));
Handle(Geom2d_TrimmedCurve) aTrimmed1 = new Geom2d_TrimmedCurve(aLine, 0.0, 10.0);
Handle(Geom2d_TrimmedCurve) aTrimmed2 = new Geom2d_TrimmedCurve(aLine, 0.0, 20.0);
EXPECT_NE(myHasher(aTrimmed1), myHasher(aTrimmed2));
EXPECT_FALSE(myHasher(aTrimmed1, aTrimmed2));
}
// ============================================================================
// OffsetCurve Tests
// ============================================================================
TEST_F(Geom2dHash_CurveHasherTest, OffsetCurve_CopiedCurves_SameHash)
{
Handle(Geom2d_Line) aLine = new Geom2d_Line(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0));
Handle(Geom2d_OffsetCurve) anOffset1 = new Geom2d_OffsetCurve(aLine, 5.0);
Handle(Geom2d_OffsetCurve) anOffset2 = Handle(Geom2d_OffsetCurve)::DownCast(anOffset1->Copy());
EXPECT_EQ(myHasher(anOffset1), myHasher(anOffset2));
EXPECT_TRUE(myHasher(anOffset1, anOffset2));
}
TEST_F(Geom2dHash_CurveHasherTest, OffsetCurve_DifferentOffset_DifferentHash)
{
Handle(Geom2d_Line) aLine = new Geom2d_Line(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0));
Handle(Geom2d_OffsetCurve) anOffset1 = new Geom2d_OffsetCurve(aLine, 5.0);
Handle(Geom2d_OffsetCurve) anOffset2 = new Geom2d_OffsetCurve(aLine, 10.0);
EXPECT_NE(myHasher(anOffset1), myHasher(anOffset2));
EXPECT_FALSE(myHasher(anOffset1, anOffset2));
}
// ============================================================================
// Cross-type Tests
// ============================================================================
TEST_F(Geom2dHash_CurveHasherTest, DifferentTypes_DifferentComparison)
{
Handle(Geom2d_Line) aLine = new Geom2d_Line(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0));
gp_Ax22d anAxis(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0), gp_Dir2d(0.0, 1.0));
Handle(Geom2d_Circle) aCircle = new Geom2d_Circle(anAxis, 5.0);
EXPECT_FALSE(myHasher(aLine, aCircle));
}
TEST_F(Geom2dHash_CurveHasherTest, NullCurves_HandledCorrectly)
{
Handle(Geom2d_Curve) aNullCurve;
Handle(Geom2d_Line) aLine = new Geom2d_Line(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0));
EXPECT_EQ(myHasher(aNullCurve), 0u);
EXPECT_TRUE(myHasher(aNullCurve, aNullCurve));
EXPECT_FALSE(myHasher(aLine, aNullCurve));
}
TEST_F(Geom2dHash_CurveHasherTest, SameObject_Equal)
{
Handle(Geom2d_Line) aLine = new Geom2d_Line(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0));
EXPECT_TRUE(myHasher(aLine, aLine));
}
// ============================================================================
// Weighted BSpline Tests
// ============================================================================
TEST_F(Geom2dHash_CurveHasherTest, BSplineCurve_Weighted_CopiedCurves_SameHash)
{
TColgp_Array1OfPnt2d aPoles(1, 4);
aPoles(1) = gp_Pnt2d(0.0, 0.0);
aPoles(2) = gp_Pnt2d(1.0, 1.0);
aPoles(3) = gp_Pnt2d(2.0, 1.0);
aPoles(4) = gp_Pnt2d(3.0, 0.0);
TColStd_Array1OfReal aWeights(1, 4);
aWeights(1) = 1.0;
aWeights(2) = 2.0;
aWeights(3) = 2.0;
aWeights(4) = 1.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;
Handle(Geom2d_BSplineCurve) aCurve1 =
new Geom2d_BSplineCurve(aPoles, aWeights, aKnots, aMults, 3);
Handle(Geom2d_BSplineCurve) aCurve2 = Handle(Geom2d_BSplineCurve)::DownCast(aCurve1->Copy());
EXPECT_EQ(myHasher(aCurve1), myHasher(aCurve2));
EXPECT_TRUE(myHasher(aCurve1, aCurve2));
}
TEST_F(Geom2dHash_CurveHasherTest, BSplineCurve_DifferentWeights_DifferentComparison)
{
TColgp_Array1OfPnt2d aPoles(1, 4);
aPoles(1) = gp_Pnt2d(0.0, 0.0);
aPoles(2) = gp_Pnt2d(1.0, 1.0);
aPoles(3) = gp_Pnt2d(2.0, 1.0);
aPoles(4) = gp_Pnt2d(3.0, 0.0);
TColStd_Array1OfReal aWeights1(1, 4);
aWeights1(1) = 1.0;
aWeights1(2) = 2.0;
aWeights1(3) = 2.0;
aWeights1(4) = 1.0;
TColStd_Array1OfReal aWeights2(1, 4);
aWeights2(1) = 1.0;
aWeights2(2) = 3.0; // Different
aWeights2(3) = 2.0;
aWeights2(4) = 1.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;
Handle(Geom2d_BSplineCurve) aCurve1 =
new Geom2d_BSplineCurve(aPoles, aWeights1, aKnots, aMults, 3);
Handle(Geom2d_BSplineCurve) aCurve2 =
new Geom2d_BSplineCurve(aPoles, aWeights2, aKnots, aMults, 3);
EXPECT_FALSE(myHasher(aCurve1, aCurve2));
}
// ============================================================================
// Weighted Bezier Tests
// ============================================================================
TEST_F(Geom2dHash_CurveHasherTest, BezierCurve_Weighted_CopiedCurves_SameHash)
{
TColgp_Array1OfPnt2d aPoles(1, 3);
aPoles(1) = gp_Pnt2d(0.0, 0.0);
aPoles(2) = gp_Pnt2d(1.0, 2.0);
aPoles(3) = gp_Pnt2d(2.0, 0.0);
TColStd_Array1OfReal aWeights(1, 3);
aWeights(1) = 1.0;
aWeights(2) = 2.0;
aWeights(3) = 1.0;
Handle(Geom2d_BezierCurve) aCurve1 = new Geom2d_BezierCurve(aPoles, aWeights);
Handle(Geom2d_BezierCurve) aCurve2 = Handle(Geom2d_BezierCurve)::DownCast(aCurve1->Copy());
EXPECT_EQ(myHasher(aCurve1), myHasher(aCurve2));
EXPECT_TRUE(myHasher(aCurve1, aCurve2));
}
// ============================================================================
// Axis Orientation Tests
// ============================================================================
TEST_F(Geom2dHash_CurveHasherTest, Circle_DifferentAxisOrientation_DifferentHash)
{
gp_Ax22d anAxis1(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0), gp_Dir2d(0.0, 1.0));
gp_Ax22d anAxis2(gp_Pnt2d(0.0, 0.0), gp_Dir2d(0.0, 1.0), gp_Dir2d(-1.0, 0.0));
Handle(Geom2d_Circle) aCircle1 = new Geom2d_Circle(anAxis1, 5.0);
Handle(Geom2d_Circle) aCircle2 = new Geom2d_Circle(anAxis2, 5.0);
EXPECT_NE(myHasher(aCircle1), myHasher(aCircle2));
EXPECT_FALSE(myHasher(aCircle1, aCircle2));
}
TEST_F(Geom2dHash_CurveHasherTest, Line_DifferentDirection_DifferentHash)
{
Handle(Geom2d_Line) aLine1 = new Geom2d_Line(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0));
Handle(Geom2d_Line) aLine2 = new Geom2d_Line(gp_Pnt2d(0.0, 0.0), gp_Dir2d(0.0, 1.0));
EXPECT_NE(myHasher(aLine1), myHasher(aLine2));
EXPECT_FALSE(myHasher(aLine1, aLine2));
}
// ============================================================================
// BSpline with Different Knots Tests
// ============================================================================
TEST_F(Geom2dHash_CurveHasherTest, BSplineCurve_DifferentKnots_DifferentComparison)
{
TColgp_Array1OfPnt2d aPoles(1, 4);
aPoles(1) = gp_Pnt2d(0.0, 0.0);
aPoles(2) = gp_Pnt2d(1.0, 1.0);
aPoles(3) = gp_Pnt2d(2.0, 1.0);
aPoles(4) = gp_Pnt2d(3.0, 0.0);
TColStd_Array1OfReal aKnots1(1, 2);
aKnots1(1) = 0.0;
aKnots1(2) = 1.0;
TColStd_Array1OfReal aKnots2(1, 2);
aKnots2(1) = 0.0;
aKnots2(2) = 2.0; // Different
TColStd_Array1OfInteger aMults(1, 2);
aMults(1) = 4;
aMults(2) = 4;
Handle(Geom2d_BSplineCurve) aCurve1 = new Geom2d_BSplineCurve(aPoles, aKnots1, aMults, 3);
Handle(Geom2d_BSplineCurve) aCurve2 = new Geom2d_BSplineCurve(aPoles, aKnots2, aMults, 3);
EXPECT_FALSE(myHasher(aCurve1, aCurve2));
}
// ============================================================================
// Periodic BSpline Tests
// ============================================================================
TEST_F(Geom2dHash_CurveHasherTest, BSplineCurve_QuadraticBezierEquivalent_CopiedCurves_SameHash)
{
// Simple quadratic B-spline (Bezier-like, single span)
TColgp_Array1OfPnt2d aPoles(1, 3);
aPoles(1) = gp_Pnt2d(0.0, 0.0);
aPoles(2) = gp_Pnt2d(1.0, 2.0);
aPoles(3) = gp_Pnt2d(2.0, 0.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;
Handle(Geom2d_BSplineCurve) aCurve1 = new Geom2d_BSplineCurve(aPoles, aKnots, aMults, 2);
Handle(Geom2d_BSplineCurve) aCurve2 = Handle(Geom2d_BSplineCurve)::DownCast(aCurve1->Copy());
EXPECT_EQ(myHasher(aCurve1), myHasher(aCurve2));
EXPECT_TRUE(myHasher(aCurve1, aCurve2));
}
// ============================================================================
// Reversed Curve Tests
// ============================================================================
TEST_F(Geom2dHash_CurveHasherTest, Line_Reversed_DifferentComparison)
{
Handle(Geom2d_Line) aLine1 = new Geom2d_Line(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0));
Handle(Geom2d_Line) aLine2 = Handle(Geom2d_Line)::DownCast(aLine1->Reversed());
EXPECT_FALSE(myHasher(aLine1, aLine2));
}
TEST_F(Geom2dHash_CurveHasherTest, BezierCurve_Reversed_DifferentComparison)
{
TColgp_Array1OfPnt2d aPoles(1, 3);
aPoles(1) = gp_Pnt2d(0.0, 0.0);
aPoles(2) = gp_Pnt2d(1.0, 2.0);
aPoles(3) = gp_Pnt2d(2.0, 0.0);
Handle(Geom2d_BezierCurve) aCurve1 = new Geom2d_BezierCurve(aPoles);
Handle(Geom2d_BezierCurve) aCurve2 = Handle(Geom2d_BezierCurve)::DownCast(aCurve1->Reversed());
EXPECT_FALSE(myHasher(aCurve1, aCurve2));
}
// ============================================================================
// Transformed Curve Tests
// ============================================================================
TEST_F(Geom2dHash_CurveHasherTest, Circle_Translated_DifferentHash)
{
gp_Ax22d anAxis(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0), gp_Dir2d(0.0, 1.0));
Handle(Geom2d_Circle) aCircle1 = new Geom2d_Circle(anAxis, 5.0);
Handle(Geom2d_Circle) aCircle2 = Handle(Geom2d_Circle)::DownCast(aCircle1->Copy());
aCircle2->Translate(gp_Vec2d(1.0, 0.0));
EXPECT_NE(myHasher(aCircle1), myHasher(aCircle2));
EXPECT_FALSE(myHasher(aCircle1, aCircle2));
}
TEST_F(Geom2dHash_CurveHasherTest, Circle_Scaled_DifferentHash)
{
gp_Ax22d anAxis(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0), gp_Dir2d(0.0, 1.0));
Handle(Geom2d_Circle) aCircle1 = new Geom2d_Circle(anAxis, 5.0);
Handle(Geom2d_Circle) aCircle2 = Handle(Geom2d_Circle)::DownCast(aCircle1->Copy());
aCircle2->Scale(gp_Pnt2d(0.0, 0.0), 2.0);
EXPECT_NE(myHasher(aCircle1), myHasher(aCircle2));
EXPECT_FALSE(myHasher(aCircle1, aCircle2));
}
// ============================================================================
// Higher Degree BSpline Tests
// ============================================================================
TEST_F(Geom2dHash_CurveHasherTest, BSplineCurve_HigherDegree_CopiedCurves_SameHash)
{
TColgp_Array1OfPnt2d aPoles(1, 6);
aPoles(1) = gp_Pnt2d(0.0, 0.0);
aPoles(2) = gp_Pnt2d(1.0, 1.0);
aPoles(3) = gp_Pnt2d(2.0, 0.5);
aPoles(4) = gp_Pnt2d(3.0, 1.5);
aPoles(5) = gp_Pnt2d(4.0, 0.0);
aPoles(6) = gp_Pnt2d(5.0, 1.0);
TColStd_Array1OfReal aKnots(1, 2);
aKnots(1) = 0.0;
aKnots(2) = 1.0;
TColStd_Array1OfInteger aMults(1, 2);
aMults(1) = 6;
aMults(2) = 6;
Handle(Geom2d_BSplineCurve) aCurve1 = new Geom2d_BSplineCurve(aPoles, aKnots, aMults, 5);
Handle(Geom2d_BSplineCurve) aCurve2 = Handle(Geom2d_BSplineCurve)::DownCast(aCurve1->Copy());
EXPECT_EQ(myHasher(aCurve1), myHasher(aCurve2));
EXPECT_TRUE(myHasher(aCurve1, aCurve2));
}
// ============================================================================
// Multiple Knot Spans Tests
// ============================================================================
TEST_F(Geom2dHash_CurveHasherTest, BSplineCurve_LinearMultipleSpans_CopiedCurves_SameHash)
{
// Linear B-spline with multiple knots (piecewise linear)
TColgp_Array1OfPnt2d aPoles(1, 4);
aPoles(1) = gp_Pnt2d(0.0, 0.0);
aPoles(2) = gp_Pnt2d(1.0, 1.0);
aPoles(3) = gp_Pnt2d(2.0, 0.0);
aPoles(4) = gp_Pnt2d(3.0, 1.0);
TColStd_Array1OfReal aKnots(1, 4);
aKnots(1) = 0.0;
aKnots(2) = 1.0;
aKnots(3) = 2.0;
aKnots(4) = 3.0;
TColStd_Array1OfInteger aMults(1, 4);
aMults(1) = 2;
aMults(2) = 1;
aMults(3) = 1;
aMults(4) = 2;
Handle(Geom2d_BSplineCurve) aCurve1 = new Geom2d_BSplineCurve(aPoles, aKnots, aMults, 1);
Handle(Geom2d_BSplineCurve) aCurve2 = Handle(Geom2d_BSplineCurve)::DownCast(aCurve1->Copy());
EXPECT_EQ(myHasher(aCurve1), myHasher(aCurve2));
EXPECT_TRUE(myHasher(aCurve1, aCurve2));
}
// ============================================================================
// Cross-type Conic Tests
// ============================================================================
TEST_F(Geom2dHash_CurveHasherTest, Ellipse_vs_Hyperbola_DifferentComparison)
{
gp_Ax22d anAxis(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0), gp_Dir2d(0.0, 1.0));
Handle(Geom2d_Ellipse) anEllipse = new Geom2d_Ellipse(anAxis, 5.0, 3.0);
Handle(Geom2d_Hyperbola) aHyperbola = new Geom2d_Hyperbola(anAxis, 5.0, 3.0);
EXPECT_FALSE(myHasher(anEllipse, aHyperbola));
}
TEST_F(Geom2dHash_CurveHasherTest, Circle_vs_Ellipse_DifferentComparison)
{
gp_Ax22d anAxis(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0), gp_Dir2d(0.0, 1.0));
Handle(Geom2d_Circle) aCircle = new Geom2d_Circle(anAxis, 5.0);
Handle(Geom2d_Ellipse) anEllipse = new Geom2d_Ellipse(anAxis, 5.0, 5.0);
EXPECT_FALSE(myHasher(aCircle, anEllipse));
}
// ============================================================================
// Trimmed vs Base Curve Tests
// ============================================================================
TEST_F(Geom2dHash_CurveHasherTest, TrimmedCurve_vs_BaseCurve_DifferentComparison)
{
Handle(Geom2d_Line) aLine = new Geom2d_Line(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0));
Handle(Geom2d_TrimmedCurve) aTrimmed = new Geom2d_TrimmedCurve(aLine, 0.0, 10.0);
EXPECT_FALSE(myHasher(aLine, aTrimmed));
}
// ============================================================================
// Edge Cases - Degenerate/Special Values
// ============================================================================
TEST_F(Geom2dHash_CurveHasherTest, Circle_VerySmallRadius_CopiedCircles_SameHash)
{
gp_Ax22d anAxis(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0), gp_Dir2d(0.0, 1.0));
Handle(Geom2d_Circle) aCircle1 = new Geom2d_Circle(anAxis, 1e-10);
Handle(Geom2d_Circle) aCircle2 = Handle(Geom2d_Circle)::DownCast(aCircle1->Copy());
EXPECT_EQ(myHasher(aCircle1), myHasher(aCircle2));
EXPECT_TRUE(myHasher(aCircle1, aCircle2));
}
TEST_F(Geom2dHash_CurveHasherTest, Circle_VeryLargeRadius_CopiedCircles_SameHash)
{
gp_Ax22d anAxis(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0), gp_Dir2d(0.0, 1.0));
Handle(Geom2d_Circle) aCircle1 = new Geom2d_Circle(anAxis, 1e10);
Handle(Geom2d_Circle) aCircle2 = Handle(Geom2d_Circle)::DownCast(aCircle1->Copy());
EXPECT_EQ(myHasher(aCircle1), myHasher(aCircle2));
EXPECT_TRUE(myHasher(aCircle1, aCircle2));
}
TEST_F(Geom2dHash_CurveHasherTest, Line_AtOrigin_vs_FarFromOrigin_DifferentHash)
{
Handle(Geom2d_Line) aLine1 = new Geom2d_Line(gp_Pnt2d(0.0, 0.0), gp_Dir2d(1.0, 0.0));
Handle(Geom2d_Line) aLine2 = new Geom2d_Line(gp_Pnt2d(1e10, 1e10), gp_Dir2d(1.0, 0.0));
EXPECT_NE(myHasher(aLine1), myHasher(aLine2));
EXPECT_FALSE(myHasher(aLine1, aLine2));
}

View File

@@ -0,0 +1,22 @@
# Source files for Geom2dHash package
set(OCCT_Geom2dHash_FILES_LOCATION "${CMAKE_CURRENT_LIST_DIR}")
set(OCCT_Geom2dHash_FILES
# Foundational Hashers
Geom2dHash_PointHasher.pxx
Geom2dHash_DirectionHasher.pxx
Geom2dHash_AxisPlacement.pxx
# Curve Hashers
Geom2dHash_LineHasher.pxx
Geom2dHash_CircleHasher.pxx
Geom2dHash_EllipseHasher.pxx
Geom2dHash_HyperbolaHasher.pxx
Geom2dHash_ParabolaHasher.pxx
Geom2dHash_BezierCurveHasher.pxx
Geom2dHash_BSplineCurveHasher.pxx
Geom2dHash_TrimmedCurveHasher.pxx
Geom2dHash_OffsetCurveHasher.pxx
Geom2dHash_CurveHasher.hxx
Geom2dHash_CurveHasher.cxx
)

View File

@@ -0,0 +1,51 @@
// 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 _Geom2dHash_AxisPlacement_HeaderFile
#define _Geom2dHash_AxisPlacement_HeaderFile
#include <Standard_HashUtils.hxx>
#include <gp_Ax22d.hxx>
#include <Geom2dHash_PointHasher.pxx>
#include <Geom2dHash_DirectionHasher.pxx>
//! OCCT-style hasher for gp_Ax22d (2D coordinate system).
//! Used for geometry deduplication.
struct Geom2dHash_AxisPlacement
{
// Hashes a 2D axis placement by its location, X direction, and Y direction.
std::size_t operator()(const gp_Ax22d& theAxisPlacement) const noexcept
{
const Geom2dHash_PointHasher aPointHasher;
const Geom2dHash_DirectionHasher aDirHasher;
const std::size_t aHashes[3] = {aPointHasher(theAxisPlacement.Location()),
aDirHasher(theAxisPlacement.XDirection()),
aDirHasher(theAxisPlacement.YDirection())};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two 2D axis placements.
bool operator()(const gp_Ax22d& theAxisPlacement1,
const gp_Ax22d& theAxisPlacement2) const noexcept
{
const Geom2dHash_PointHasher aPointHasher;
const Geom2dHash_DirectionHasher aDirHasher;
return aPointHasher(theAxisPlacement1.Location(), theAxisPlacement2.Location())
&& aDirHasher(theAxisPlacement1.XDirection(), theAxisPlacement2.XDirection())
&& aDirHasher(theAxisPlacement1.YDirection(), theAxisPlacement2.YDirection());
}
};
#endif // _Geom2dHash_AxisPlacement_HeaderFile

View File

@@ -0,0 +1,98 @@
// 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 _Geom2dHash_BSplineCurveHasher_HeaderFile
#define _Geom2dHash_BSplineCurveHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <Geom2d_BSplineCurve.hxx>
#include <Geom2dHash_PointHasher.pxx>
#include <cmath>
//! OCCT-style hasher for Geom2d_BSplineCurve (2D B-spline curve).
//! Used for geometry deduplication.
//! Hashes only metadata (degree, pole count, knot count, rationality) for efficiency.
struct Geom2dHash_BSplineCurveHasher
{
// Hashes the B-spline curve metadata only.
std::size_t operator()(const Handle(Geom2d_BSplineCurve)& theCurve) const noexcept
{
const std::size_t aHashes[4] = {opencascade::hash(theCurve->Degree()),
opencascade::hash(theCurve->NbPoles()),
opencascade::hash(theCurve->NbKnots()),
opencascade::hash(static_cast<int>(theCurve->IsRational()))};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two B-spline curves by full geometric data.
bool operator()(const Handle(Geom2d_BSplineCurve)& theCurve1,
const Handle(Geom2d_BSplineCurve)& theCurve2) const noexcept
{
constexpr double aTolerance = 1e-12;
// Compare degrees
if (theCurve1->Degree() != theCurve2->Degree())
{
return false;
}
// Compare knot counts
if (theCurve1->NbKnots() != theCurve2->NbKnots())
{
return false;
}
// Compare knots and multiplicities
for (int i = 1; i <= theCurve1->NbKnots(); ++i)
{
if (std::abs(theCurve1->Knot(i) - theCurve2->Knot(i)) > aTolerance
|| theCurve1->Multiplicity(i) != theCurve2->Multiplicity(i))
{
return false;
}
}
// Compare rationality
if (theCurve1->IsRational() != theCurve2->IsRational())
{
return false;
}
const Geom2dHash_PointHasher aPointHasher;
// Compare poles
for (int i = 1; i <= theCurve1->NbPoles(); ++i)
{
if (!aPointHasher(theCurve1->Pole(i), theCurve2->Pole(i)))
{
return false;
}
}
// Compare weights if rational
if (theCurve1->IsRational())
{
for (int i = 1; i <= theCurve1->NbPoles(); ++i)
{
if (std::abs(theCurve1->Weight(i) - theCurve2->Weight(i)) > aTolerance)
{
return false;
}
}
}
return true;
}
};
#endif // _Geom2dHash_BSplineCurveHasher_HeaderFile

View File

@@ -0,0 +1,81 @@
// 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 _Geom2dHash_BezierCurveHasher_HeaderFile
#define _Geom2dHash_BezierCurveHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <Geom2d_BezierCurve.hxx>
#include <Geom2dHash_PointHasher.pxx>
#include <cmath>
//! OCCT-style hasher for Geom2d_BezierCurve (2D Bezier curve).
//! Used for geometry deduplication.
//! Hashes only metadata (degree, pole count, rationality) for efficiency.
struct Geom2dHash_BezierCurveHasher
{
// Hashes the Bezier curve metadata only.
std::size_t operator()(const Handle(Geom2d_BezierCurve)& theCurve) const noexcept
{
const std::size_t aHashes[3] = {opencascade::hash(theCurve->Degree()),
opencascade::hash(theCurve->NbPoles()),
opencascade::hash(static_cast<int>(theCurve->IsRational()))};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two Bezier curves by full geometric data.
bool operator()(const Handle(Geom2d_BezierCurve)& theCurve1,
const Handle(Geom2d_BezierCurve)& theCurve2) const noexcept
{
constexpr double aTolerance = 1e-12;
// Compare degrees
if (theCurve1->Degree() != theCurve2->Degree())
{
return false;
}
// Compare rationality
if (theCurve1->IsRational() != theCurve2->IsRational())
{
return false;
}
const Geom2dHash_PointHasher aPointHasher;
// Compare poles
for (int i = 1; i <= theCurve1->NbPoles(); ++i)
{
if (!aPointHasher(theCurve1->Pole(i), theCurve2->Pole(i)))
{
return false;
}
}
// Compare weights if rational
if (theCurve1->IsRational())
{
for (int i = 1; i <= theCurve1->NbPoles(); ++i)
{
if (std::abs(theCurve1->Weight(i) - theCurve2->Weight(i)) > aTolerance)
{
return false;
}
}
}
return true;
}
};
#endif // _Geom2dHash_BezierCurveHasher_HeaderFile

View File

@@ -0,0 +1,51 @@
// 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 _Geom2dHash_CircleHasher_HeaderFile
#define _Geom2dHash_CircleHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <Geom2d_Circle.hxx>
#include <Geom2dHash_AxisPlacement.pxx>
#include <cmath>
//! OCCT-style hasher for Geom2d_Circle (2D circle).
//! Used for geometry deduplication.
struct Geom2dHash_CircleHasher
{
// Hashes the circle by its position and radius.
std::size_t operator()(const Handle(Geom2d_Circle)& theCircle) const noexcept
{
constexpr double aTolerance = 1e-12;
constexpr double aFactor = 1.0 / aTolerance;
const Geom2dHash_AxisPlacement anAxisHasher;
const std::size_t aHashes[2] = {
anAxisHasher(theCircle->Position()),
opencascade::hash(static_cast<int64_t>(std::round(theCircle->Radius() * aFactor)))};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two circles by their positions and radii.
bool operator()(const Handle(Geom2d_Circle)& theCircle1,
const Handle(Geom2d_Circle)& theCircle2) const noexcept
{
constexpr double aTolerance = 1e-12;
const Geom2dHash_AxisPlacement anAxisHasher;
return anAxisHasher(theCircle1->Position(), theCircle2->Position())
&& std::abs(theCircle1->Radius() - theCircle2->Radius()) <= aTolerance;
}
};
#endif // _Geom2dHash_CircleHasher_HeaderFile

View File

@@ -0,0 +1,152 @@
// 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 <Geom2dHash_CurveHasher.hxx>
#include <Standard_HashUtils.hxx>
#include <Geom2d_Curve.hxx>
#include <Geom2d_Line.hxx>
#include <Geom2d_Circle.hxx>
#include <Geom2d_Ellipse.hxx>
#include <Geom2d_Hyperbola.hxx>
#include <Geom2d_Parabola.hxx>
#include <Geom2d_BezierCurve.hxx>
#include <Geom2d_BSplineCurve.hxx>
#include <Geom2d_TrimmedCurve.hxx>
#include <Geom2d_OffsetCurve.hxx>
#include <Geom2dHash_LineHasher.pxx>
#include <Geom2dHash_CircleHasher.pxx>
#include <Geom2dHash_EllipseHasher.pxx>
#include <Geom2dHash_HyperbolaHasher.pxx>
#include <Geom2dHash_ParabolaHasher.pxx>
#include <Geom2dHash_BezierCurveHasher.pxx>
#include <Geom2dHash_BSplineCurveHasher.pxx>
#include <Geom2dHash_TrimmedCurveHasher.pxx>
#include <Geom2dHash_OffsetCurveHasher.pxx>
//=================================================================================================
std::size_t Geom2dHash_CurveHasher::operator()(const Handle(Geom2d_Curve)& theCurve) const noexcept
{
if (theCurve.IsNull())
{
return 0;
}
// Dispatch based on actual curve type
if (Handle(Geom2d_Line) aLine = Handle(Geom2d_Line)::DownCast(theCurve))
{
return Geom2dHash_LineHasher{}(aLine);
}
if (Handle(Geom2d_Circle) aCircle = Handle(Geom2d_Circle)::DownCast(theCurve))
{
return Geom2dHash_CircleHasher{}(aCircle);
}
if (Handle(Geom2d_Ellipse) anEllipse = Handle(Geom2d_Ellipse)::DownCast(theCurve))
{
return Geom2dHash_EllipseHasher{}(anEllipse);
}
if (Handle(Geom2d_Hyperbola) aHyperbola = Handle(Geom2d_Hyperbola)::DownCast(theCurve))
{
return Geom2dHash_HyperbolaHasher{}(aHyperbola);
}
if (Handle(Geom2d_Parabola) aParabola = Handle(Geom2d_Parabola)::DownCast(theCurve))
{
return Geom2dHash_ParabolaHasher{}(aParabola);
}
if (Handle(Geom2d_BezierCurve) aBezier = Handle(Geom2d_BezierCurve)::DownCast(theCurve))
{
return Geom2dHash_BezierCurveHasher{}(aBezier);
}
if (Handle(Geom2d_BSplineCurve) aBSpline = Handle(Geom2d_BSplineCurve)::DownCast(theCurve))
{
return Geom2dHash_BSplineCurveHasher{}(aBSpline);
}
if (Handle(Geom2d_TrimmedCurve) aTrimmed = Handle(Geom2d_TrimmedCurve)::DownCast(theCurve))
{
return Geom2dHash_TrimmedCurveHasher{}(aTrimmed);
}
if (Handle(Geom2d_OffsetCurve) anOffset = Handle(Geom2d_OffsetCurve)::DownCast(theCurve))
{
return Geom2dHash_OffsetCurveHasher{}(anOffset);
}
// Unknown curve type - hash the type name
return std::hash<std::string>{}(theCurve->DynamicType()->Name());
}
//=================================================================================================
bool Geom2dHash_CurveHasher::operator()(const Handle(Geom2d_Curve)& theCurve1,
const Handle(Geom2d_Curve)& theCurve2) const noexcept
{
if (theCurve1.IsNull() || theCurve2.IsNull())
{
return theCurve1.IsNull() && theCurve2.IsNull();
}
if (theCurve1 == theCurve2)
{
return true;
}
// Must be same type
if (theCurve1->DynamicType() != theCurve2->DynamicType())
{
return false;
}
// Dispatch based on actual curve type
if (Handle(Geom2d_Line) aLine1 = Handle(Geom2d_Line)::DownCast(theCurve1))
{
return Geom2dHash_LineHasher{}(aLine1, Handle(Geom2d_Line)::DownCast(theCurve2));
}
if (Handle(Geom2d_Circle) aCircle1 = Handle(Geom2d_Circle)::DownCast(theCurve1))
{
return Geom2dHash_CircleHasher{}(aCircle1, Handle(Geom2d_Circle)::DownCast(theCurve2));
}
if (Handle(Geom2d_Ellipse) anEllipse1 = Handle(Geom2d_Ellipse)::DownCast(theCurve1))
{
return Geom2dHash_EllipseHasher{}(anEllipse1, Handle(Geom2d_Ellipse)::DownCast(theCurve2));
}
if (Handle(Geom2d_Hyperbola) aHyp1 = Handle(Geom2d_Hyperbola)::DownCast(theCurve1))
{
return Geom2dHash_HyperbolaHasher{}(aHyp1, Handle(Geom2d_Hyperbola)::DownCast(theCurve2));
}
if (Handle(Geom2d_Parabola) aPar1 = Handle(Geom2d_Parabola)::DownCast(theCurve1))
{
return Geom2dHash_ParabolaHasher{}(aPar1, Handle(Geom2d_Parabola)::DownCast(theCurve2));
}
if (Handle(Geom2d_BezierCurve) aBez1 = Handle(Geom2d_BezierCurve)::DownCast(theCurve1))
{
return Geom2dHash_BezierCurveHasher{}(aBez1, Handle(Geom2d_BezierCurve)::DownCast(theCurve2));
}
if (Handle(Geom2d_BSplineCurve) aBSpl1 = Handle(Geom2d_BSplineCurve)::DownCast(theCurve1))
{
return Geom2dHash_BSplineCurveHasher{}(aBSpl1,
Handle(Geom2d_BSplineCurve)::DownCast(theCurve2));
}
if (Handle(Geom2d_TrimmedCurve) aTrim1 = Handle(Geom2d_TrimmedCurve)::DownCast(theCurve1))
{
return Geom2dHash_TrimmedCurveHasher{}(aTrim1,
Handle(Geom2d_TrimmedCurve)::DownCast(theCurve2));
}
if (Handle(Geom2d_OffsetCurve) aOff1 = Handle(Geom2d_OffsetCurve)::DownCast(theCurve1))
{
return Geom2dHash_OffsetCurveHasher{}(aOff1, Handle(Geom2d_OffsetCurve)::DownCast(theCurve2));
}
// Unknown curve type - compare by pointer
return theCurve1.get() == theCurve2.get();
}

View File

@@ -0,0 +1,34 @@
// 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 _Geom2dHash_CurveHasher_HeaderFile
#define _Geom2dHash_CurveHasher_HeaderFile
#include <Standard_Handle.hxx>
#include <cstddef>
class Geom2d_Curve;
//! Polymorphic hasher for Geom2d_Curve using RTTI dispatch.
//! Used for geometry deduplication.
struct Geom2dHash_CurveHasher
{
// Hashes any Geom2d_Curve by dispatching to the appropriate specific hasher.
Standard_EXPORT std::size_t operator()(const Handle(Geom2d_Curve)& theCurve) const noexcept;
// Compares two curves using polymorphic dispatch.
Standard_EXPORT bool operator()(const Handle(Geom2d_Curve)& theCurve1,
const Handle(Geom2d_Curve)& theCurve2) const noexcept;
};
#endif // _Geom2dHash_CurveHasher_HeaderFile

View File

@@ -0,0 +1,47 @@
// 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 _Geom2dHash_DirectionHasher_HeaderFile
#define _Geom2dHash_DirectionHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <gp_Dir2d.hxx>
#include <cmath>
//! OCCT-style hasher for gp_Dir2d (2D directions).
//! Used for geometry deduplication.
struct Geom2dHash_DirectionHasher
{
// Hashes the 2D direction by its XY components.
std::size_t operator()(const gp_Dir2d& theDirection) const noexcept
{
constexpr double aTolerance = 1e-12;
constexpr double aFactor = 1.0 / aTolerance;
// Round each component to tolerance precision before hashing
const std::size_t aHashes[2] = {
opencascade::hash(static_cast<int64_t>(std::round(theDirection.X() * aFactor))),
opencascade::hash(static_cast<int64_t>(std::round(theDirection.Y() * aFactor)))};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two 2D directions with fixed tolerance.
bool operator()(const gp_Dir2d& theDirection1, const gp_Dir2d& theDirection2) const noexcept
{
constexpr double aTolerance = 1e-12;
return std::abs(theDirection1.X() - theDirection2.X()) <= aTolerance
&& std::abs(theDirection1.Y() - theDirection2.Y()) <= aTolerance;
}
};
#endif // _Geom2dHash_DirectionHasher_HeaderFile

View File

@@ -0,0 +1,53 @@
// 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 _Geom2dHash_EllipseHasher_HeaderFile
#define _Geom2dHash_EllipseHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <Geom2d_Ellipse.hxx>
#include <Geom2dHash_AxisPlacement.pxx>
#include <cmath>
//! OCCT-style hasher for Geom2d_Ellipse (2D ellipse).
//! Used for geometry deduplication.
struct Geom2dHash_EllipseHasher
{
// Hashes the ellipse by its position, major radius, and minor radius.
std::size_t operator()(const Handle(Geom2d_Ellipse)& theEllipse) const noexcept
{
constexpr double aTolerance = 1e-12;
constexpr double aFactor = 1.0 / aTolerance;
const Geom2dHash_AxisPlacement anAxisHasher;
const std::size_t aHashes[3] = {
anAxisHasher(theEllipse->Position()),
opencascade::hash(static_cast<int64_t>(std::round(theEllipse->MajorRadius() * aFactor))),
opencascade::hash(static_cast<int64_t>(std::round(theEllipse->MinorRadius() * aFactor)))};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two ellipses by their positions and radii.
bool operator()(const Handle(Geom2d_Ellipse)& theEllipse1,
const Handle(Geom2d_Ellipse)& theEllipse2) const noexcept
{
constexpr double aTolerance = 1e-12;
const Geom2dHash_AxisPlacement anAxisHasher;
return anAxisHasher(theEllipse1->Position(), theEllipse2->Position())
&& std::abs(theEllipse1->MajorRadius() - theEllipse2->MajorRadius()) <= aTolerance
&& std::abs(theEllipse1->MinorRadius() - theEllipse2->MinorRadius()) <= aTolerance;
}
};
#endif // _Geom2dHash_EllipseHasher_HeaderFile

View File

@@ -0,0 +1,53 @@
// 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 _Geom2dHash_HyperbolaHasher_HeaderFile
#define _Geom2dHash_HyperbolaHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <Geom2d_Hyperbola.hxx>
#include <Geom2dHash_AxisPlacement.pxx>
#include <cmath>
//! OCCT-style hasher for Geom2d_Hyperbola (2D hyperbola).
//! Used for geometry deduplication.
struct Geom2dHash_HyperbolaHasher
{
// Hashes the hyperbola by its position, major radius, and minor radius.
std::size_t operator()(const Handle(Geom2d_Hyperbola)& theHyperbola) const noexcept
{
constexpr double aTolerance = 1e-12;
constexpr double aFactor = 1.0 / aTolerance;
const Geom2dHash_AxisPlacement anAxisHasher;
const std::size_t aHashes[3] = {
anAxisHasher(theHyperbola->Position()),
opencascade::hash(static_cast<int64_t>(std::round(theHyperbola->MajorRadius() * aFactor))),
opencascade::hash(static_cast<int64_t>(std::round(theHyperbola->MinorRadius() * aFactor)))};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two hyperbolas by their positions and radii.
bool operator()(const Handle(Geom2d_Hyperbola)& theHyperbola1,
const Handle(Geom2d_Hyperbola)& theHyperbola2) const noexcept
{
constexpr double aTolerance = 1e-12;
const Geom2dHash_AxisPlacement anAxisHasher;
return anAxisHasher(theHyperbola1->Position(), theHyperbola2->Position())
&& std::abs(theHyperbola1->MajorRadius() - theHyperbola2->MajorRadius()) <= aTolerance
&& std::abs(theHyperbola1->MinorRadius() - theHyperbola2->MinorRadius()) <= aTolerance;
}
};
#endif // _Geom2dHash_HyperbolaHasher_HeaderFile

View File

@@ -0,0 +1,49 @@
// 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 _Geom2dHash_LineHasher_HeaderFile
#define _Geom2dHash_LineHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <Geom2d_Line.hxx>
#include <Geom2dHash_PointHasher.pxx>
#include <Geom2dHash_DirectionHasher.pxx>
//! OCCT-style hasher for Geom2d_Line (2D line).
//! Used for geometry deduplication.
struct Geom2dHash_LineHasher
{
// Hashes the line by its location and direction.
std::size_t operator()(const Handle(Geom2d_Line)& theLine) const noexcept
{
const Geom2dHash_PointHasher aPointHasher;
const Geom2dHash_DirectionHasher aDirHasher;
const std::size_t aHashes[2] = {aPointHasher(theLine->Position().Location()),
aDirHasher(theLine->Position().Direction())};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two lines by their positions.
bool operator()(const Handle(Geom2d_Line)& theLine1,
const Handle(Geom2d_Line)& theLine2) const noexcept
{
const Geom2dHash_PointHasher aPointHasher;
const Geom2dHash_DirectionHasher aDirHasher;
return aPointHasher(theLine1->Position().Location(), theLine2->Position().Location())
&& aDirHasher(theLine1->Position().Direction(), theLine2->Position().Direction());
}
};
#endif // _Geom2dHash_LineHasher_HeaderFile

View File

@@ -0,0 +1,52 @@
// 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 _Geom2dHash_OffsetCurveHasher_HeaderFile
#define _Geom2dHash_OffsetCurveHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <Geom2d_OffsetCurve.hxx>
#include <Geom2dHash_CurveHasher.hxx>
#include <cmath>
//! OCCT-style hasher for Geom2d_OffsetCurve (2D offset curve).
//! Used for geometry deduplication.
struct Geom2dHash_OffsetCurveHasher
{
// Hashes the offset curve by its offset distance and basis curve.
std::size_t operator()(const Handle(Geom2d_OffsetCurve)& theCurve) const noexcept
{
constexpr double aTolerance = 1e-12;
constexpr double aFactor = 1.0 / aTolerance;
const Geom2dHash_CurveHasher aCurveHasher;
const std::size_t aHashes[2] = {
aCurveHasher(theCurve->BasisCurve()),
opencascade::hash(static_cast<int64_t>(std::round(theCurve->Offset() * aFactor)))};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two offset curves.
bool operator()(const Handle(Geom2d_OffsetCurve)& theCurve1,
const Handle(Geom2d_OffsetCurve)& theCurve2) const noexcept
{
constexpr double aTolerance = 1e-12;
const Geom2dHash_CurveHasher aCurveHasher;
return aCurveHasher(theCurve1->BasisCurve(), theCurve2->BasisCurve())
&& std::abs(theCurve1->Offset() - theCurve2->Offset()) <= aTolerance;
}
};
#endif // _Geom2dHash_OffsetCurveHasher_HeaderFile

View File

@@ -0,0 +1,51 @@
// 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 _Geom2dHash_ParabolaHasher_HeaderFile
#define _Geom2dHash_ParabolaHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <Geom2d_Parabola.hxx>
#include <Geom2dHash_AxisPlacement.pxx>
#include <cmath>
//! OCCT-style hasher for Geom2d_Parabola (2D parabola).
//! Used for geometry deduplication.
struct Geom2dHash_ParabolaHasher
{
// Hashes the parabola by its position and focal length.
std::size_t operator()(const Handle(Geom2d_Parabola)& theParabola) const noexcept
{
constexpr double aTolerance = 1e-12;
constexpr double aFactor = 1.0 / aTolerance;
const Geom2dHash_AxisPlacement anAxisHasher;
const std::size_t aHashes[2] = {
anAxisHasher(theParabola->Position()),
opencascade::hash(static_cast<int64_t>(std::round(theParabola->Focal() * aFactor)))};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two parabolas by their positions and focal lengths.
bool operator()(const Handle(Geom2d_Parabola)& theParabola1,
const Handle(Geom2d_Parabola)& theParabola2) const noexcept
{
constexpr double aTolerance = 1e-12;
const Geom2dHash_AxisPlacement anAxisHasher;
return anAxisHasher(theParabola1->Position(), theParabola2->Position())
&& std::abs(theParabola1->Focal() - theParabola2->Focal()) <= aTolerance;
}
};
#endif // _Geom2dHash_ParabolaHasher_HeaderFile

View File

@@ -0,0 +1,47 @@
// 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 _Geom2dHash_PointHasher_HeaderFile
#define _Geom2dHash_PointHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <gp_Pnt2d.hxx>
#include <cmath>
//! OCCT-style hasher for gp_Pnt2d (2D points).
//! Used for geometry deduplication.
struct Geom2dHash_PointHasher
{
// Hashes the 2D point by its XY coordinates.
std::size_t operator()(const gp_Pnt2d& thePoint) const noexcept
{
constexpr double aTolerance = 1e-12;
constexpr double aFactor = 1.0 / aTolerance;
// Round each coordinate to tolerance precision before hashing
const std::size_t aHashes[2] = {
opencascade::hash(static_cast<int64_t>(std::round(thePoint.X() * aFactor))),
opencascade::hash(static_cast<int64_t>(std::round(thePoint.Y() * aFactor)))};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two 2D points with fixed tolerance.
bool operator()(const gp_Pnt2d& thePoint1, const gp_Pnt2d& thePoint2) const noexcept
{
constexpr double aTolerance = 1e-12;
return std::abs(thePoint1.X() - thePoint2.X()) <= aTolerance
&& std::abs(thePoint1.Y() - thePoint2.Y()) <= aTolerance;
}
};
#endif // _Geom2dHash_PointHasher_HeaderFile

View File

@@ -0,0 +1,54 @@
// 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 _Geom2dHash_TrimmedCurveHasher_HeaderFile
#define _Geom2dHash_TrimmedCurveHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <Geom2d_TrimmedCurve.hxx>
#include <Geom2dHash_CurveHasher.hxx>
#include <cmath>
//! OCCT-style hasher for Geom2d_TrimmedCurve (2D trimmed curve).
//! Used for geometry deduplication.
struct Geom2dHash_TrimmedCurveHasher
{
// Hashes the trimmed curve by its parameters and basis curve.
std::size_t operator()(const Handle(Geom2d_TrimmedCurve)& theCurve) const noexcept
{
constexpr double aTolerance = 1e-12;
constexpr double aFactor = 1.0 / aTolerance;
const Geom2dHash_CurveHasher aCurveHasher;
const std::size_t aHashes[3] = {
aCurveHasher(theCurve->BasisCurve()),
opencascade::hash(static_cast<int64_t>(std::round(theCurve->FirstParameter() * aFactor))),
opencascade::hash(static_cast<int64_t>(std::round(theCurve->LastParameter() * aFactor)))};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two trimmed curves.
bool operator()(const Handle(Geom2d_TrimmedCurve)& theCurve1,
const Handle(Geom2d_TrimmedCurve)& theCurve2) const noexcept
{
constexpr double aTolerance = 1e-12;
const Geom2dHash_CurveHasher aCurveHasher;
return aCurveHasher(theCurve1->BasisCurve(), theCurve2->BasisCurve())
&& std::abs(theCurve1->FirstParameter() - theCurve2->FirstParameter()) <= aTolerance
&& std::abs(theCurve1->LastParameter() - theCurve2->LastParameter()) <= aTolerance;
}
};
#endif // _Geom2dHash_TrimmedCurveHasher_HeaderFile

View File

@@ -7,4 +7,5 @@ set(OCCT_TKG2d_LIST_OF_PACKAGES
Geom2dLProp
Geom2dAdaptor
Geom2dEvaluator
Geom2dHash
)

View File

@@ -10,4 +10,6 @@ set(OCCT_TKG3d_GTests_FILES
Geom_OffsetSurface_Test.cxx
GeomAPI_ExtremaCurveCurve_Test.cxx
GeomAPI_Interpolate_Test.cxx
GeomHash_CurveHasher_Test.cxx
GeomHash_SurfaceHasher_Test.cxx
)

View File

@@ -0,0 +1,690 @@
// 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 <gtest/gtest.h>
#include <GeomHash_CurveHasher.hxx>
#include <Geom_Line.hxx>
#include <Geom_Circle.hxx>
#include <Geom_Ellipse.hxx>
#include <Geom_Hyperbola.hxx>
#include <Geom_Parabola.hxx>
#include <Geom_BezierCurve.hxx>
#include <Geom_BSplineCurve.hxx>
#include <Geom_TrimmedCurve.hxx>
#include <Geom_OffsetCurve.hxx>
#include <gp_Ax1.hxx>
#include <gp_Ax2.hxx>
#include <gp_Pnt.hxx>
#include <gp_Dir.hxx>
#include <gp_Vec.hxx>
#include <TColgp_Array1OfPnt.hxx>
#include <TColStd_Array1OfReal.hxx>
#include <TColStd_Array1OfInteger.hxx>
class GeomHash_CurveHasherTest : public ::testing::Test
{
protected:
GeomHash_CurveHasher myHasher;
};
// ============================================================================
// Line Tests
// ============================================================================
TEST_F(GeomHash_CurveHasherTest, Line_CopiedLines_SameHash)
{
gp_Pnt aLoc(1.0, 2.0, 3.0);
gp_Dir aDir(1.0, 0.0, 0.0);
Handle(Geom_Line) aLine1 = new Geom_Line(aLoc, aDir);
Handle(Geom_Line) aLine2 = Handle(Geom_Line)::DownCast(aLine1->Copy());
EXPECT_EQ(myHasher(aLine1), myHasher(aLine2));
EXPECT_TRUE(myHasher(aLine1, aLine2));
}
TEST_F(GeomHash_CurveHasherTest, Line_DifferentLines_DifferentHash)
{
Handle(Geom_Line) aLine1 = new Geom_Line(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(1.0, 0.0, 0.0));
Handle(Geom_Line) aLine2 = new Geom_Line(gp_Pnt(1.0, 0.0, 0.0), gp_Dir(1.0, 0.0, 0.0));
EXPECT_NE(myHasher(aLine1), myHasher(aLine2));
EXPECT_FALSE(myHasher(aLine1, aLine2));
}
// ============================================================================
// Circle Tests
// ============================================================================
TEST_F(GeomHash_CurveHasherTest, Circle_CopiedCircles_SameHash)
{
gp_Ax2 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_Circle) aCircle1 = new Geom_Circle(anAxis, 5.0);
Handle(Geom_Circle) aCircle2 = Handle(Geom_Circle)::DownCast(aCircle1->Copy());
EXPECT_EQ(myHasher(aCircle1), myHasher(aCircle2));
EXPECT_TRUE(myHasher(aCircle1, aCircle2));
}
TEST_F(GeomHash_CurveHasherTest, Circle_DifferentRadius_DifferentHash)
{
gp_Ax2 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_Circle) aCircle1 = new Geom_Circle(anAxis, 5.0);
Handle(Geom_Circle) aCircle2 = new Geom_Circle(anAxis, 10.0);
EXPECT_NE(myHasher(aCircle1), myHasher(aCircle2));
EXPECT_FALSE(myHasher(aCircle1, aCircle2));
}
// ============================================================================
// Ellipse Tests
// ============================================================================
TEST_F(GeomHash_CurveHasherTest, Ellipse_CopiedEllipses_SameHash)
{
gp_Ax2 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_Ellipse) anEllipse1 = new Geom_Ellipse(anAxis, 10.0, 5.0);
Handle(Geom_Ellipse) anEllipse2 = Handle(Geom_Ellipse)::DownCast(anEllipse1->Copy());
EXPECT_EQ(myHasher(anEllipse1), myHasher(anEllipse2));
EXPECT_TRUE(myHasher(anEllipse1, anEllipse2));
}
TEST_F(GeomHash_CurveHasherTest, Ellipse_DifferentRadii_DifferentHash)
{
gp_Ax2 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_Ellipse) anEllipse1 = new Geom_Ellipse(anAxis, 10.0, 5.0);
Handle(Geom_Ellipse) anEllipse2 = new Geom_Ellipse(anAxis, 10.0, 7.0);
EXPECT_NE(myHasher(anEllipse1), myHasher(anEllipse2));
EXPECT_FALSE(myHasher(anEllipse1, anEllipse2));
}
// ============================================================================
// Hyperbola Tests
// ============================================================================
TEST_F(GeomHash_CurveHasherTest, Hyperbola_CopiedHyperbolas_SameHash)
{
gp_Ax2 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_Hyperbola) aHyp1 = new Geom_Hyperbola(anAxis, 5.0, 3.0);
Handle(Geom_Hyperbola) aHyp2 = Handle(Geom_Hyperbola)::DownCast(aHyp1->Copy());
EXPECT_EQ(myHasher(aHyp1), myHasher(aHyp2));
EXPECT_TRUE(myHasher(aHyp1, aHyp2));
}
TEST_F(GeomHash_CurveHasherTest, Hyperbola_DifferentRadii_DifferentHash)
{
gp_Ax2 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_Hyperbola) aHyp1 = new Geom_Hyperbola(anAxis, 5.0, 3.0);
Handle(Geom_Hyperbola) aHyp2 = new Geom_Hyperbola(anAxis, 5.0, 4.0);
EXPECT_NE(myHasher(aHyp1), myHasher(aHyp2));
EXPECT_FALSE(myHasher(aHyp1, aHyp2));
}
// ============================================================================
// Parabola Tests
// ============================================================================
TEST_F(GeomHash_CurveHasherTest, Parabola_CopiedParabolas_SameHash)
{
gp_Ax2 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_Parabola) aPar1 = new Geom_Parabola(anAxis, 2.0);
Handle(Geom_Parabola) aPar2 = Handle(Geom_Parabola)::DownCast(aPar1->Copy());
EXPECT_EQ(myHasher(aPar1), myHasher(aPar2));
EXPECT_TRUE(myHasher(aPar1, aPar2));
}
TEST_F(GeomHash_CurveHasherTest, Parabola_DifferentFocal_DifferentHash)
{
gp_Ax2 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_Parabola) aPar1 = new Geom_Parabola(anAxis, 2.0);
Handle(Geom_Parabola) aPar2 = new Geom_Parabola(anAxis, 3.0);
EXPECT_NE(myHasher(aPar1), myHasher(aPar2));
EXPECT_FALSE(myHasher(aPar1, aPar2));
}
// ============================================================================
// BezierCurve Tests
// ============================================================================
TEST_F(GeomHash_CurveHasherTest, BezierCurve_CopiedCurves_SameHash)
{
TColgp_Array1OfPnt aPoles(1, 4);
aPoles(1) = gp_Pnt(0.0, 0.0, 0.0);
aPoles(2) = gp_Pnt(1.0, 2.0, 0.0);
aPoles(3) = gp_Pnt(3.0, 2.0, 0.0);
aPoles(4) = gp_Pnt(4.0, 0.0, 0.0);
Handle(Geom_BezierCurve) aCurve1 = new Geom_BezierCurve(aPoles);
Handle(Geom_BezierCurve) aCurve2 = Handle(Geom_BezierCurve)::DownCast(aCurve1->Copy());
EXPECT_EQ(myHasher(aCurve1), myHasher(aCurve2));
EXPECT_TRUE(myHasher(aCurve1, aCurve2));
}
TEST_F(GeomHash_CurveHasherTest, BezierCurve_DifferentPoles_DifferentComparison)
{
TColgp_Array1OfPnt aPoles1(1, 3);
aPoles1(1) = gp_Pnt(0.0, 0.0, 0.0);
aPoles1(2) = gp_Pnt(1.0, 2.0, 0.0);
aPoles1(3) = gp_Pnt(2.0, 0.0, 0.0);
TColgp_Array1OfPnt aPoles2(1, 3);
aPoles2(1) = gp_Pnt(0.0, 0.0, 0.0);
aPoles2(2) = gp_Pnt(1.0, 3.0, 0.0); // Different
aPoles2(3) = gp_Pnt(2.0, 0.0, 0.0);
Handle(Geom_BezierCurve) aCurve1 = new Geom_BezierCurve(aPoles1);
Handle(Geom_BezierCurve) aCurve2 = new Geom_BezierCurve(aPoles2);
// Hash may be same (metadata only), but comparison should differ
EXPECT_FALSE(myHasher(aCurve1, aCurve2));
}
// ============================================================================
// BSplineCurve Tests
// ============================================================================
TEST_F(GeomHash_CurveHasherTest, BSplineCurve_CopiedCurves_SameHash)
{
TColgp_Array1OfPnt aPoles(1, 4);
aPoles(1) = gp_Pnt(0.0, 0.0, 0.0);
aPoles(2) = gp_Pnt(1.0, 1.0, 0.0);
aPoles(3) = gp_Pnt(2.0, 1.0, 0.0);
aPoles(4) = gp_Pnt(3.0, 0.0, 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;
Handle(Geom_BSplineCurve) aCurve1 = new Geom_BSplineCurve(aPoles, aKnots, aMults, 3);
Handle(Geom_BSplineCurve) aCurve2 = Handle(Geom_BSplineCurve)::DownCast(aCurve1->Copy());
EXPECT_EQ(myHasher(aCurve1), myHasher(aCurve2));
EXPECT_TRUE(myHasher(aCurve1, aCurve2));
}
// ============================================================================
// TrimmedCurve Tests
// ============================================================================
TEST_F(GeomHash_CurveHasherTest, TrimmedCurve_CopiedCurves_SameHash)
{
Handle(Geom_Line) aLine = new Geom_Line(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(1.0, 0.0, 0.0));
Handle(Geom_TrimmedCurve) aTrimmed1 = new Geom_TrimmedCurve(aLine, 0.0, 10.0);
Handle(Geom_TrimmedCurve) aTrimmed2 = Handle(Geom_TrimmedCurve)::DownCast(aTrimmed1->Copy());
EXPECT_EQ(myHasher(aTrimmed1), myHasher(aTrimmed2));
EXPECT_TRUE(myHasher(aTrimmed1, aTrimmed2));
}
TEST_F(GeomHash_CurveHasherTest, TrimmedCurve_DifferentBounds_DifferentHash)
{
Handle(Geom_Line) aLine = new Geom_Line(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(1.0, 0.0, 0.0));
Handle(Geom_TrimmedCurve) aTrimmed1 = new Geom_TrimmedCurve(aLine, 0.0, 10.0);
Handle(Geom_TrimmedCurve) aTrimmed2 = new Geom_TrimmedCurve(aLine, 0.0, 20.0);
EXPECT_NE(myHasher(aTrimmed1), myHasher(aTrimmed2));
EXPECT_FALSE(myHasher(aTrimmed1, aTrimmed2));
}
// ============================================================================
// OffsetCurve Tests
// ============================================================================
TEST_F(GeomHash_CurveHasherTest, OffsetCurve_CopiedCurves_SameHash)
{
Handle(Geom_Line) aLine = new Geom_Line(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(1.0, 0.0, 0.0));
gp_Dir aRefDir(0.0, 0.0, 1.0);
Handle(Geom_OffsetCurve) anOffset1 = new Geom_OffsetCurve(aLine, 5.0, aRefDir);
Handle(Geom_OffsetCurve) anOffset2 = Handle(Geom_OffsetCurve)::DownCast(anOffset1->Copy());
EXPECT_EQ(myHasher(anOffset1), myHasher(anOffset2));
EXPECT_TRUE(myHasher(anOffset1, anOffset2));
}
TEST_F(GeomHash_CurveHasherTest, OffsetCurve_DifferentOffset_DifferentHash)
{
Handle(Geom_Line) aLine = new Geom_Line(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(1.0, 0.0, 0.0));
gp_Dir aRefDir(0.0, 0.0, 1.0);
Handle(Geom_OffsetCurve) anOffset1 = new Geom_OffsetCurve(aLine, 5.0, aRefDir);
Handle(Geom_OffsetCurve) anOffset2 = new Geom_OffsetCurve(aLine, 10.0, aRefDir);
EXPECT_NE(myHasher(anOffset1), myHasher(anOffset2));
EXPECT_FALSE(myHasher(anOffset1, anOffset2));
}
// ============================================================================
// Cross-type Tests
// ============================================================================
TEST_F(GeomHash_CurveHasherTest, DifferentTypes_DifferentComparison)
{
Handle(Geom_Line) aLine = new Geom_Line(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(1.0, 0.0, 0.0));
gp_Ax2 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_Circle) aCircle = new Geom_Circle(anAxis, 5.0);
EXPECT_FALSE(myHasher(aLine, aCircle));
}
TEST_F(GeomHash_CurveHasherTest, NullCurves_HandledCorrectly)
{
Handle(Geom_Curve) aNullCurve;
Handle(Geom_Line) aLine = new Geom_Line(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(1.0, 0.0, 0.0));
EXPECT_EQ(myHasher(aNullCurve), 0u);
EXPECT_TRUE(myHasher(aNullCurve, aNullCurve));
EXPECT_FALSE(myHasher(aLine, aNullCurve));
}
TEST_F(GeomHash_CurveHasherTest, SameObject_Equal)
{
Handle(Geom_Line) aLine = new Geom_Line(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(1.0, 0.0, 0.0));
EXPECT_TRUE(myHasher(aLine, aLine));
}
// ============================================================================
// Weighted BSpline Tests
// ============================================================================
TEST_F(GeomHash_CurveHasherTest, BSplineCurve_Weighted_CopiedCurves_SameHash)
{
TColgp_Array1OfPnt aPoles(1, 4);
aPoles(1) = gp_Pnt(0.0, 0.0, 0.0);
aPoles(2) = gp_Pnt(1.0, 1.0, 0.0);
aPoles(3) = gp_Pnt(2.0, 1.0, 0.0);
aPoles(4) = gp_Pnt(3.0, 0.0, 0.0);
TColStd_Array1OfReal aWeights(1, 4);
aWeights(1) = 1.0;
aWeights(2) = 2.0;
aWeights(3) = 2.0;
aWeights(4) = 1.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;
Handle(Geom_BSplineCurve) aCurve1 = new Geom_BSplineCurve(aPoles, aWeights, aKnots, aMults, 3);
Handle(Geom_BSplineCurve) aCurve2 = Handle(Geom_BSplineCurve)::DownCast(aCurve1->Copy());
EXPECT_EQ(myHasher(aCurve1), myHasher(aCurve2));
EXPECT_TRUE(myHasher(aCurve1, aCurve2));
}
TEST_F(GeomHash_CurveHasherTest, BSplineCurve_DifferentWeights_DifferentComparison)
{
TColgp_Array1OfPnt aPoles(1, 4);
aPoles(1) = gp_Pnt(0.0, 0.0, 0.0);
aPoles(2) = gp_Pnt(1.0, 1.0, 0.0);
aPoles(3) = gp_Pnt(2.0, 1.0, 0.0);
aPoles(4) = gp_Pnt(3.0, 0.0, 0.0);
TColStd_Array1OfReal aWeights1(1, 4);
aWeights1(1) = 1.0;
aWeights1(2) = 2.0;
aWeights1(3) = 2.0;
aWeights1(4) = 1.0;
TColStd_Array1OfReal aWeights2(1, 4);
aWeights2(1) = 1.0;
aWeights2(2) = 3.0; // Different
aWeights2(3) = 2.0;
aWeights2(4) = 1.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;
Handle(Geom_BSplineCurve) aCurve1 = new Geom_BSplineCurve(aPoles, aWeights1, aKnots, aMults, 3);
Handle(Geom_BSplineCurve) aCurve2 = new Geom_BSplineCurve(aPoles, aWeights2, aKnots, aMults, 3);
EXPECT_FALSE(myHasher(aCurve1, aCurve2));
}
// ============================================================================
// Weighted Bezier Tests
// ============================================================================
TEST_F(GeomHash_CurveHasherTest, BezierCurve_Weighted_CopiedCurves_SameHash)
{
TColgp_Array1OfPnt aPoles(1, 3);
aPoles(1) = gp_Pnt(0.0, 0.0, 0.0);
aPoles(2) = gp_Pnt(1.0, 2.0, 0.0);
aPoles(3) = gp_Pnt(2.0, 0.0, 0.0);
TColStd_Array1OfReal aWeights(1, 3);
aWeights(1) = 1.0;
aWeights(2) = 2.0;
aWeights(3) = 1.0;
Handle(Geom_BezierCurve) aCurve1 = new Geom_BezierCurve(aPoles, aWeights);
Handle(Geom_BezierCurve) aCurve2 = Handle(Geom_BezierCurve)::DownCast(aCurve1->Copy());
EXPECT_EQ(myHasher(aCurve1), myHasher(aCurve2));
EXPECT_TRUE(myHasher(aCurve1, aCurve2));
}
// ============================================================================
// Axis Orientation Tests
// ============================================================================
TEST_F(GeomHash_CurveHasherTest, Circle_DifferentAxisOrientation_DifferentHash)
{
gp_Ax2 anAxis1(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
gp_Ax2 anAxis2(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 1.0, 0.0));
Handle(Geom_Circle) aCircle1 = new Geom_Circle(anAxis1, 5.0);
Handle(Geom_Circle) aCircle2 = new Geom_Circle(anAxis2, 5.0);
EXPECT_NE(myHasher(aCircle1), myHasher(aCircle2));
EXPECT_FALSE(myHasher(aCircle1, aCircle2));
}
TEST_F(GeomHash_CurveHasherTest, Line_DifferentDirection_DifferentHash)
{
Handle(Geom_Line) aLine1 = new Geom_Line(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(1.0, 0.0, 0.0));
Handle(Geom_Line) aLine2 = new Geom_Line(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 1.0, 0.0));
EXPECT_NE(myHasher(aLine1), myHasher(aLine2));
EXPECT_FALSE(myHasher(aLine1, aLine2));
}
// ============================================================================
// BSpline with Different Knots Tests
// ============================================================================
TEST_F(GeomHash_CurveHasherTest, BSplineCurve_DifferentKnots_DifferentComparison)
{
TColgp_Array1OfPnt aPoles(1, 4);
aPoles(1) = gp_Pnt(0.0, 0.0, 0.0);
aPoles(2) = gp_Pnt(1.0, 1.0, 0.0);
aPoles(3) = gp_Pnt(2.0, 1.0, 0.0);
aPoles(4) = gp_Pnt(3.0, 0.0, 0.0);
TColStd_Array1OfReal aKnots1(1, 2);
aKnots1(1) = 0.0;
aKnots1(2) = 1.0;
TColStd_Array1OfReal aKnots2(1, 2);
aKnots2(1) = 0.0;
aKnots2(2) = 2.0; // Different
TColStd_Array1OfInteger aMults(1, 2);
aMults(1) = 4;
aMults(2) = 4;
Handle(Geom_BSplineCurve) aCurve1 = new Geom_BSplineCurve(aPoles, aKnots1, aMults, 3);
Handle(Geom_BSplineCurve) aCurve2 = new Geom_BSplineCurve(aPoles, aKnots2, aMults, 3);
EXPECT_FALSE(myHasher(aCurve1, aCurve2));
}
// ============================================================================
// Periodic BSpline Tests
// ============================================================================
TEST_F(GeomHash_CurveHasherTest, BSplineCurve_QuadraticBezierEquivalent_CopiedCurves_SameHash)
{
// Simple quadratic B-spline (Bezier-like, single span)
TColgp_Array1OfPnt aPoles(1, 3);
aPoles(1) = gp_Pnt(0.0, 0.0, 0.0);
aPoles(2) = gp_Pnt(1.0, 2.0, 0.0);
aPoles(3) = gp_Pnt(2.0, 0.0, 0.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;
Handle(Geom_BSplineCurve) aCurve1 = new Geom_BSplineCurve(aPoles, aKnots, aMults, 2);
Handle(Geom_BSplineCurve) aCurve2 = Handle(Geom_BSplineCurve)::DownCast(aCurve1->Copy());
EXPECT_EQ(myHasher(aCurve1), myHasher(aCurve2));
EXPECT_TRUE(myHasher(aCurve1, aCurve2));
}
// ============================================================================
// Reversed Curve Tests
// ============================================================================
TEST_F(GeomHash_CurveHasherTest, Line_Reversed_DifferentComparison)
{
Handle(Geom_Line) aLine1 = new Geom_Line(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(1.0, 0.0, 0.0));
Handle(Geom_Line) aLine2 = Handle(Geom_Line)::DownCast(aLine1->Reversed());
// Reversed line has opposite direction
EXPECT_FALSE(myHasher(aLine1, aLine2));
}
TEST_F(GeomHash_CurveHasherTest, BezierCurve_Reversed_DifferentComparison)
{
TColgp_Array1OfPnt aPoles(1, 3);
aPoles(1) = gp_Pnt(0.0, 0.0, 0.0);
aPoles(2) = gp_Pnt(1.0, 2.0, 0.0);
aPoles(3) = gp_Pnt(2.0, 0.0, 0.0);
Handle(Geom_BezierCurve) aCurve1 = new Geom_BezierCurve(aPoles);
Handle(Geom_BezierCurve) aCurve2 = Handle(Geom_BezierCurve)::DownCast(aCurve1->Reversed());
// Reversed curve has poles in different order
EXPECT_FALSE(myHasher(aCurve1, aCurve2));
}
// ============================================================================
// Transformed Curve Tests
// ============================================================================
TEST_F(GeomHash_CurveHasherTest, Circle_Translated_DifferentHash)
{
gp_Ax2 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_Circle) aCircle1 = new Geom_Circle(anAxis, 5.0);
Handle(Geom_Circle) aCircle2 = Handle(Geom_Circle)::DownCast(aCircle1->Copy());
aCircle2->Translate(gp_Vec(1.0, 0.0, 0.0));
EXPECT_NE(myHasher(aCircle1), myHasher(aCircle2));
EXPECT_FALSE(myHasher(aCircle1, aCircle2));
}
TEST_F(GeomHash_CurveHasherTest, Circle_Scaled_DifferentHash)
{
gp_Ax2 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_Circle) aCircle1 = new Geom_Circle(anAxis, 5.0);
Handle(Geom_Circle) aCircle2 = Handle(Geom_Circle)::DownCast(aCircle1->Copy());
aCircle2->Scale(gp_Pnt(0.0, 0.0, 0.0), 2.0);
EXPECT_NE(myHasher(aCircle1), myHasher(aCircle2));
EXPECT_FALSE(myHasher(aCircle1, aCircle2));
}
// ============================================================================
// Higher Degree BSpline Tests
// ============================================================================
TEST_F(GeomHash_CurveHasherTest, BSplineCurve_HigherDegree_CopiedCurves_SameHash)
{
TColgp_Array1OfPnt aPoles(1, 6);
aPoles(1) = gp_Pnt(0.0, 0.0, 0.0);
aPoles(2) = gp_Pnt(1.0, 1.0, 0.0);
aPoles(3) = gp_Pnt(2.0, 0.5, 0.0);
aPoles(4) = gp_Pnt(3.0, 1.5, 0.0);
aPoles(5) = gp_Pnt(4.0, 0.0, 0.0);
aPoles(6) = gp_Pnt(5.0, 1.0, 0.0);
TColStd_Array1OfReal aKnots(1, 2);
aKnots(1) = 0.0;
aKnots(2) = 1.0;
TColStd_Array1OfInteger aMults(1, 2);
aMults(1) = 6;
aMults(2) = 6;
// Degree 5 curve
Handle(Geom_BSplineCurve) aCurve1 = new Geom_BSplineCurve(aPoles, aKnots, aMults, 5);
Handle(Geom_BSplineCurve) aCurve2 = Handle(Geom_BSplineCurve)::DownCast(aCurve1->Copy());
EXPECT_EQ(myHasher(aCurve1), myHasher(aCurve2));
EXPECT_TRUE(myHasher(aCurve1, aCurve2));
}
TEST_F(GeomHash_CurveHasherTest, BSplineCurve_DifferentDegree_DifferentComparison)
{
TColgp_Array1OfPnt aPoles(1, 4);
aPoles(1) = gp_Pnt(0.0, 0.0, 0.0);
aPoles(2) = gp_Pnt(1.0, 1.0, 0.0);
aPoles(3) = gp_Pnt(2.0, 1.0, 0.0);
aPoles(4) = gp_Pnt(3.0, 0.0, 0.0);
TColStd_Array1OfReal aKnots(1, 2);
aKnots(1) = 0.0;
aKnots(2) = 1.0;
TColStd_Array1OfInteger aMults1(1, 2);
aMults1(1) = 4;
aMults1(2) = 4;
TColStd_Array1OfInteger aMults2(1, 2);
aMults2(1) = 3;
aMults2(2) = 3;
// Different degrees: 3 vs 2
Handle(Geom_BSplineCurve) aCurve1 = new Geom_BSplineCurve(aPoles, aKnots, aMults1, 3);
TColgp_Array1OfPnt aPoles2(1, 3);
aPoles2(1) = gp_Pnt(0.0, 0.0, 0.0);
aPoles2(2) = gp_Pnt(1.5, 1.0, 0.0);
aPoles2(3) = gp_Pnt(3.0, 0.0, 0.0);
Handle(Geom_BSplineCurve) aCurve2 = new Geom_BSplineCurve(aPoles2, aKnots, aMults2, 2);
EXPECT_FALSE(myHasher(aCurve1, aCurve2));
}
// ============================================================================
// Multiple Knot Spans Tests
// ============================================================================
TEST_F(GeomHash_CurveHasherTest, BSplineCurve_LinearMultipleSpans_CopiedCurves_SameHash)
{
// Linear B-spline with multiple knots (piecewise linear)
TColgp_Array1OfPnt aPoles(1, 4);
aPoles(1) = gp_Pnt(0.0, 0.0, 0.0);
aPoles(2) = gp_Pnt(1.0, 1.0, 0.0);
aPoles(3) = gp_Pnt(2.0, 0.0, 0.0);
aPoles(4) = gp_Pnt(3.0, 1.0, 0.0);
TColStd_Array1OfReal aKnots(1, 4);
aKnots(1) = 0.0;
aKnots(2) = 1.0;
aKnots(3) = 2.0;
aKnots(4) = 3.0;
TColStd_Array1OfInteger aMults(1, 4);
aMults(1) = 2;
aMults(2) = 1;
aMults(3) = 1;
aMults(4) = 2;
Handle(Geom_BSplineCurve) aCurve1 = new Geom_BSplineCurve(aPoles, aKnots, aMults, 1);
Handle(Geom_BSplineCurve) aCurve2 = Handle(Geom_BSplineCurve)::DownCast(aCurve1->Copy());
EXPECT_EQ(myHasher(aCurve1), myHasher(aCurve2));
EXPECT_TRUE(myHasher(aCurve1, aCurve2));
}
// ============================================================================
// Cross-type Conic Tests
// ============================================================================
TEST_F(GeomHash_CurveHasherTest, Ellipse_vs_Hyperbola_DifferentComparison)
{
gp_Ax2 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_Ellipse) anEllipse = new Geom_Ellipse(anAxis, 5.0, 3.0);
Handle(Geom_Hyperbola) aHyperbola = new Geom_Hyperbola(anAxis, 5.0, 3.0);
EXPECT_FALSE(myHasher(anEllipse, aHyperbola));
}
TEST_F(GeomHash_CurveHasherTest, Circle_vs_Ellipse_DifferentComparison)
{
gp_Ax2 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_Circle) aCircle = new Geom_Circle(anAxis, 5.0);
Handle(Geom_Ellipse) anEllipse = new Geom_Ellipse(anAxis, 5.0, 5.0);
EXPECT_FALSE(myHasher(aCircle, anEllipse));
}
// ============================================================================
// Trimmed vs Base Curve Tests
// ============================================================================
TEST_F(GeomHash_CurveHasherTest, TrimmedCurve_vs_BaseCurve_DifferentComparison)
{
Handle(Geom_Line) aLine = new Geom_Line(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(1.0, 0.0, 0.0));
Handle(Geom_TrimmedCurve) aTrimmed = new Geom_TrimmedCurve(aLine, 0.0, 10.0);
// Trimmed curve is a different type from base curve
EXPECT_FALSE(myHasher(aLine, aTrimmed));
}
// ============================================================================
// Edge Cases - Degenerate/Special Values
// ============================================================================
TEST_F(GeomHash_CurveHasherTest, Circle_VerySmallRadius_CopiedCircles_SameHash)
{
gp_Ax2 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_Circle) aCircle1 = new Geom_Circle(anAxis, 1e-10);
Handle(Geom_Circle) aCircle2 = Handle(Geom_Circle)::DownCast(aCircle1->Copy());
EXPECT_EQ(myHasher(aCircle1), myHasher(aCircle2));
EXPECT_TRUE(myHasher(aCircle1, aCircle2));
}
TEST_F(GeomHash_CurveHasherTest, Circle_VeryLargeRadius_CopiedCircles_SameHash)
{
gp_Ax2 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_Circle) aCircle1 = new Geom_Circle(anAxis, 1e10);
Handle(Geom_Circle) aCircle2 = Handle(Geom_Circle)::DownCast(aCircle1->Copy());
EXPECT_EQ(myHasher(aCircle1), myHasher(aCircle2));
EXPECT_TRUE(myHasher(aCircle1, aCircle2));
}
TEST_F(GeomHash_CurveHasherTest, Line_AtOrigin_vs_FarFromOrigin_DifferentHash)
{
Handle(Geom_Line) aLine1 = new Geom_Line(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(1.0, 0.0, 0.0));
Handle(Geom_Line) aLine2 = new Geom_Line(gp_Pnt(1e10, 1e10, 1e10), gp_Dir(1.0, 0.0, 0.0));
EXPECT_NE(myHasher(aLine1), myHasher(aLine2));
EXPECT_FALSE(myHasher(aLine1, aLine2));
}

View File

@@ -0,0 +1,749 @@
// 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 <gtest/gtest.h>
#include <GeomHash_SurfaceHasher.hxx>
#include <Geom_Plane.hxx>
#include <Geom_CylindricalSurface.hxx>
#include <Geom_ConicalSurface.hxx>
#include <Geom_SphericalSurface.hxx>
#include <Geom_ToroidalSurface.hxx>
#include <Geom_BezierSurface.hxx>
#include <Geom_BSplineSurface.hxx>
#include <Geom_SurfaceOfRevolution.hxx>
#include <Geom_SurfaceOfLinearExtrusion.hxx>
#include <Geom_RectangularTrimmedSurface.hxx>
#include <Geom_OffsetSurface.hxx>
#include <Geom_Line.hxx>
#include <Geom_Circle.hxx>
#include <gp_Ax2.hxx>
#include <gp_Ax3.hxx>
#include <gp_Pnt.hxx>
#include <gp_Dir.hxx>
#include <gp_Vec.hxx>
#include <TColgp_Array2OfPnt.hxx>
#include <TColStd_Array1OfReal.hxx>
#include <TColStd_Array1OfInteger.hxx>
#include <TColStd_Array2OfReal.hxx>
class GeomHash_SurfaceHasherTest : public ::testing::Test
{
protected:
GeomHash_SurfaceHasher myHasher;
};
// ============================================================================
// Plane Tests
// ============================================================================
TEST_F(GeomHash_SurfaceHasherTest, Plane_CopiedPlanes_SameHash)
{
gp_Ax3 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_Plane) aPlane1 = new Geom_Plane(anAxis);
Handle(Geom_Plane) aPlane2 = Handle(Geom_Plane)::DownCast(aPlane1->Copy());
EXPECT_EQ(myHasher(aPlane1), myHasher(aPlane2));
EXPECT_TRUE(myHasher(aPlane1, aPlane2));
}
TEST_F(GeomHash_SurfaceHasherTest, Plane_DifferentPlanes_DifferentHash)
{
Handle(Geom_Plane) aPlane1 = new Geom_Plane(gp_Ax3(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0)));
Handle(Geom_Plane) aPlane2 = new Geom_Plane(gp_Ax3(gp_Pnt(0.0, 0.0, 1.0), gp_Dir(0.0, 0.0, 1.0)));
EXPECT_NE(myHasher(aPlane1), myHasher(aPlane2));
EXPECT_FALSE(myHasher(aPlane1, aPlane2));
}
// ============================================================================
// Cylindrical Surface Tests
// ============================================================================
TEST_F(GeomHash_SurfaceHasherTest, CylindricalSurface_CopiedSurfaces_SameHash)
{
gp_Ax3 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_CylindricalSurface) aCyl1 = new Geom_CylindricalSurface(anAxis, 5.0);
Handle(Geom_CylindricalSurface) aCyl2 = Handle(Geom_CylindricalSurface)::DownCast(aCyl1->Copy());
EXPECT_EQ(myHasher(aCyl1), myHasher(aCyl2));
EXPECT_TRUE(myHasher(aCyl1, aCyl2));
}
TEST_F(GeomHash_SurfaceHasherTest, CylindricalSurface_DifferentRadius_DifferentHash)
{
gp_Ax3 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_CylindricalSurface) aCyl1 = new Geom_CylindricalSurface(anAxis, 5.0);
Handle(Geom_CylindricalSurface) aCyl2 = new Geom_CylindricalSurface(anAxis, 10.0);
EXPECT_NE(myHasher(aCyl1), myHasher(aCyl2));
EXPECT_FALSE(myHasher(aCyl1, aCyl2));
}
// ============================================================================
// Conical Surface Tests
// ============================================================================
TEST_F(GeomHash_SurfaceHasherTest, ConicalSurface_CopiedSurfaces_SameHash)
{
gp_Ax3 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_ConicalSurface) aCone1 = new Geom_ConicalSurface(anAxis, M_PI / 6.0, 5.0);
Handle(Geom_ConicalSurface) aCone2 = Handle(Geom_ConicalSurface)::DownCast(aCone1->Copy());
EXPECT_EQ(myHasher(aCone1), myHasher(aCone2));
EXPECT_TRUE(myHasher(aCone1, aCone2));
}
TEST_F(GeomHash_SurfaceHasherTest, ConicalSurface_DifferentAngle_DifferentHash)
{
gp_Ax3 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_ConicalSurface) aCone1 = new Geom_ConicalSurface(anAxis, M_PI / 6.0, 5.0);
Handle(Geom_ConicalSurface) aCone2 = new Geom_ConicalSurface(anAxis, M_PI / 4.0, 5.0);
EXPECT_NE(myHasher(aCone1), myHasher(aCone2));
EXPECT_FALSE(myHasher(aCone1, aCone2));
}
// ============================================================================
// Spherical Surface Tests
// ============================================================================
TEST_F(GeomHash_SurfaceHasherTest, SphericalSurface_CopiedSurfaces_SameHash)
{
gp_Ax3 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_SphericalSurface) aSphere1 = new Geom_SphericalSurface(anAxis, 5.0);
Handle(Geom_SphericalSurface) aSphere2 =
Handle(Geom_SphericalSurface)::DownCast(aSphere1->Copy());
EXPECT_EQ(myHasher(aSphere1), myHasher(aSphere2));
EXPECT_TRUE(myHasher(aSphere1, aSphere2));
}
TEST_F(GeomHash_SurfaceHasherTest, SphericalSurface_DifferentRadius_DifferentHash)
{
gp_Ax3 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_SphericalSurface) aSphere1 = new Geom_SphericalSurface(anAxis, 5.0);
Handle(Geom_SphericalSurface) aSphere2 = new Geom_SphericalSurface(anAxis, 10.0);
EXPECT_NE(myHasher(aSphere1), myHasher(aSphere2));
EXPECT_FALSE(myHasher(aSphere1, aSphere2));
}
// ============================================================================
// Toroidal Surface Tests
// ============================================================================
TEST_F(GeomHash_SurfaceHasherTest, ToroidalSurface_CopiedSurfaces_SameHash)
{
gp_Ax3 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_ToroidalSurface) aTorus1 = new Geom_ToroidalSurface(anAxis, 10.0, 3.0);
Handle(Geom_ToroidalSurface) aTorus2 = Handle(Geom_ToroidalSurface)::DownCast(aTorus1->Copy());
EXPECT_EQ(myHasher(aTorus1), myHasher(aTorus2));
EXPECT_TRUE(myHasher(aTorus1, aTorus2));
}
TEST_F(GeomHash_SurfaceHasherTest, ToroidalSurface_DifferentRadii_DifferentHash)
{
gp_Ax3 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_ToroidalSurface) aTorus1 = new Geom_ToroidalSurface(anAxis, 10.0, 3.0);
Handle(Geom_ToroidalSurface) aTorus2 = new Geom_ToroidalSurface(anAxis, 10.0, 5.0);
EXPECT_NE(myHasher(aTorus1), myHasher(aTorus2));
EXPECT_FALSE(myHasher(aTorus1, aTorus2));
}
// ============================================================================
// BezierSurface Tests
// ============================================================================
TEST_F(GeomHash_SurfaceHasherTest, BezierSurface_CopiedSurfaces_SameHash)
{
TColgp_Array2OfPnt aPoles(1, 2, 1, 2);
aPoles(1, 1) = gp_Pnt(0.0, 0.0, 0.0);
aPoles(1, 2) = gp_Pnt(1.0, 0.0, 0.0);
aPoles(2, 1) = gp_Pnt(0.0, 1.0, 0.0);
aPoles(2, 2) = gp_Pnt(1.0, 1.0, 1.0);
Handle(Geom_BezierSurface) aSurf1 = new Geom_BezierSurface(aPoles);
Handle(Geom_BezierSurface) aSurf2 = Handle(Geom_BezierSurface)::DownCast(aSurf1->Copy());
EXPECT_EQ(myHasher(aSurf1), myHasher(aSurf2));
EXPECT_TRUE(myHasher(aSurf1, aSurf2));
}
TEST_F(GeomHash_SurfaceHasherTest, BezierSurface_DifferentPoles_DifferentComparison)
{
TColgp_Array2OfPnt aPoles1(1, 2, 1, 2);
aPoles1(1, 1) = gp_Pnt(0.0, 0.0, 0.0);
aPoles1(1, 2) = gp_Pnt(1.0, 0.0, 0.0);
aPoles1(2, 1) = gp_Pnt(0.0, 1.0, 0.0);
aPoles1(2, 2) = gp_Pnt(1.0, 1.0, 1.0);
TColgp_Array2OfPnt aPoles2(1, 2, 1, 2);
aPoles2(1, 1) = gp_Pnt(0.0, 0.0, 0.0);
aPoles2(1, 2) = gp_Pnt(1.0, 0.0, 0.0);
aPoles2(2, 1) = gp_Pnt(0.0, 1.0, 0.0);
aPoles2(2, 2) = gp_Pnt(1.0, 1.0, 2.0); // Different
Handle(Geom_BezierSurface) aSurf1 = new Geom_BezierSurface(aPoles1);
Handle(Geom_BezierSurface) aSurf2 = new Geom_BezierSurface(aPoles2);
// Hash may be same (metadata only), but comparison should differ
EXPECT_FALSE(myHasher(aSurf1, aSurf2));
}
// ============================================================================
// BSplineSurface Tests
// ============================================================================
TEST_F(GeomHash_SurfaceHasherTest, BSplineSurface_CopiedSurfaces_SameHash)
{
TColgp_Array2OfPnt aPoles(1, 2, 1, 2);
aPoles(1, 1) = gp_Pnt(0.0, 0.0, 0.0);
aPoles(1, 2) = gp_Pnt(1.0, 0.0, 0.0);
aPoles(2, 1) = gp_Pnt(0.0, 1.0, 0.0);
aPoles(2, 2) = gp_Pnt(1.0, 1.0, 1.0);
TColStd_Array1OfReal aUKnots(1, 2), aVKnots(1, 2);
aUKnots(1) = 0.0;
aUKnots(2) = 1.0;
aVKnots(1) = 0.0;
aVKnots(2) = 1.0;
TColStd_Array1OfInteger aUMults(1, 2), aVMults(1, 2);
aUMults(1) = 2;
aUMults(2) = 2;
aVMults(1) = 2;
aVMults(2) = 2;
Handle(Geom_BSplineSurface) aSurf1 =
new Geom_BSplineSurface(aPoles, aUKnots, aVKnots, aUMults, aVMults, 1, 1);
Handle(Geom_BSplineSurface) aSurf2 = Handle(Geom_BSplineSurface)::DownCast(aSurf1->Copy());
EXPECT_EQ(myHasher(aSurf1), myHasher(aSurf2));
EXPECT_TRUE(myHasher(aSurf1, aSurf2));
}
// ============================================================================
// SurfaceOfRevolution Tests
// ============================================================================
TEST_F(GeomHash_SurfaceHasherTest, SurfaceOfRevolution_CopiedSurfaces_SameHash)
{
Handle(Geom_Line) aLine = new Geom_Line(gp_Pnt(1.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
gp_Ax1 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_SurfaceOfRevolution) aRev1 = new Geom_SurfaceOfRevolution(aLine, anAxis);
Handle(Geom_SurfaceOfRevolution) aRev2 =
Handle(Geom_SurfaceOfRevolution)::DownCast(aRev1->Copy());
EXPECT_EQ(myHasher(aRev1), myHasher(aRev2));
EXPECT_TRUE(myHasher(aRev1, aRev2));
}
// ============================================================================
// SurfaceOfLinearExtrusion Tests
// ============================================================================
TEST_F(GeomHash_SurfaceHasherTest, SurfaceOfLinearExtrusion_CopiedSurfaces_SameHash)
{
Handle(Geom_Line) aLine = new Geom_Line(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(1.0, 0.0, 0.0));
gp_Dir aDir(0.0, 0.0, 1.0);
Handle(Geom_SurfaceOfLinearExtrusion) anExt1 = new Geom_SurfaceOfLinearExtrusion(aLine, aDir);
Handle(Geom_SurfaceOfLinearExtrusion) anExt2 =
Handle(Geom_SurfaceOfLinearExtrusion)::DownCast(anExt1->Copy());
EXPECT_EQ(myHasher(anExt1), myHasher(anExt2));
EXPECT_TRUE(myHasher(anExt1, anExt2));
}
// ============================================================================
// RectangularTrimmedSurface Tests
// ============================================================================
TEST_F(GeomHash_SurfaceHasherTest, RectangularTrimmedSurface_CopiedSurfaces_SameHash)
{
gp_Ax3 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_Plane) aPlane = new Geom_Plane(anAxis);
Handle(Geom_RectangularTrimmedSurface) aTrimmed1 =
new Geom_RectangularTrimmedSurface(aPlane, 0.0, 10.0, 0.0, 10.0);
Handle(Geom_RectangularTrimmedSurface) aTrimmed2 =
Handle(Geom_RectangularTrimmedSurface)::DownCast(aTrimmed1->Copy());
EXPECT_EQ(myHasher(aTrimmed1), myHasher(aTrimmed2));
EXPECT_TRUE(myHasher(aTrimmed1, aTrimmed2));
}
TEST_F(GeomHash_SurfaceHasherTest, RectangularTrimmedSurface_DifferentBounds_DifferentHash)
{
gp_Ax3 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_Plane) aPlane = new Geom_Plane(anAxis);
Handle(Geom_RectangularTrimmedSurface) aTrimmed1 =
new Geom_RectangularTrimmedSurface(aPlane, 0.0, 10.0, 0.0, 10.0);
Handle(Geom_RectangularTrimmedSurface) aTrimmed2 =
new Geom_RectangularTrimmedSurface(aPlane, 0.0, 20.0, 0.0, 10.0);
EXPECT_NE(myHasher(aTrimmed1), myHasher(aTrimmed2));
EXPECT_FALSE(myHasher(aTrimmed1, aTrimmed2));
}
// ============================================================================
// OffsetSurface Tests
// ============================================================================
TEST_F(GeomHash_SurfaceHasherTest, OffsetSurface_CopiedSurfaces_SameHash)
{
gp_Ax3 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_Plane) aPlane = new Geom_Plane(anAxis);
Handle(Geom_OffsetSurface) anOffset1 = new Geom_OffsetSurface(aPlane, 5.0);
Handle(Geom_OffsetSurface) anOffset2 = Handle(Geom_OffsetSurface)::DownCast(anOffset1->Copy());
EXPECT_EQ(myHasher(anOffset1), myHasher(anOffset2));
EXPECT_TRUE(myHasher(anOffset1, anOffset2));
}
TEST_F(GeomHash_SurfaceHasherTest, OffsetSurface_DifferentOffset_DifferentHash)
{
gp_Ax3 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_Plane) aPlane = new Geom_Plane(anAxis);
Handle(Geom_OffsetSurface) anOffset1 = new Geom_OffsetSurface(aPlane, 5.0);
Handle(Geom_OffsetSurface) anOffset2 = new Geom_OffsetSurface(aPlane, 10.0);
EXPECT_NE(myHasher(anOffset1), myHasher(anOffset2));
EXPECT_FALSE(myHasher(anOffset1, anOffset2));
}
// ============================================================================
// Cross-type Tests
// ============================================================================
TEST_F(GeomHash_SurfaceHasherTest, DifferentTypes_DifferentComparison)
{
gp_Ax3 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_Plane) aPlane = new Geom_Plane(anAxis);
Handle(Geom_SphericalSurface) aSphere = new Geom_SphericalSurface(anAxis, 5.0);
EXPECT_FALSE(myHasher(aPlane, aSphere));
}
TEST_F(GeomHash_SurfaceHasherTest, NullSurfaces_HandledCorrectly)
{
Handle(Geom_Surface) aNullSurface;
gp_Ax3 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_Plane) aPlane = new Geom_Plane(anAxis);
EXPECT_EQ(myHasher(aNullSurface), 0u);
EXPECT_TRUE(myHasher(aNullSurface, aNullSurface));
EXPECT_FALSE(myHasher(aPlane, aNullSurface));
}
TEST_F(GeomHash_SurfaceHasherTest, SameObject_Equal)
{
gp_Ax3 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_Plane) aPlane = new Geom_Plane(anAxis);
EXPECT_TRUE(myHasher(aPlane, aPlane));
}
// ============================================================================
// Weighted BSpline Surface Tests
// ============================================================================
TEST_F(GeomHash_SurfaceHasherTest, BSplineSurface_Weighted_CopiedSurfaces_SameHash)
{
TColgp_Array2OfPnt aPoles(1, 2, 1, 2);
aPoles(1, 1) = gp_Pnt(0.0, 0.0, 0.0);
aPoles(1, 2) = gp_Pnt(1.0, 0.0, 0.0);
aPoles(2, 1) = gp_Pnt(0.0, 1.0, 0.0);
aPoles(2, 2) = gp_Pnt(1.0, 1.0, 1.0);
TColStd_Array2OfReal aWeights(1, 2, 1, 2);
aWeights(1, 1) = 1.0;
aWeights(1, 2) = 1.0;
aWeights(2, 1) = 1.0;
aWeights(2, 2) = 2.0;
TColStd_Array1OfReal aUKnots(1, 2), aVKnots(1, 2);
aUKnots(1) = 0.0;
aUKnots(2) = 1.0;
aVKnots(1) = 0.0;
aVKnots(2) = 1.0;
TColStd_Array1OfInteger aUMults(1, 2), aVMults(1, 2);
aUMults(1) = 2;
aUMults(2) = 2;
aVMults(1) = 2;
aVMults(2) = 2;
Handle(Geom_BSplineSurface) aSurf1 =
new Geom_BSplineSurface(aPoles, aWeights, aUKnots, aVKnots, aUMults, aVMults, 1, 1);
Handle(Geom_BSplineSurface) aSurf2 = Handle(Geom_BSplineSurface)::DownCast(aSurf1->Copy());
EXPECT_EQ(myHasher(aSurf1), myHasher(aSurf2));
EXPECT_TRUE(myHasher(aSurf1, aSurf2));
}
TEST_F(GeomHash_SurfaceHasherTest, BSplineSurface_DifferentWeights_DifferentComparison)
{
TColgp_Array2OfPnt aPoles(1, 2, 1, 2);
aPoles(1, 1) = gp_Pnt(0.0, 0.0, 0.0);
aPoles(1, 2) = gp_Pnt(1.0, 0.0, 0.0);
aPoles(2, 1) = gp_Pnt(0.0, 1.0, 0.0);
aPoles(2, 2) = gp_Pnt(1.0, 1.0, 1.0);
TColStd_Array2OfReal aWeights1(1, 2, 1, 2);
aWeights1(1, 1) = 1.0;
aWeights1(1, 2) = 1.0;
aWeights1(2, 1) = 1.0;
aWeights1(2, 2) = 2.0;
TColStd_Array2OfReal aWeights2(1, 2, 1, 2);
aWeights2(1, 1) = 1.0;
aWeights2(1, 2) = 1.0;
aWeights2(2, 1) = 1.0;
aWeights2(2, 2) = 3.0; // Different
TColStd_Array1OfReal aUKnots(1, 2), aVKnots(1, 2);
aUKnots(1) = 0.0;
aUKnots(2) = 1.0;
aVKnots(1) = 0.0;
aVKnots(2) = 1.0;
TColStd_Array1OfInteger aUMults(1, 2), aVMults(1, 2);
aUMults(1) = 2;
aUMults(2) = 2;
aVMults(1) = 2;
aVMults(2) = 2;
Handle(Geom_BSplineSurface) aSurf1 =
new Geom_BSplineSurface(aPoles, aWeights1, aUKnots, aVKnots, aUMults, aVMults, 1, 1);
Handle(Geom_BSplineSurface) aSurf2 =
new Geom_BSplineSurface(aPoles, aWeights2, aUKnots, aVKnots, aUMults, aVMults, 1, 1);
EXPECT_FALSE(myHasher(aSurf1, aSurf2));
}
// ============================================================================
// Weighted Bezier Surface Tests
// ============================================================================
TEST_F(GeomHash_SurfaceHasherTest, BezierSurface_Weighted_CopiedSurfaces_SameHash)
{
TColgp_Array2OfPnt aPoles(1, 2, 1, 2);
aPoles(1, 1) = gp_Pnt(0.0, 0.0, 0.0);
aPoles(1, 2) = gp_Pnt(1.0, 0.0, 0.0);
aPoles(2, 1) = gp_Pnt(0.0, 1.0, 0.0);
aPoles(2, 2) = gp_Pnt(1.0, 1.0, 1.0);
TColStd_Array2OfReal aWeights(1, 2, 1, 2);
aWeights(1, 1) = 1.0;
aWeights(1, 2) = 1.0;
aWeights(2, 1) = 1.0;
aWeights(2, 2) = 2.0;
Handle(Geom_BezierSurface) aSurf1 = new Geom_BezierSurface(aPoles, aWeights);
Handle(Geom_BezierSurface) aSurf2 = Handle(Geom_BezierSurface)::DownCast(aSurf1->Copy());
EXPECT_EQ(myHasher(aSurf1), myHasher(aSurf2));
EXPECT_TRUE(myHasher(aSurf1, aSurf2));
}
// ============================================================================
// Axis Orientation Tests
// ============================================================================
TEST_F(GeomHash_SurfaceHasherTest, Plane_DifferentNormal_DifferentHash)
{
gp_Ax3 anAxis1(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
gp_Ax3 anAxis2(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 1.0, 0.0));
Handle(Geom_Plane) aPlane1 = new Geom_Plane(anAxis1);
Handle(Geom_Plane) aPlane2 = new Geom_Plane(anAxis2);
EXPECT_NE(myHasher(aPlane1), myHasher(aPlane2));
EXPECT_FALSE(myHasher(aPlane1, aPlane2));
}
TEST_F(GeomHash_SurfaceHasherTest, CylindricalSurface_DifferentAxis_DifferentHash)
{
gp_Ax3 anAxis1(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
gp_Ax3 anAxis2(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(1.0, 0.0, 0.0));
Handle(Geom_CylindricalSurface) aCyl1 = new Geom_CylindricalSurface(anAxis1, 5.0);
Handle(Geom_CylindricalSurface) aCyl2 = new Geom_CylindricalSurface(anAxis2, 5.0);
EXPECT_NE(myHasher(aCyl1), myHasher(aCyl2));
EXPECT_FALSE(myHasher(aCyl1, aCyl2));
}
// ============================================================================
// Revolution with Different Basis Curve Tests
// ============================================================================
TEST_F(GeomHash_SurfaceHasherTest, SurfaceOfRevolution_DifferentBasisCurve_DifferentComparison)
{
Handle(Geom_Line) aLine1 = new Geom_Line(gp_Pnt(1.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_Line) aLine2 = new Geom_Line(gp_Pnt(2.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
gp_Ax1 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_SurfaceOfRevolution) aRev1 = new Geom_SurfaceOfRevolution(aLine1, anAxis);
Handle(Geom_SurfaceOfRevolution) aRev2 = new Geom_SurfaceOfRevolution(aLine2, anAxis);
EXPECT_FALSE(myHasher(aRev1, aRev2));
}
// ============================================================================
// Extrusion with Different Direction Tests
// ============================================================================
TEST_F(GeomHash_SurfaceHasherTest, SurfaceOfLinearExtrusion_DifferentDirection_DifferentHash)
{
Handle(Geom_Line) aLine = new Geom_Line(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(1.0, 0.0, 0.0));
gp_Dir aDir1(0.0, 0.0, 1.0);
gp_Dir aDir2(0.0, 1.0, 0.0);
Handle(Geom_SurfaceOfLinearExtrusion) anExt1 = new Geom_SurfaceOfLinearExtrusion(aLine, aDir1);
Handle(Geom_SurfaceOfLinearExtrusion) anExt2 = new Geom_SurfaceOfLinearExtrusion(aLine, aDir2);
EXPECT_NE(myHasher(anExt1), myHasher(anExt2));
EXPECT_FALSE(myHasher(anExt1, anExt2));
}
// ============================================================================
// BSpline Surface with Different Knots Tests
// ============================================================================
TEST_F(GeomHash_SurfaceHasherTest, BSplineSurface_DifferentKnots_DifferentComparison)
{
TColgp_Array2OfPnt aPoles(1, 2, 1, 2);
aPoles(1, 1) = gp_Pnt(0.0, 0.0, 0.0);
aPoles(1, 2) = gp_Pnt(1.0, 0.0, 0.0);
aPoles(2, 1) = gp_Pnt(0.0, 1.0, 0.0);
aPoles(2, 2) = gp_Pnt(1.0, 1.0, 1.0);
TColStd_Array1OfReal aUKnots1(1, 2), aUKnots2(1, 2), aVKnots(1, 2);
aUKnots1(1) = 0.0;
aUKnots1(2) = 1.0;
aUKnots2(1) = 0.0;
aUKnots2(2) = 2.0; // Different
aVKnots(1) = 0.0;
aVKnots(2) = 1.0;
TColStd_Array1OfInteger aUMults(1, 2), aVMults(1, 2);
aUMults(1) = 2;
aUMults(2) = 2;
aVMults(1) = 2;
aVMults(2) = 2;
Handle(Geom_BSplineSurface) aSurf1 =
new Geom_BSplineSurface(aPoles, aUKnots1, aVKnots, aUMults, aVMults, 1, 1);
Handle(Geom_BSplineSurface) aSurf2 =
new Geom_BSplineSurface(aPoles, aUKnots2, aVKnots, aUMults, aVMults, 1, 1);
EXPECT_FALSE(myHasher(aSurf1, aSurf2));
}
// ============================================================================
// Transformed Surface Tests
// ============================================================================
TEST_F(GeomHash_SurfaceHasherTest, Plane_Translated_DifferentHash)
{
gp_Ax3 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_Plane) aPlane1 = new Geom_Plane(anAxis);
Handle(Geom_Plane) aPlane2 = Handle(Geom_Plane)::DownCast(aPlane1->Copy());
aPlane2->Translate(gp_Vec(0.0, 0.0, 1.0));
EXPECT_NE(myHasher(aPlane1), myHasher(aPlane2));
EXPECT_FALSE(myHasher(aPlane1, aPlane2));
}
TEST_F(GeomHash_SurfaceHasherTest, Sphere_Scaled_DifferentHash)
{
gp_Ax3 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_SphericalSurface) aSphere1 = new Geom_SphericalSurface(anAxis, 5.0);
Handle(Geom_SphericalSurface) aSphere2 =
Handle(Geom_SphericalSurface)::DownCast(aSphere1->Copy());
aSphere2->Scale(gp_Pnt(0.0, 0.0, 0.0), 2.0);
EXPECT_NE(myHasher(aSphere1), myHasher(aSphere2));
EXPECT_FALSE(myHasher(aSphere1, aSphere2));
}
// ============================================================================
// UReversed/VReversed Surface Tests
// ============================================================================
TEST_F(GeomHash_SurfaceHasherTest, BezierSurface_UReversed_DifferentComparison)
{
TColgp_Array2OfPnt aPoles(1, 2, 1, 2);
aPoles(1, 1) = gp_Pnt(0.0, 0.0, 0.0);
aPoles(1, 2) = gp_Pnt(1.0, 0.0, 0.0);
aPoles(2, 1) = gp_Pnt(0.0, 1.0, 0.0);
aPoles(2, 2) = gp_Pnt(1.0, 1.0, 1.0);
Handle(Geom_BezierSurface) aSurf1 = new Geom_BezierSurface(aPoles);
Handle(Geom_BezierSurface) aSurf2 = Handle(Geom_BezierSurface)::DownCast(aSurf1->UReversed());
EXPECT_FALSE(myHasher(aSurf1, aSurf2));
}
TEST_F(GeomHash_SurfaceHasherTest, BezierSurface_VReversed_DifferentComparison)
{
TColgp_Array2OfPnt aPoles(1, 2, 1, 2);
aPoles(1, 1) = gp_Pnt(0.0, 0.0, 0.0);
aPoles(1, 2) = gp_Pnt(1.0, 0.0, 0.0);
aPoles(2, 1) = gp_Pnt(0.0, 1.0, 0.0);
aPoles(2, 2) = gp_Pnt(1.0, 1.0, 1.0);
Handle(Geom_BezierSurface) aSurf1 = new Geom_BezierSurface(aPoles);
Handle(Geom_BezierSurface) aSurf2 = Handle(Geom_BezierSurface)::DownCast(aSurf1->VReversed());
EXPECT_FALSE(myHasher(aSurf1, aSurf2));
}
// ============================================================================
// Higher Degree BSpline Surface Tests
// ============================================================================
TEST_F(GeomHash_SurfaceHasherTest, BSplineSurface_HigherDegree_CopiedSurfaces_SameHash)
{
TColgp_Array2OfPnt aPoles(1, 4, 1, 4);
for (Standard_Integer i = 1; i <= 4; ++i)
{
for (Standard_Integer j = 1; j <= 4; ++j)
{
aPoles(i, j) = gp_Pnt((i - 1) * 1.0, (j - 1) * 1.0, (i + j) * 0.1);
}
}
TColStd_Array1OfReal aUKnots(1, 2), aVKnots(1, 2);
aUKnots(1) = 0.0;
aUKnots(2) = 1.0;
aVKnots(1) = 0.0;
aVKnots(2) = 1.0;
TColStd_Array1OfInteger aUMults(1, 2), aVMults(1, 2);
aUMults(1) = 4;
aUMults(2) = 4;
aVMults(1) = 4;
aVMults(2) = 4;
Handle(Geom_BSplineSurface) aSurf1 =
new Geom_BSplineSurface(aPoles, aUKnots, aVKnots, aUMults, aVMults, 3, 3);
Handle(Geom_BSplineSurface) aSurf2 = Handle(Geom_BSplineSurface)::DownCast(aSurf1->Copy());
EXPECT_EQ(myHasher(aSurf1), myHasher(aSurf2));
EXPECT_TRUE(myHasher(aSurf1, aSurf2));
}
// ============================================================================
// Cross-type Elementary Surface Tests
// ============================================================================
TEST_F(GeomHash_SurfaceHasherTest, Cylinder_vs_Cone_DifferentComparison)
{
gp_Ax3 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_CylindricalSurface) aCylinder = new Geom_CylindricalSurface(anAxis, 5.0);
Handle(Geom_ConicalSurface) aCone = new Geom_ConicalSurface(anAxis, M_PI / 6.0, 5.0);
EXPECT_FALSE(myHasher(aCylinder, aCone));
}
TEST_F(GeomHash_SurfaceHasherTest, Sphere_vs_Torus_DifferentComparison)
{
gp_Ax3 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_SphericalSurface) aSphere = new Geom_SphericalSurface(anAxis, 5.0);
Handle(Geom_ToroidalSurface) aTorus = new Geom_ToroidalSurface(anAxis, 5.0, 1.0);
EXPECT_FALSE(myHasher(aSphere, aTorus));
}
// ============================================================================
// Trimmed vs Base Surface Tests
// ============================================================================
TEST_F(GeomHash_SurfaceHasherTest, RectangularTrimmedSurface_vs_BaseSurface_DifferentComparison)
{
gp_Ax3 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_Plane) aPlane = new Geom_Plane(anAxis);
Handle(Geom_RectangularTrimmedSurface) aTrimmed =
new Geom_RectangularTrimmedSurface(aPlane, 0.0, 10.0, 0.0, 10.0);
EXPECT_FALSE(myHasher(aPlane, aTrimmed));
}
// ============================================================================
// Edge Cases - Degenerate/Special Values
// ============================================================================
TEST_F(GeomHash_SurfaceHasherTest, Sphere_VerySmallRadius_CopiedSpheres_SameHash)
{
gp_Ax3 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_SphericalSurface) aSphere1 = new Geom_SphericalSurface(anAxis, 1e-10);
Handle(Geom_SphericalSurface) aSphere2 =
Handle(Geom_SphericalSurface)::DownCast(aSphere1->Copy());
EXPECT_EQ(myHasher(aSphere1), myHasher(aSphere2));
EXPECT_TRUE(myHasher(aSphere1, aSphere2));
}
TEST_F(GeomHash_SurfaceHasherTest, Sphere_VeryLargeRadius_CopiedSpheres_SameHash)
{
gp_Ax3 anAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_SphericalSurface) aSphere1 = new Geom_SphericalSurface(anAxis, 1e10);
Handle(Geom_SphericalSurface) aSphere2 =
Handle(Geom_SphericalSurface)::DownCast(aSphere1->Copy());
EXPECT_EQ(myHasher(aSphere1), myHasher(aSphere2));
EXPECT_TRUE(myHasher(aSphere1, aSphere2));
}
TEST_F(GeomHash_SurfaceHasherTest, Plane_AtOrigin_vs_FarFromOrigin_DifferentHash)
{
gp_Ax3 anAxis1(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
gp_Ax3 anAxis2(gp_Pnt(1e10, 1e10, 1e10), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_Plane) aPlane1 = new Geom_Plane(anAxis1);
Handle(Geom_Plane) aPlane2 = new Geom_Plane(anAxis2);
EXPECT_NE(myHasher(aPlane1), myHasher(aPlane2));
EXPECT_FALSE(myHasher(aPlane1, aPlane2));
}
// ============================================================================
// Revolution with Circle Basis (forms Torus-like)
// ============================================================================
TEST_F(GeomHash_SurfaceHasherTest, SurfaceOfRevolution_CircleBasis_CopiedSurfaces_SameHash)
{
gp_Ax2 aCircleAxis(gp_Pnt(5.0, 0.0, 0.0), gp_Dir(0.0, 1.0, 0.0));
Handle(Geom_Circle) aCircle = new Geom_Circle(aCircleAxis, 1.0);
gp_Ax1 aRevAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_SurfaceOfRevolution) aRev1 = new Geom_SurfaceOfRevolution(aCircle, aRevAxis);
Handle(Geom_SurfaceOfRevolution) aRev2 =
Handle(Geom_SurfaceOfRevolution)::DownCast(aRev1->Copy());
EXPECT_EQ(myHasher(aRev1), myHasher(aRev2));
EXPECT_TRUE(myHasher(aRev1, aRev2));
}
// ============================================================================
// Offset Surface with Different Base
// ============================================================================
TEST_F(GeomHash_SurfaceHasherTest, OffsetSurface_DifferentBaseSurface_DifferentComparison)
{
gp_Ax3 anAxis1(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
gp_Ax3 anAxis2(gp_Pnt(1.0, 0.0, 0.0), gp_Dir(0.0, 0.0, 1.0));
Handle(Geom_Plane) aPlane1 = new Geom_Plane(anAxis1);
Handle(Geom_Plane) aPlane2 = new Geom_Plane(anAxis2);
Handle(Geom_OffsetSurface) anOffset1 = new Geom_OffsetSurface(aPlane1, 5.0);
Handle(Geom_OffsetSurface) anOffset2 = new Geom_OffsetSurface(aPlane2, 5.0);
EXPECT_FALSE(myHasher(anOffset1, anOffset2));
}

View File

@@ -0,0 +1,38 @@
# Source files for GeomHash package
set(OCCT_GeomHash_FILES_LOCATION "${CMAKE_CURRENT_LIST_DIR}")
set(OCCT_GeomHash_FILES
# Foundational Hashers
GeomHash_PointHasher.pxx
GeomHash_DirectionHasher.pxx
GeomHash_VectorHasher.pxx
GeomHash_AxisPlacement.pxx
# Surface Hashers
GeomHash_PlaneHasher.pxx
GeomHash_CylindricalSurfaceHasher.pxx
GeomHash_ConicalSurfaceHasher.pxx
GeomHash_SphericalSurfaceHasher.pxx
GeomHash_ToroidalSurfaceHasher.pxx
GeomHash_SurfaceOfRevolutionHasher.pxx
GeomHash_SurfaceOfLinearExtrusionHasher.pxx
GeomHash_BezierSurfaceHasher.pxx
GeomHash_BSplineSurfaceHasher.pxx
GeomHash_RectangularTrimmedSurfaceHasher.pxx
GeomHash_OffsetSurfaceHasher.pxx
GeomHash_SurfaceHasher.hxx
GeomHash_SurfaceHasher.cxx
# Curve Hashers
GeomHash_LineHasher.pxx
GeomHash_CircleHasher.pxx
GeomHash_EllipseHasher.pxx
GeomHash_HyperbolaHasher.pxx
GeomHash_ParabolaHasher.pxx
GeomHash_BezierCurveHasher.pxx
GeomHash_BSplineCurveHasher.pxx
GeomHash_TrimmedCurveHasher.pxx
GeomHash_OffsetCurveHasher.pxx
GeomHash_CurveHasher.hxx
GeomHash_CurveHasher.cxx
)

View File

@@ -0,0 +1,52 @@
// 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 _GeomHash_AxisPlacement_HeaderFile
#define _GeomHash_AxisPlacement_HeaderFile
#include <Standard_HashUtils.hxx>
#include <gp_Ax2.hxx>
#include <GeomHash_PointHasher.pxx>
#include <GeomHash_DirectionHasher.pxx>
//! OCCT-style hasher for gp_Ax2 (axis placement).
//! Used for geometry deduplication.
//! Compositional hasher using PointHasher and DirectionHasher.
struct GeomHash_AxisPlacement
{
// Hashes the axis placement by location, axis direction, and X direction.
std::size_t operator()(const gp_Ax2& theAxisPlacement) const noexcept
{
const GeomHash_PointHasher aPointHasher;
const GeomHash_DirectionHasher aDirectionHasher;
const std::size_t aHashes[3] = {aPointHasher(theAxisPlacement.Location()),
aDirectionHasher(theAxisPlacement.Direction()),
aDirectionHasher(theAxisPlacement.XDirection())};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two axis placements.
bool operator()(const gp_Ax2& theAxisPlacement1, const gp_Ax2& theAxisPlacement2) const noexcept
{
const GeomHash_PointHasher aPointHasher;
const GeomHash_DirectionHasher aDirectionHasher;
return aPointHasher(theAxisPlacement1.Location(), theAxisPlacement2.Location())
&& aDirectionHasher(theAxisPlacement1.Direction(), theAxisPlacement2.Direction())
&& aDirectionHasher(theAxisPlacement1.XDirection(), theAxisPlacement2.XDirection());
}
};
#endif // _GeomHash_AxisPlacement_HeaderFile

View File

@@ -0,0 +1,98 @@
// 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 _GeomHash_BSplineCurveHasher_HeaderFile
#define _GeomHash_BSplineCurveHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <Geom_BSplineCurve.hxx>
#include <GeomHash_PointHasher.pxx>
#include <cmath>
//! OCCT-style hasher for Geom_BSplineCurve (3D B-spline curve).
//! Used for geometry deduplication.
//! Hashes only metadata (degree, pole count, knot count, rationality) for efficiency.
struct GeomHash_BSplineCurveHasher
{
// Hashes the B-spline curve metadata only.
std::size_t operator()(const Handle(Geom_BSplineCurve)& theCurve) const noexcept
{
const std::size_t aHashes[4] = {opencascade::hash(theCurve->Degree()),
opencascade::hash(theCurve->NbPoles()),
opencascade::hash(theCurve->NbKnots()),
opencascade::hash(static_cast<int>(theCurve->IsRational()))};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two B-spline curves by full geometric data.
bool operator()(const Handle(Geom_BSplineCurve)& theCurve1,
const Handle(Geom_BSplineCurve)& theCurve2) const noexcept
{
constexpr double aTolerance = 1e-12;
// Compare degrees
if (theCurve1->Degree() != theCurve2->Degree())
{
return false;
}
// Compare knot counts
if (theCurve1->NbKnots() != theCurve2->NbKnots())
{
return false;
}
// Compare knots and multiplicities
for (int i = 1; i <= theCurve1->NbKnots(); ++i)
{
if (std::abs(theCurve1->Knot(i) - theCurve2->Knot(i)) > aTolerance
|| theCurve1->Multiplicity(i) != theCurve2->Multiplicity(i))
{
return false;
}
}
// Compare rationality
if (theCurve1->IsRational() != theCurve2->IsRational())
{
return false;
}
const GeomHash_PointHasher aPointHasher;
// Compare poles
for (int i = 1; i <= theCurve1->NbPoles(); ++i)
{
if (!aPointHasher(theCurve1->Pole(i), theCurve2->Pole(i)))
{
return false;
}
}
// Compare weights if rational
if (theCurve1->IsRational())
{
for (int i = 1; i <= theCurve1->NbPoles(); ++i)
{
if (std::abs(theCurve1->Weight(i) - theCurve2->Weight(i)) > aTolerance)
{
return false;
}
}
}
return true;
}
};
#endif // _GeomHash_BSplineCurveHasher_HeaderFile

View File

@@ -0,0 +1,122 @@
// 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 _GeomHash_BSplineSurfaceHasher_HeaderFile
#define _GeomHash_BSplineSurfaceHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <Geom_BSplineSurface.hxx>
#include <GeomHash_PointHasher.pxx>
#include <cmath>
//! OCCT-style hasher for Geom_BSplineSurface.
//! Used for geometry deduplication.
//! Hashes only metadata (degrees, pole counts, knot counts, rationality) for efficiency.
struct GeomHash_BSplineSurfaceHasher
{
// Hashes the B-spline surface metadata only.
std::size_t operator()(const Handle(Geom_BSplineSurface)& theSurface) const noexcept
{
const std::size_t aHashes[7] = {
opencascade::hash(theSurface->UDegree()),
opencascade::hash(theSurface->VDegree()),
opencascade::hash(theSurface->NbUPoles()),
opencascade::hash(theSurface->NbVPoles()),
opencascade::hash(theSurface->NbUKnots()),
opencascade::hash(theSurface->NbVKnots()),
opencascade::hash(static_cast<int>(theSurface->IsURational())
| (static_cast<int>(theSurface->IsVRational()) << 1))};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two B-spline surfaces by full geometric data.
bool operator()(const Handle(Geom_BSplineSurface)& theSurface1,
const Handle(Geom_BSplineSurface)& theSurface2) const noexcept
{
constexpr double aTolerance = 1e-12;
// Compare degrees
if (theSurface1->UDegree() != theSurface2->UDegree()
|| theSurface1->VDegree() != theSurface2->VDegree())
{
return false;
}
// Compare knot counts
if (theSurface1->NbUKnots() != theSurface2->NbUKnots()
|| theSurface1->NbVKnots() != theSurface2->NbVKnots())
{
return false;
}
// Compare U knots and multiplicities
for (int i = 1; i <= theSurface1->NbUKnots(); ++i)
{
if (std::abs(theSurface1->UKnot(i) - theSurface2->UKnot(i)) > aTolerance
|| theSurface1->UMultiplicity(i) != theSurface2->UMultiplicity(i))
{
return false;
}
}
// Compare V knots and multiplicities
for (int i = 1; i <= theSurface1->NbVKnots(); ++i)
{
if (std::abs(theSurface1->VKnot(i) - theSurface2->VKnot(i)) > aTolerance
|| theSurface1->VMultiplicity(i) != theSurface2->VMultiplicity(i))
{
return false;
}
}
// Compare rationality
if (theSurface1->IsURational() != theSurface2->IsURational()
|| theSurface1->IsVRational() != theSurface2->IsVRational())
{
return false;
}
const GeomHash_PointHasher aPointHasher;
// Compare poles
for (int i = 1; i <= theSurface1->NbUPoles(); ++i)
{
for (int j = 1; j <= theSurface1->NbVPoles(); ++j)
{
if (!aPointHasher(theSurface1->Pole(i, j), theSurface2->Pole(i, j)))
{
return false;
}
}
}
// Compare weights if rational
if (theSurface1->IsURational() || theSurface1->IsVRational())
{
for (int i = 1; i <= theSurface1->NbUPoles(); ++i)
{
for (int j = 1; j <= theSurface1->NbVPoles(); ++j)
{
if (std::abs(theSurface1->Weight(i, j) - theSurface2->Weight(i, j)) > aTolerance)
{
return false;
}
}
}
}
return true;
}
};
#endif // _GeomHash_BSplineSurfaceHasher_HeaderFile

View File

@@ -0,0 +1,81 @@
// 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 _GeomHash_BezierCurveHasher_HeaderFile
#define _GeomHash_BezierCurveHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <Geom_BezierCurve.hxx>
#include <GeomHash_PointHasher.pxx>
#include <cmath>
//! OCCT-style hasher for Geom_BezierCurve (3D Bezier curve).
//! Used for geometry deduplication.
//! Hashes only metadata (degree, pole count, rationality) for efficiency.
struct GeomHash_BezierCurveHasher
{
// Hashes the Bezier curve metadata only.
std::size_t operator()(const Handle(Geom_BezierCurve)& theCurve) const noexcept
{
const std::size_t aHashes[3] = {opencascade::hash(theCurve->Degree()),
opencascade::hash(theCurve->NbPoles()),
opencascade::hash(static_cast<int>(theCurve->IsRational()))};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two Bezier curves by full geometric data.
bool operator()(const Handle(Geom_BezierCurve)& theCurve1,
const Handle(Geom_BezierCurve)& theCurve2) const noexcept
{
constexpr double aTolerance = 1e-12;
// Compare degrees
if (theCurve1->Degree() != theCurve2->Degree())
{
return false;
}
// Compare rationality
if (theCurve1->IsRational() != theCurve2->IsRational())
{
return false;
}
const GeomHash_PointHasher aPointHasher;
// Compare poles
for (int i = 1; i <= theCurve1->NbPoles(); ++i)
{
if (!aPointHasher(theCurve1->Pole(i), theCurve2->Pole(i)))
{
return false;
}
}
// Compare weights if rational
if (theCurve1->IsRational())
{
for (int i = 1; i <= theCurve1->NbPoles(); ++i)
{
if (std::abs(theCurve1->Weight(i) - theCurve2->Weight(i)) > aTolerance)
{
return false;
}
}
}
return true;
}
};
#endif // _GeomHash_BezierCurveHasher_HeaderFile

View File

@@ -0,0 +1,93 @@
// 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 _GeomHash_BezierSurfaceHasher_HeaderFile
#define _GeomHash_BezierSurfaceHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <Geom_BezierSurface.hxx>
#include <GeomHash_PointHasher.pxx>
#include <cmath>
//! OCCT-style hasher for Geom_BezierSurface.
//! Used for geometry deduplication.
//! Hashes only metadata (degrees, pole counts, rationality) for efficiency.
struct GeomHash_BezierSurfaceHasher
{
// Hashes the Bezier surface metadata only.
std::size_t operator()(const Handle(Geom_BezierSurface)& theSurface) const noexcept
{
const std::size_t aHashes[5] = {
opencascade::hash(theSurface->UDegree()),
opencascade::hash(theSurface->VDegree()),
opencascade::hash(theSurface->NbUPoles()),
opencascade::hash(theSurface->NbVPoles()),
opencascade::hash(static_cast<int>(theSurface->IsURational())
| (static_cast<int>(theSurface->IsVRational()) << 1))};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two Bezier surfaces by full geometric data.
bool operator()(const Handle(Geom_BezierSurface)& theSurface1,
const Handle(Geom_BezierSurface)& theSurface2) const noexcept
{
constexpr double aTolerance = 1e-12;
// Compare degrees
if (theSurface1->UDegree() != theSurface2->UDegree()
|| theSurface1->VDegree() != theSurface2->VDegree())
{
return false;
}
// Compare rationality
if (theSurface1->IsURational() != theSurface2->IsURational()
|| theSurface1->IsVRational() != theSurface2->IsVRational())
{
return false;
}
const GeomHash_PointHasher aPointHasher;
// Compare poles
for (int i = 1; i <= theSurface1->NbUPoles(); ++i)
{
for (int j = 1; j <= theSurface1->NbVPoles(); ++j)
{
if (!aPointHasher(theSurface1->Pole(i, j), theSurface2->Pole(i, j)))
{
return false;
}
}
}
// Compare weights if rational
if (theSurface1->IsURational() || theSurface1->IsVRational())
{
for (int i = 1; i <= theSurface1->NbUPoles(); ++i)
{
for (int j = 1; j <= theSurface1->NbVPoles(); ++j)
{
if (std::abs(theSurface1->Weight(i, j) - theSurface2->Weight(i, j)) > aTolerance)
{
return false;
}
}
}
}
return true;
}
};
#endif // _GeomHash_BezierSurfaceHasher_HeaderFile

View File

@@ -0,0 +1,51 @@
// 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 _GeomHash_CircleHasher_HeaderFile
#define _GeomHash_CircleHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <Geom_Circle.hxx>
#include <GeomHash_AxisPlacement.pxx>
#include <cmath>
//! OCCT-style hasher for Geom_Circle (3D circle).
//! Used for geometry deduplication.
struct GeomHash_CircleHasher
{
// Hashes the circle by its position and radius.
std::size_t operator()(const Handle(Geom_Circle)& theCircle) const noexcept
{
constexpr double aTolerance = 1e-12;
constexpr double aFactor = 1.0 / aTolerance;
const GeomHash_AxisPlacement anAxisHasher;
const std::size_t aHashes[2] = {
anAxisHasher(theCircle->Position()),
opencascade::hash(static_cast<int64_t>(std::round(theCircle->Radius() * aFactor)))};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two circles by their positions and radii.
bool operator()(const Handle(Geom_Circle)& theCircle1,
const Handle(Geom_Circle)& theCircle2) const noexcept
{
constexpr double aTolerance = 1e-12;
const GeomHash_AxisPlacement anAxisHasher;
return anAxisHasher(theCircle1->Position(), theCircle2->Position())
&& std::abs(theCircle1->Radius() - theCircle2->Radius()) <= aTolerance;
}
};
#endif // _GeomHash_CircleHasher_HeaderFile

View File

@@ -0,0 +1,52 @@
// 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 _GeomHash_ConicalSurfaceHasher_HeaderFile
#define _GeomHash_ConicalSurfaceHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <Geom_ConicalSurface.hxx>
#include <GeomHash_AxisPlacement.pxx>
#include <cmath>
//! OCCT-style hasher for Geom_ConicalSurface.
//! Used for geometry deduplication.
struct GeomHash_ConicalSurfaceHasher
{
// Hashes the cone by its position, apex radius, and semi-angle.
std::size_t operator()(const Handle(Geom_ConicalSurface)& theCone) const noexcept
{
constexpr double aTolerance = 1e-12;
constexpr double aFactor = 1.0 / aTolerance;
const GeomHash_AxisPlacement anAxisHasher;
const std::size_t aHashes[3] = {
anAxisHasher(theCone->Position().Ax2()),
opencascade::hash(static_cast<int64_t>(std::round(theCone->RefRadius() * aFactor))),
opencascade::hash(static_cast<int64_t>(std::round(theCone->SemiAngle() * aFactor)))};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two cones by their positions, radii, and semi-angles.
bool operator()(const Handle(Geom_ConicalSurface)& theCone1,
const Handle(Geom_ConicalSurface)& theCone2) const noexcept
{
constexpr double aTolerance = 1e-12;
const GeomHash_AxisPlacement anAxisHasher;
return anAxisHasher(theCone1->Position().Ax2(), theCone2->Position().Ax2())
&& std::abs(theCone1->RefRadius() - theCone2->RefRadius()) <= aTolerance
&& std::abs(theCone1->SemiAngle() - theCone2->SemiAngle()) <= aTolerance;
}
};
#endif // _GeomHash_ConicalSurfaceHasher_HeaderFile

View File

@@ -0,0 +1,150 @@
// 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 <GeomHash_CurveHasher.hxx>
#include <Standard_HashUtils.hxx>
#include <Geom_Curve.hxx>
#include <Geom_Line.hxx>
#include <Geom_Circle.hxx>
#include <Geom_Ellipse.hxx>
#include <Geom_Hyperbola.hxx>
#include <Geom_Parabola.hxx>
#include <Geom_BezierCurve.hxx>
#include <Geom_BSplineCurve.hxx>
#include <Geom_TrimmedCurve.hxx>
#include <Geom_OffsetCurve.hxx>
#include <GeomHash_LineHasher.pxx>
#include <GeomHash_CircleHasher.pxx>
#include <GeomHash_EllipseHasher.pxx>
#include <GeomHash_HyperbolaHasher.pxx>
#include <GeomHash_ParabolaHasher.pxx>
#include <GeomHash_BezierCurveHasher.pxx>
#include <GeomHash_BSplineCurveHasher.pxx>
#include <GeomHash_TrimmedCurveHasher.pxx>
#include <GeomHash_OffsetCurveHasher.pxx>
//=================================================================================================
std::size_t GeomHash_CurveHasher::operator()(const Handle(Geom_Curve)& theCurve) const noexcept
{
if (theCurve.IsNull())
{
return 0;
}
// Dispatch based on actual curve type
if (Handle(Geom_Line) aLine = Handle(Geom_Line)::DownCast(theCurve))
{
return GeomHash_LineHasher{}(aLine);
}
if (Handle(Geom_Circle) aCircle = Handle(Geom_Circle)::DownCast(theCurve))
{
return GeomHash_CircleHasher{}(aCircle);
}
if (Handle(Geom_Ellipse) anEllipse = Handle(Geom_Ellipse)::DownCast(theCurve))
{
return GeomHash_EllipseHasher{}(anEllipse);
}
if (Handle(Geom_Hyperbola) aHyperbola = Handle(Geom_Hyperbola)::DownCast(theCurve))
{
return GeomHash_HyperbolaHasher{}(aHyperbola);
}
if (Handle(Geom_Parabola) aParabola = Handle(Geom_Parabola)::DownCast(theCurve))
{
return GeomHash_ParabolaHasher{}(aParabola);
}
if (Handle(Geom_BezierCurve) aBezier = Handle(Geom_BezierCurve)::DownCast(theCurve))
{
return GeomHash_BezierCurveHasher{}(aBezier);
}
if (Handle(Geom_BSplineCurve) aBSpline = Handle(Geom_BSplineCurve)::DownCast(theCurve))
{
return GeomHash_BSplineCurveHasher{}(aBSpline);
}
if (Handle(Geom_TrimmedCurve) aTrimmed = Handle(Geom_TrimmedCurve)::DownCast(theCurve))
{
return GeomHash_TrimmedCurveHasher{}(aTrimmed);
}
if (Handle(Geom_OffsetCurve) anOffset = Handle(Geom_OffsetCurve)::DownCast(theCurve))
{
return GeomHash_OffsetCurveHasher{}(anOffset);
}
// Unknown curve type - hash the type name
return std::hash<std::string>{}(theCurve->DynamicType()->Name());
}
//=================================================================================================
bool GeomHash_CurveHasher::operator()(const Handle(Geom_Curve)& theCurve1,
const Handle(Geom_Curve)& theCurve2) const noexcept
{
if (theCurve1.IsNull() || theCurve2.IsNull())
{
return theCurve1.IsNull() && theCurve2.IsNull();
}
if (theCurve1 == theCurve2)
{
return true;
}
// Must be same type
if (theCurve1->DynamicType() != theCurve2->DynamicType())
{
return false;
}
// Dispatch based on actual curve type
if (Handle(Geom_Line) aLine1 = Handle(Geom_Line)::DownCast(theCurve1))
{
return GeomHash_LineHasher{}(aLine1, Handle(Geom_Line)::DownCast(theCurve2));
}
if (Handle(Geom_Circle) aCircle1 = Handle(Geom_Circle)::DownCast(theCurve1))
{
return GeomHash_CircleHasher{}(aCircle1, Handle(Geom_Circle)::DownCast(theCurve2));
}
if (Handle(Geom_Ellipse) anEllipse1 = Handle(Geom_Ellipse)::DownCast(theCurve1))
{
return GeomHash_EllipseHasher{}(anEllipse1, Handle(Geom_Ellipse)::DownCast(theCurve2));
}
if (Handle(Geom_Hyperbola) aHyp1 = Handle(Geom_Hyperbola)::DownCast(theCurve1))
{
return GeomHash_HyperbolaHasher{}(aHyp1, Handle(Geom_Hyperbola)::DownCast(theCurve2));
}
if (Handle(Geom_Parabola) aPar1 = Handle(Geom_Parabola)::DownCast(theCurve1))
{
return GeomHash_ParabolaHasher{}(aPar1, Handle(Geom_Parabola)::DownCast(theCurve2));
}
if (Handle(Geom_BezierCurve) aBez1 = Handle(Geom_BezierCurve)::DownCast(theCurve1))
{
return GeomHash_BezierCurveHasher{}(aBez1, Handle(Geom_BezierCurve)::DownCast(theCurve2));
}
if (Handle(Geom_BSplineCurve) aBSpl1 = Handle(Geom_BSplineCurve)::DownCast(theCurve1))
{
return GeomHash_BSplineCurveHasher{}(aBSpl1, Handle(Geom_BSplineCurve)::DownCast(theCurve2));
}
if (Handle(Geom_TrimmedCurve) aTrim1 = Handle(Geom_TrimmedCurve)::DownCast(theCurve1))
{
return GeomHash_TrimmedCurveHasher{}(aTrim1, Handle(Geom_TrimmedCurve)::DownCast(theCurve2));
}
if (Handle(Geom_OffsetCurve) aOff1 = Handle(Geom_OffsetCurve)::DownCast(theCurve1))
{
return GeomHash_OffsetCurveHasher{}(aOff1, Handle(Geom_OffsetCurve)::DownCast(theCurve2));
}
// Unknown curve type - compare by pointer
return theCurve1.get() == theCurve2.get();
}

View File

@@ -0,0 +1,34 @@
// 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 _GeomHash_CurveHasher_HeaderFile
#define _GeomHash_CurveHasher_HeaderFile
#include <Standard_Handle.hxx>
#include <cstddef>
class Geom_Curve;
//! Polymorphic hasher for Geom_Curve using RTTI dispatch.
//! Used for geometry deduplication.
struct GeomHash_CurveHasher
{
// Hashes any Geom_Curve by dispatching to the appropriate specific hasher.
Standard_EXPORT std::size_t operator()(const Handle(Geom_Curve)& theCurve) const noexcept;
// Compares two curves using polymorphic dispatch.
Standard_EXPORT bool operator()(const Handle(Geom_Curve)& theCurve1,
const Handle(Geom_Curve)& theCurve2) const noexcept;
};
#endif // _GeomHash_CurveHasher_HeaderFile

View File

@@ -0,0 +1,50 @@
// 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 _GeomHash_CylindricalSurfaceHasher_HeaderFile
#define _GeomHash_CylindricalSurfaceHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <Geom_CylindricalSurface.hxx>
#include <GeomHash_AxisPlacement.pxx>
#include <cmath>
//! OCCT-style hasher for Geom_CylindricalSurface.
//! Used for geometry deduplication.
struct GeomHash_CylindricalSurfaceHasher
{
// Hashes the cylinder by its position and radius.
std::size_t operator()(const Handle(Geom_CylindricalSurface)& theCylinder) const noexcept
{
constexpr double aTolerance = 1e-12;
constexpr double aFactor = 1.0 / aTolerance;
const GeomHash_AxisPlacement anAxisHasher;
const std::size_t aHashes[2] = {
anAxisHasher(theCylinder->Position().Ax2()),
opencascade::hash(static_cast<int64_t>(std::round(theCylinder->Radius() * aFactor)))};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two cylinders by their positions and radii.
bool operator()(const Handle(Geom_CylindricalSurface)& theCylinder1,
const Handle(Geom_CylindricalSurface)& theCylinder2) const noexcept
{
constexpr double aTolerance = 1e-12;
const GeomHash_AxisPlacement anAxisHasher;
return anAxisHasher(theCylinder1->Position().Ax2(), theCylinder2->Position().Ax2())
&& std::abs(theCylinder1->Radius() - theCylinder2->Radius()) <= aTolerance;
}
};
#endif // _GeomHash_CylindricalSurfaceHasher_HeaderFile

View File

@@ -0,0 +1,49 @@
// 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 _GeomHash_DirectionHasher_HeaderFile
#define _GeomHash_DirectionHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <gp_Dir.hxx>
#include <cmath>
//! OCCT-style hasher for gp_Dir (3D directions).
//! Used for geometry deduplication.
struct GeomHash_DirectionHasher
{
// Hashes the 3D direction by its XYZ components.
std::size_t operator()(const gp_Dir& theDirection) const noexcept
{
constexpr double aTolerance = 1e-12;
constexpr double aFactor = 1.0 / aTolerance;
// Round each component to tolerance precision before hashing
const std::size_t aHashes[3] = {
opencascade::hash(static_cast<int64_t>(std::round(theDirection.X() * aFactor))),
opencascade::hash(static_cast<int64_t>(std::round(theDirection.Y() * aFactor))),
opencascade::hash(static_cast<int64_t>(std::round(theDirection.Z() * aFactor)))};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two 3D directions with fixed tolerance.
bool operator()(const gp_Dir& theDirection1, const gp_Dir& theDirection2) const noexcept
{
constexpr double aTolerance = 1e-12;
return std::abs(theDirection1.X() - theDirection2.X()) <= aTolerance
&& std::abs(theDirection1.Y() - theDirection2.Y()) <= aTolerance
&& std::abs(theDirection1.Z() - theDirection2.Z()) <= aTolerance;
}
};
#endif // _GeomHash_DirectionHasher_HeaderFile

View File

@@ -0,0 +1,53 @@
// 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 _GeomHash_EllipseHasher_HeaderFile
#define _GeomHash_EllipseHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <Geom_Ellipse.hxx>
#include <GeomHash_AxisPlacement.pxx>
#include <cmath>
//! OCCT-style hasher for Geom_Ellipse (3D ellipse).
//! Used for geometry deduplication.
struct GeomHash_EllipseHasher
{
// Hashes the ellipse by its position, major radius, and minor radius.
std::size_t operator()(const Handle(Geom_Ellipse)& theEllipse) const noexcept
{
constexpr double aTolerance = 1e-12;
constexpr double aFactor = 1.0 / aTolerance;
const GeomHash_AxisPlacement anAxisHasher;
const std::size_t aHashes[3] = {
anAxisHasher(theEllipse->Position()),
opencascade::hash(static_cast<int64_t>(std::round(theEllipse->MajorRadius() * aFactor))),
opencascade::hash(static_cast<int64_t>(std::round(theEllipse->MinorRadius() * aFactor)))};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two ellipses by their positions and radii.
bool operator()(const Handle(Geom_Ellipse)& theEllipse1,
const Handle(Geom_Ellipse)& theEllipse2) const noexcept
{
constexpr double aTolerance = 1e-12;
const GeomHash_AxisPlacement anAxisHasher;
return anAxisHasher(theEllipse1->Position(), theEllipse2->Position())
&& std::abs(theEllipse1->MajorRadius() - theEllipse2->MajorRadius()) <= aTolerance
&& std::abs(theEllipse1->MinorRadius() - theEllipse2->MinorRadius()) <= aTolerance;
}
};
#endif // _GeomHash_EllipseHasher_HeaderFile

View File

@@ -0,0 +1,53 @@
// 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 _GeomHash_HyperbolaHasher_HeaderFile
#define _GeomHash_HyperbolaHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <Geom_Hyperbola.hxx>
#include <GeomHash_AxisPlacement.pxx>
#include <cmath>
//! OCCT-style hasher for Geom_Hyperbola (3D hyperbola).
//! Used for geometry deduplication.
struct GeomHash_HyperbolaHasher
{
// Hashes the hyperbola by its position, major radius, and minor radius.
std::size_t operator()(const Handle(Geom_Hyperbola)& theHyperbola) const noexcept
{
constexpr double aTolerance = 1e-12;
constexpr double aFactor = 1.0 / aTolerance;
const GeomHash_AxisPlacement anAxisHasher;
const std::size_t aHashes[3] = {
anAxisHasher(theHyperbola->Position()),
opencascade::hash(static_cast<int64_t>(std::round(theHyperbola->MajorRadius() * aFactor))),
opencascade::hash(static_cast<int64_t>(std::round(theHyperbola->MinorRadius() * aFactor)))};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two hyperbolas by their positions and radii.
bool operator()(const Handle(Geom_Hyperbola)& theHyperbola1,
const Handle(Geom_Hyperbola)& theHyperbola2) const noexcept
{
constexpr double aTolerance = 1e-12;
const GeomHash_AxisPlacement anAxisHasher;
return anAxisHasher(theHyperbola1->Position(), theHyperbola2->Position())
&& std::abs(theHyperbola1->MajorRadius() - theHyperbola2->MajorRadius()) <= aTolerance
&& std::abs(theHyperbola1->MinorRadius() - theHyperbola2->MinorRadius()) <= aTolerance;
}
};
#endif // _GeomHash_HyperbolaHasher_HeaderFile

View File

@@ -0,0 +1,49 @@
// 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 _GeomHash_LineHasher_HeaderFile
#define _GeomHash_LineHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <Geom_Line.hxx>
#include <GeomHash_PointHasher.pxx>
#include <GeomHash_DirectionHasher.pxx>
//! OCCT-style hasher for Geom_Line (3D line).
//! Used for geometry deduplication.
struct GeomHash_LineHasher
{
// Hashes the line by its location and direction.
std::size_t operator()(const Handle(Geom_Line)& theLine) const noexcept
{
const GeomHash_PointHasher aPointHasher;
const GeomHash_DirectionHasher aDirHasher;
const std::size_t aHashes[2] = {aPointHasher(theLine->Position().Location()),
aDirHasher(theLine->Position().Direction())};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two lines by their positions.
bool operator()(const Handle(Geom_Line)& theLine1,
const Handle(Geom_Line)& theLine2) const noexcept
{
const GeomHash_PointHasher aPointHasher;
const GeomHash_DirectionHasher aDirHasher;
return aPointHasher(theLine1->Position().Location(), theLine2->Position().Location())
&& aDirHasher(theLine1->Position().Direction(), theLine2->Position().Direction());
}
};
#endif // _GeomHash_LineHasher_HeaderFile

View File

@@ -0,0 +1,57 @@
// 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 _GeomHash_OffsetCurveHasher_HeaderFile
#define _GeomHash_OffsetCurveHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <Geom_OffsetCurve.hxx>
#include <GeomHash_DirectionHasher.pxx>
#include <GeomHash_CurveHasher.hxx>
#include <cmath>
//! OCCT-style hasher for Geom_OffsetCurve (3D offset curve).
//! Used for geometry deduplication.
struct GeomHash_OffsetCurveHasher
{
// Hashes the offset curve by its offset distance, direction, and basis curve.
std::size_t operator()(const Handle(Geom_OffsetCurve)& theCurve) const noexcept
{
constexpr double aTolerance = 1e-12;
constexpr double aFactor = 1.0 / aTolerance;
const GeomHash_DirectionHasher aDirHasher;
const GeomHash_CurveHasher aCurveHasher;
const std::size_t aHashes[3] = {
aCurveHasher(theCurve->BasisCurve()),
opencascade::hash(static_cast<int64_t>(std::round(theCurve->Offset() * aFactor))),
aDirHasher(theCurve->Direction())};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two offset curves.
bool operator()(const Handle(Geom_OffsetCurve)& theCurve1,
const Handle(Geom_OffsetCurve)& theCurve2) const noexcept
{
constexpr double aTolerance = 1e-12;
const GeomHash_DirectionHasher aDirHasher;
const GeomHash_CurveHasher aCurveHasher;
return aCurveHasher(theCurve1->BasisCurve(), theCurve2->BasisCurve())
&& std::abs(theCurve1->Offset() - theCurve2->Offset()) <= aTolerance
&& aDirHasher(theCurve1->Direction(), theCurve2->Direction());
}
};
#endif // _GeomHash_OffsetCurveHasher_HeaderFile

View File

@@ -0,0 +1,52 @@
// 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 _GeomHash_OffsetSurfaceHasher_HeaderFile
#define _GeomHash_OffsetSurfaceHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <Geom_OffsetSurface.hxx>
#include <GeomHash_SurfaceHasher.hxx>
#include <cmath>
//! OCCT-style hasher for Geom_OffsetSurface.
//! Used for geometry deduplication.
struct GeomHash_OffsetSurfaceHasher
{
// Hashes the offset surface by its offset distance and basis surface.
std::size_t operator()(const Handle(Geom_OffsetSurface)& theSurface) const noexcept
{
constexpr double aTolerance = 1e-12;
constexpr double aFactor = 1.0 / aTolerance;
const GeomHash_SurfaceHasher aSurfaceHasher;
const std::size_t aHashes[2] = {
aSurfaceHasher(theSurface->BasisSurface()),
opencascade::hash(static_cast<int64_t>(std::round(theSurface->Offset() * aFactor)))};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two offset surfaces by their offset distances and basis surfaces.
bool operator()(const Handle(Geom_OffsetSurface)& theSurface1,
const Handle(Geom_OffsetSurface)& theSurface2) const noexcept
{
constexpr double aTolerance = 1e-12;
const GeomHash_SurfaceHasher aSurfaceHasher;
return aSurfaceHasher(theSurface1->BasisSurface(), theSurface2->BasisSurface())
&& std::abs(theSurface1->Offset() - theSurface2->Offset()) <= aTolerance;
}
};
#endif // _GeomHash_OffsetSurfaceHasher_HeaderFile

View File

@@ -0,0 +1,51 @@
// 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 _GeomHash_ParabolaHasher_HeaderFile
#define _GeomHash_ParabolaHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <Geom_Parabola.hxx>
#include <GeomHash_AxisPlacement.pxx>
#include <cmath>
//! OCCT-style hasher for Geom_Parabola (3D parabola).
//! Used for geometry deduplication.
struct GeomHash_ParabolaHasher
{
// Hashes the parabola by its position and focal length.
std::size_t operator()(const Handle(Geom_Parabola)& theParabola) const noexcept
{
constexpr double aTolerance = 1e-12;
constexpr double aFactor = 1.0 / aTolerance;
const GeomHash_AxisPlacement anAxisHasher;
const std::size_t aHashes[2] = {
anAxisHasher(theParabola->Position()),
opencascade::hash(static_cast<int64_t>(std::round(theParabola->Focal() * aFactor)))};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two parabolas by their positions and focal lengths.
bool operator()(const Handle(Geom_Parabola)& theParabola1,
const Handle(Geom_Parabola)& theParabola2) const noexcept
{
constexpr double aTolerance = 1e-12;
const GeomHash_AxisPlacement anAxisHasher;
return anAxisHasher(theParabola1->Position(), theParabola2->Position())
&& std::abs(theParabola1->Focal() - theParabola2->Focal()) <= aTolerance;
}
};
#endif // _GeomHash_ParabolaHasher_HeaderFile

View File

@@ -0,0 +1,41 @@
// 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 _GeomHash_PlaneHasher_HeaderFile
#define _GeomHash_PlaneHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <Geom_Plane.hxx>
#include <GeomHash_AxisPlacement.pxx>
//! OCCT-style hasher for Geom_Plane surfaces.
//! Used for geometry deduplication.
struct GeomHash_PlaneHasher
{
// Hashes the plane by its position (location, normal, reference direction).
std::size_t operator()(const Handle(Geom_Plane)& thePlane) const noexcept
{
const GeomHash_AxisPlacement anAxisHasher;
return anAxisHasher(thePlane->Position().Ax2());
}
// Compares two planes by their positions.
bool operator()(const Handle(Geom_Plane)& thePlane1,
const Handle(Geom_Plane)& thePlane2) const noexcept
{
const GeomHash_AxisPlacement anAxisHasher;
return anAxisHasher(thePlane1->Position().Ax2(), thePlane2->Position().Ax2());
}
};
#endif // _GeomHash_PlaneHasher_HeaderFile

View File

@@ -0,0 +1,49 @@
// 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 _GeomHash_PointHasher_HeaderFile
#define _GeomHash_PointHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <gp_Pnt.hxx>
#include <cmath>
//! OCCT-style hasher for gp_Pnt (3D points).
//! Used for geometry deduplication.
struct GeomHash_PointHasher
{
// Hashes the 3D point by its XYZ coordinates.
std::size_t operator()(const gp_Pnt& thePoint) const noexcept
{
constexpr double aTolerance = 1e-12;
constexpr double aFactor = 1.0 / aTolerance;
// Round each coordinate to tolerance precision before hashing
const std::size_t aHashes[3] = {
opencascade::hash(static_cast<int64_t>(std::round(thePoint.X() * aFactor))),
opencascade::hash(static_cast<int64_t>(std::round(thePoint.Y() * aFactor))),
opencascade::hash(static_cast<int64_t>(std::round(thePoint.Z() * aFactor)))};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two 3D points with fixed tolerance.
bool operator()(const gp_Pnt& thePoint1, const gp_Pnt& thePoint2) const noexcept
{
constexpr double aTolerance = 1e-12;
return std::abs(thePoint1.X() - thePoint2.X()) <= aTolerance
&& std::abs(thePoint1.Y() - thePoint2.Y()) <= aTolerance
&& std::abs(thePoint1.Z() - thePoint2.Z()) <= aTolerance;
}
};
#endif // _GeomHash_PointHasher_HeaderFile

View File

@@ -0,0 +1,71 @@
// 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 _GeomHash_RectangularTrimmedSurfaceHasher_HeaderFile
#define _GeomHash_RectangularTrimmedSurfaceHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <Geom_RectangularTrimmedSurface.hxx>
#include <GeomHash_SurfaceHasher.hxx>
#include <cmath>
//! OCCT-style hasher for Geom_RectangularTrimmedSurface.
//! Used for geometry deduplication.
struct GeomHash_RectangularTrimmedSurfaceHasher
{
// Hashes the trimmed surface by its trim bounds and basis surface.
std::size_t operator()(const Handle(Geom_RectangularTrimmedSurface)& theSurface) const noexcept
{
constexpr double aTolerance = 1e-12;
constexpr double aFactor = 1.0 / aTolerance;
const GeomHash_SurfaceHasher aSurfaceHasher;
double aU1, aU2, aV1, aV2;
theSurface->Bounds(aU1, aU2, aV1, aV2);
const std::size_t aHashes[5] = {
aSurfaceHasher(theSurface->BasisSurface()),
opencascade::hash(static_cast<int64_t>(std::round(aU1 * aFactor))),
opencascade::hash(static_cast<int64_t>(std::round(aU2 * aFactor))),
opencascade::hash(static_cast<int64_t>(std::round(aV1 * aFactor))),
opencascade::hash(static_cast<int64_t>(std::round(aV2 * aFactor)))};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two trimmed surfaces by their trim bounds and basis surfaces.
bool operator()(const Handle(Geom_RectangularTrimmedSurface)& theSurface1,
const Handle(Geom_RectangularTrimmedSurface)& theSurface2) const noexcept
{
constexpr double aTolerance = 1e-12;
const GeomHash_SurfaceHasher aSurfaceHasher;
// Compare basis surfaces
if (!aSurfaceHasher(theSurface1->BasisSurface(), theSurface2->BasisSurface()))
{
return false;
}
// Compare trim bounds
double aU1_1, aU2_1, aV1_1, aV2_1;
double aU1_2, aU2_2, aV1_2, aV2_2;
theSurface1->Bounds(aU1_1, aU2_1, aV1_1, aV2_1);
theSurface2->Bounds(aU1_2, aU2_2, aV1_2, aV2_2);
return std::abs(aU1_1 - aU1_2) <= aTolerance && std::abs(aU2_1 - aU2_2) <= aTolerance
&& std::abs(aV1_1 - aV1_2) <= aTolerance && std::abs(aV2_1 - aV2_2) <= aTolerance;
}
};
#endif // _GeomHash_RectangularTrimmedSurfaceHasher_HeaderFile

View File

@@ -0,0 +1,50 @@
// 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 _GeomHash_SphericalSurfaceHasher_HeaderFile
#define _GeomHash_SphericalSurfaceHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <Geom_SphericalSurface.hxx>
#include <GeomHash_AxisPlacement.pxx>
#include <cmath>
//! OCCT-style hasher for Geom_SphericalSurface.
//! Used for geometry deduplication.
struct GeomHash_SphericalSurfaceHasher
{
// Hashes the sphere by its position and radius.
std::size_t operator()(const Handle(Geom_SphericalSurface)& theSphere) const noexcept
{
constexpr double aTolerance = 1e-12;
constexpr double aFactor = 1.0 / aTolerance;
const GeomHash_AxisPlacement anAxisHasher;
const std::size_t aHashes[2] = {
anAxisHasher(theSphere->Position().Ax2()),
opencascade::hash(static_cast<int64_t>(std::round(theSphere->Radius() * aFactor)))};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two spheres by their positions and radii.
bool operator()(const Handle(Geom_SphericalSurface)& theSphere1,
const Handle(Geom_SphericalSurface)& theSphere2) const noexcept
{
constexpr double aTolerance = 1e-12;
const GeomHash_AxisPlacement anAxisHasher;
return anAxisHasher(theSphere1->Position().Ax2(), theSphere2->Position().Ax2())
&& std::abs(theSphere1->Radius() - theSphere2->Radius()) <= aTolerance;
}
};
#endif // _GeomHash_SphericalSurfaceHasher_HeaderFile

View File

@@ -0,0 +1,191 @@
// 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 <GeomHash_SurfaceHasher.hxx>
#include <Standard_HashUtils.hxx>
#include <Geom_Surface.hxx>
#include <Geom_Plane.hxx>
#include <Geom_CylindricalSurface.hxx>
#include <Geom_ConicalSurface.hxx>
#include <Geom_SphericalSurface.hxx>
#include <Geom_ToroidalSurface.hxx>
#include <Geom_SurfaceOfRevolution.hxx>
#include <Geom_SurfaceOfLinearExtrusion.hxx>
#include <Geom_BezierSurface.hxx>
#include <Geom_BSplineSurface.hxx>
#include <Geom_RectangularTrimmedSurface.hxx>
#include <Geom_OffsetSurface.hxx>
#include <GeomHash_PlaneHasher.pxx>
#include <GeomHash_CylindricalSurfaceHasher.pxx>
#include <GeomHash_ConicalSurfaceHasher.pxx>
#include <GeomHash_SphericalSurfaceHasher.pxx>
#include <GeomHash_ToroidalSurfaceHasher.pxx>
#include <GeomHash_SurfaceOfRevolutionHasher.pxx>
#include <GeomHash_SurfaceOfLinearExtrusionHasher.pxx>
#include <GeomHash_BezierSurfaceHasher.pxx>
#include <GeomHash_BSplineSurfaceHasher.pxx>
#include <GeomHash_RectangularTrimmedSurfaceHasher.pxx>
#include <GeomHash_OffsetSurfaceHasher.pxx>
//=================================================================================================
std::size_t GeomHash_SurfaceHasher::operator()(
const Handle(Geom_Surface)& theSurface) const noexcept
{
if (theSurface.IsNull())
{
return 0;
}
// Dispatch based on actual surface type
if (Handle(Geom_Plane) aPlane = Handle(Geom_Plane)::DownCast(theSurface))
{
return GeomHash_PlaneHasher{}(aPlane);
}
if (Handle(Geom_CylindricalSurface) aCylinder =
Handle(Geom_CylindricalSurface)::DownCast(theSurface))
{
return GeomHash_CylindricalSurfaceHasher{}(aCylinder);
}
if (Handle(Geom_ConicalSurface) aCone = Handle(Geom_ConicalSurface)::DownCast(theSurface))
{
return GeomHash_ConicalSurfaceHasher{}(aCone);
}
if (Handle(Geom_SphericalSurface) aSphere = Handle(Geom_SphericalSurface)::DownCast(theSurface))
{
return GeomHash_SphericalSurfaceHasher{}(aSphere);
}
if (Handle(Geom_ToroidalSurface) aTorus = Handle(Geom_ToroidalSurface)::DownCast(theSurface))
{
return GeomHash_ToroidalSurfaceHasher{}(aTorus);
}
if (Handle(Geom_SurfaceOfRevolution) aRevol =
Handle(Geom_SurfaceOfRevolution)::DownCast(theSurface))
{
return GeomHash_SurfaceOfRevolutionHasher{}(aRevol);
}
if (Handle(Geom_SurfaceOfLinearExtrusion) aExtr =
Handle(Geom_SurfaceOfLinearExtrusion)::DownCast(theSurface))
{
return GeomHash_SurfaceOfLinearExtrusionHasher{}(aExtr);
}
if (Handle(Geom_BezierSurface) aBezier = Handle(Geom_BezierSurface)::DownCast(theSurface))
{
return GeomHash_BezierSurfaceHasher{}(aBezier);
}
if (Handle(Geom_BSplineSurface) aBSpline = Handle(Geom_BSplineSurface)::DownCast(theSurface))
{
return GeomHash_BSplineSurfaceHasher{}(aBSpline);
}
if (Handle(Geom_RectangularTrimmedSurface) aTrimmed =
Handle(Geom_RectangularTrimmedSurface)::DownCast(theSurface))
{
return GeomHash_RectangularTrimmedSurfaceHasher{}(aTrimmed);
}
if (Handle(Geom_OffsetSurface) aOffset = Handle(Geom_OffsetSurface)::DownCast(theSurface))
{
return GeomHash_OffsetSurfaceHasher{}(aOffset);
}
// Unknown surface type - hash the type name
return std::hash<std::string>{}(theSurface->DynamicType()->Name());
}
//=================================================================================================
bool GeomHash_SurfaceHasher::operator()(const Handle(Geom_Surface)& theSurface1,
const Handle(Geom_Surface)& theSurface2) const noexcept
{
if (theSurface1.IsNull() || theSurface2.IsNull())
{
return theSurface1.IsNull() && theSurface2.IsNull();
}
if (theSurface1 == theSurface2)
{
return true;
}
// Must be same type
if (theSurface1->DynamicType() != theSurface2->DynamicType())
{
return false;
}
// Dispatch based on actual surface type
if (Handle(Geom_Plane) aPlane1 = Handle(Geom_Plane)::DownCast(theSurface1))
{
return GeomHash_PlaneHasher{}(aPlane1, Handle(Geom_Plane)::DownCast(theSurface2));
}
if (Handle(Geom_CylindricalSurface) aCyl1 =
Handle(Geom_CylindricalSurface)::DownCast(theSurface1))
{
return GeomHash_CylindricalSurfaceHasher{}(
aCyl1,
Handle(Geom_CylindricalSurface)::DownCast(theSurface2));
}
if (Handle(Geom_ConicalSurface) aCone1 = Handle(Geom_ConicalSurface)::DownCast(theSurface1))
{
return GeomHash_ConicalSurfaceHasher{}(aCone1,
Handle(Geom_ConicalSurface)::DownCast(theSurface2));
}
if (Handle(Geom_SphericalSurface) aSph1 = Handle(Geom_SphericalSurface)::DownCast(theSurface1))
{
return GeomHash_SphericalSurfaceHasher{}(aSph1,
Handle(Geom_SphericalSurface)::DownCast(theSurface2));
}
if (Handle(Geom_ToroidalSurface) aTor1 = Handle(Geom_ToroidalSurface)::DownCast(theSurface1))
{
return GeomHash_ToroidalSurfaceHasher{}(aTor1,
Handle(Geom_ToroidalSurface)::DownCast(theSurface2));
}
if (Handle(Geom_SurfaceOfRevolution) aRev1 =
Handle(Geom_SurfaceOfRevolution)::DownCast(theSurface1))
{
return GeomHash_SurfaceOfRevolutionHasher{}(
aRev1,
Handle(Geom_SurfaceOfRevolution)::DownCast(theSurface2));
}
if (Handle(Geom_SurfaceOfLinearExtrusion) aExt1 =
Handle(Geom_SurfaceOfLinearExtrusion)::DownCast(theSurface1))
{
return GeomHash_SurfaceOfLinearExtrusionHasher{}(
aExt1,
Handle(Geom_SurfaceOfLinearExtrusion)::DownCast(theSurface2));
}
if (Handle(Geom_BezierSurface) aBez1 = Handle(Geom_BezierSurface)::DownCast(theSurface1))
{
return GeomHash_BezierSurfaceHasher{}(aBez1, Handle(Geom_BezierSurface)::DownCast(theSurface2));
}
if (Handle(Geom_BSplineSurface) aBSpl1 = Handle(Geom_BSplineSurface)::DownCast(theSurface1))
{
return GeomHash_BSplineSurfaceHasher{}(aBSpl1,
Handle(Geom_BSplineSurface)::DownCast(theSurface2));
}
if (Handle(Geom_RectangularTrimmedSurface) aTrim1 =
Handle(Geom_RectangularTrimmedSurface)::DownCast(theSurface1))
{
return GeomHash_RectangularTrimmedSurfaceHasher{}(
aTrim1,
Handle(Geom_RectangularTrimmedSurface)::DownCast(theSurface2));
}
if (Handle(Geom_OffsetSurface) aOff1 = Handle(Geom_OffsetSurface)::DownCast(theSurface1))
{
return GeomHash_OffsetSurfaceHasher{}(aOff1, Handle(Geom_OffsetSurface)::DownCast(theSurface2));
}
// Unknown surface type - compare by pointer
return theSurface1.get() == theSurface2.get();
}

View File

@@ -0,0 +1,34 @@
// 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 _GeomHash_SurfaceHasher_HeaderFile
#define _GeomHash_SurfaceHasher_HeaderFile
#include <Standard_Handle.hxx>
#include <cstddef>
class Geom_Surface;
//! Polymorphic hasher for Geom_Surface using RTTI dispatch.
//! Used for geometry deduplication.
struct GeomHash_SurfaceHasher
{
// Hashes any Geom_Surface by dispatching to the appropriate specific hasher.
Standard_EXPORT std::size_t operator()(const Handle(Geom_Surface)& theSurface) const noexcept;
// Compares two surfaces using polymorphic dispatch.
Standard_EXPORT bool operator()(const Handle(Geom_Surface)& theSurface1,
const Handle(Geom_Surface)& theSurface2) const noexcept;
};
#endif // _GeomHash_SurfaceHasher_HeaderFile

View File

@@ -0,0 +1,48 @@
// 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 _GeomHash_SurfaceOfLinearExtrusionHasher_HeaderFile
#define _GeomHash_SurfaceOfLinearExtrusionHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <Geom_SurfaceOfLinearExtrusion.hxx>
#include <GeomHash_DirectionHasher.pxx>
#include <GeomHash_CurveHasher.hxx>
//! OCCT-style hasher for Geom_SurfaceOfLinearExtrusion.
//! Used for geometry deduplication.
struct GeomHash_SurfaceOfLinearExtrusionHasher
{
// Hashes the extrusion surface by its direction and basis curve.
std::size_t operator()(const Handle(Geom_SurfaceOfLinearExtrusion)& theSurface) const noexcept
{
const GeomHash_DirectionHasher aDirHasher;
const GeomHash_CurveHasher aCurveHasher;
const std::size_t aHashes[2] = {aCurveHasher(theSurface->BasisCurve()),
aDirHasher(theSurface->Direction())};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two extrusion surfaces.
bool operator()(const Handle(Geom_SurfaceOfLinearExtrusion)& theSurface1,
const Handle(Geom_SurfaceOfLinearExtrusion)& theSurface2) const noexcept
{
const GeomHash_DirectionHasher aDirHasher;
const GeomHash_CurveHasher aCurveHasher;
return aCurveHasher(theSurface1->BasisCurve(), theSurface2->BasisCurve())
&& aDirHasher(theSurface1->Direction(), theSurface2->Direction());
}
};
#endif // _GeomHash_SurfaceOfLinearExtrusionHasher_HeaderFile

View File

@@ -0,0 +1,58 @@
// 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 _GeomHash_SurfaceOfRevolutionHasher_HeaderFile
#define _GeomHash_SurfaceOfRevolutionHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <Geom_SurfaceOfRevolution.hxx>
#include <GeomHash_PointHasher.pxx>
#include <GeomHash_DirectionHasher.pxx>
#include <GeomHash_CurveHasher.hxx>
//! OCCT-style hasher for Geom_SurfaceOfRevolution.
//! Used for geometry deduplication.
struct GeomHash_SurfaceOfRevolutionHasher
{
// Hashes the revolution surface by its axis and basis curve.
std::size_t operator()(const Handle(Geom_SurfaceOfRevolution)& theSurface) const noexcept
{
const GeomHash_PointHasher aPointHasher;
const GeomHash_DirectionHasher aDirHasher;
const GeomHash_CurveHasher aCurveHasher;
const gp_Ax1& anAxis = theSurface->Axis();
const std::size_t aHashes[3] = {aCurveHasher(theSurface->BasisCurve()),
aPointHasher(anAxis.Location()),
aDirHasher(anAxis.Direction())};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two revolution surfaces.
bool operator()(const Handle(Geom_SurfaceOfRevolution)& theSurface1,
const Handle(Geom_SurfaceOfRevolution)& theSurface2) const noexcept
{
const GeomHash_PointHasher aPointHasher;
const GeomHash_DirectionHasher aDirHasher;
const GeomHash_CurveHasher aCurveHasher;
const gp_Ax1& anAxis1 = theSurface1->Axis();
const gp_Ax1& anAxis2 = theSurface2->Axis();
return aCurveHasher(theSurface1->BasisCurve(), theSurface2->BasisCurve())
&& aPointHasher(anAxis1.Location(), anAxis2.Location())
&& aDirHasher(anAxis1.Direction(), anAxis2.Direction());
}
};
#endif // _GeomHash_SurfaceOfRevolutionHasher_HeaderFile

View File

@@ -0,0 +1,52 @@
// 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 _GeomHash_ToroidalSurfaceHasher_HeaderFile
#define _GeomHash_ToroidalSurfaceHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <Geom_ToroidalSurface.hxx>
#include <GeomHash_AxisPlacement.pxx>
#include <cmath>
//! OCCT-style hasher for Geom_ToroidalSurface.
//! Used for geometry deduplication.
struct GeomHash_ToroidalSurfaceHasher
{
// Hashes the torus by its position, major radius, and minor radius.
std::size_t operator()(const Handle(Geom_ToroidalSurface)& theTorus) const noexcept
{
constexpr double aTolerance = 1e-12;
constexpr double aFactor = 1.0 / aTolerance;
const GeomHash_AxisPlacement anAxisHasher;
const std::size_t aHashes[3] = {
anAxisHasher(theTorus->Position().Ax2()),
opencascade::hash(static_cast<int64_t>(std::round(theTorus->MajorRadius() * aFactor))),
opencascade::hash(static_cast<int64_t>(std::round(theTorus->MinorRadius() * aFactor)))};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two tori by their positions, major radii, and minor radii.
bool operator()(const Handle(Geom_ToroidalSurface)& theTorus1,
const Handle(Geom_ToroidalSurface)& theTorus2) const noexcept
{
constexpr double aTolerance = 1e-12;
const GeomHash_AxisPlacement anAxisHasher;
return anAxisHasher(theTorus1->Position().Ax2(), theTorus2->Position().Ax2())
&& std::abs(theTorus1->MajorRadius() - theTorus2->MajorRadius()) <= aTolerance
&& std::abs(theTorus1->MinorRadius() - theTorus2->MinorRadius()) <= aTolerance;
}
};
#endif // _GeomHash_ToroidalSurfaceHasher_HeaderFile

View File

@@ -0,0 +1,54 @@
// 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 _GeomHash_TrimmedCurveHasher_HeaderFile
#define _GeomHash_TrimmedCurveHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <Geom_TrimmedCurve.hxx>
#include <GeomHash_CurveHasher.hxx>
#include <cmath>
//! OCCT-style hasher for Geom_TrimmedCurve (3D trimmed curve).
//! Used for geometry deduplication.
struct GeomHash_TrimmedCurveHasher
{
// Hashes the trimmed curve by its parameters and basis curve.
std::size_t operator()(const Handle(Geom_TrimmedCurve)& theCurve) const noexcept
{
constexpr double aTolerance = 1e-12;
constexpr double aFactor = 1.0 / aTolerance;
const GeomHash_CurveHasher aCurveHasher;
const std::size_t aHashes[3] = {
aCurveHasher(theCurve->BasisCurve()),
opencascade::hash(static_cast<int64_t>(std::round(theCurve->FirstParameter() * aFactor))),
opencascade::hash(static_cast<int64_t>(std::round(theCurve->LastParameter() * aFactor)))};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two trimmed curves.
bool operator()(const Handle(Geom_TrimmedCurve)& theCurve1,
const Handle(Geom_TrimmedCurve)& theCurve2) const noexcept
{
constexpr double aTolerance = 1e-12;
const GeomHash_CurveHasher aCurveHasher;
return aCurveHasher(theCurve1->BasisCurve(), theCurve2->BasisCurve())
&& std::abs(theCurve1->FirstParameter() - theCurve2->FirstParameter()) <= aTolerance
&& std::abs(theCurve1->LastParameter() - theCurve2->LastParameter()) <= aTolerance;
}
};
#endif // _GeomHash_TrimmedCurveHasher_HeaderFile

View File

@@ -0,0 +1,49 @@
// 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 _GeomHash_VectorHasher_HeaderFile
#define _GeomHash_VectorHasher_HeaderFile
#include <Standard_HashUtils.hxx>
#include <gp_Vec.hxx>
#include <cmath>
//! OCCT-style hasher for gp_Vec (3D vectors).
//! Used for geometry deduplication.
struct GeomHash_VectorHasher
{
// Hashes the 3D vector by its XYZ components.
std::size_t operator()(const gp_Vec& theVector) const noexcept
{
constexpr double aTolerance = 1e-12;
constexpr double aFactor = 1.0 / aTolerance;
// Round each component to tolerance precision before hashing
const std::size_t aHashes[3] = {
opencascade::hash(static_cast<int64_t>(std::round(theVector.X() * aFactor))),
opencascade::hash(static_cast<int64_t>(std::round(theVector.Y() * aFactor))),
opencascade::hash(static_cast<int64_t>(std::round(theVector.Z() * aFactor)))};
return opencascade::hashBytes(aHashes, sizeof(aHashes));
}
// Compares two 3D vectors with fixed tolerance.
bool operator()(const gp_Vec& theVector1, const gp_Vec& theVector2) const noexcept
{
constexpr double aTolerance = 1e-12;
return std::abs(theVector1.X() - theVector2.X()) <= aTolerance
&& std::abs(theVector1.Y() - theVector2.Y()) <= aTolerance
&& std::abs(theVector1.Z() - theVector2.Z()) <= aTolerance;
}
};
#endif // _GeomHash_VectorHasher_HeaderFile

View File

@@ -10,4 +10,5 @@ set(OCCT_TKG3d_LIST_OF_PACKAGES
TopAbs
GeomEvaluator
GProp
GeomHash
)