- 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
Geom2dAPI_InterCurveCurve_Test.cxx
Geom2dGcc_Circ2d2TanOn_Test.cxx
Geom2dGcc_Circ2d2TanRad_Test.cxx
+ Geom2dHash_CurveHasher_Test.cxx
)
--- /dev/null
+// 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));
+}
--- /dev/null
+# 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
+)
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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();
+}
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
Geom2dLProp
Geom2dAdaptor
Geom2dEvaluator
+ Geom2dHash
)
Geom_OffsetSurface_Test.cxx
GeomAPI_ExtremaCurveCurve_Test.cxx
GeomAPI_Interpolate_Test.cxx
+ GeomHash_CurveHasher_Test.cxx
+ GeomHash_SurfaceHasher_Test.cxx
)
--- /dev/null
+// 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));
+}
--- /dev/null
+// 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));
+}
--- /dev/null
+# 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
+)
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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();
+}
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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();
+}
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
TopAbs
GeomEvaluator
GProp
+ GeomHash
)