]> OCCT Git - occt.git/commitdiff
Revert "Foundation Classes - TopLoc package update (#849)" IR master
authordpasukhi <dpasukhi@opencascade.com>
Tue, 2 Dec 2025 21:36:52 +0000 (21:36 +0000)
committerdpasukhi <dpasukhi@opencascade.com>
Tue, 2 Dec 2025 21:36:52 +0000 (21:36 +0000)
This reverts commit a6e68c7e70a175605fa0779add068143c7bc8ed2.

14 files changed:
src/FoundationClasses/TKMath/GTests/FILES.cmake
src/FoundationClasses/TKMath/GTests/TopLoc_Location_Test.cxx [deleted file]
src/FoundationClasses/TKMath/TopLoc/FILES.cmake
src/FoundationClasses/TKMath/TopLoc/TopLoc_Datum3D.hxx
src/FoundationClasses/TKMath/TopLoc/TopLoc_ItemLocation.cxx
src/FoundationClasses/TKMath/TopLoc/TopLoc_Location.cxx
src/FoundationClasses/TKMath/TopLoc/TopLoc_Location.hxx
src/FoundationClasses/TKMath/TopLoc/TopLoc_Location.lxx
src/FoundationClasses/TKMath/TopLoc/TopLoc_SListNodeOfItemLocation.hxx
src/FoundationClasses/TKMath/TopLoc/TopLoc_SListNodeOfItemLocation.lxx [new file with mode: 0644]
src/FoundationClasses/TKMath/TopLoc/TopLoc_SListOfItemLocation.cxx
src/FoundationClasses/TKMath/TopLoc/TopLoc_SListOfItemLocation.hxx
src/FoundationClasses/TKernel/GTests/FILES.cmake
src/FoundationClasses/TKernel/GTests/TopLoc_Location_Test.cxx [new file with mode: 0644]

index bfd4fd84f0baf655dd342233424dce811f42e834..1df64acae512d3d7281e68320b1eb82e884c55f0 100644 (file)
@@ -58,5 +58,4 @@ set(OCCT_TKMath_GTests_FILES
   PLib_Test.cxx
   PLib_JacobiPolynomial_Test.cxx
   PLib_HermitJacobi_Test.cxx
-  TopLoc_Location_Test.cxx
 )
diff --git a/src/FoundationClasses/TKMath/GTests/TopLoc_Location_Test.cxx b/src/FoundationClasses/TKMath/GTests/TopLoc_Location_Test.cxx
deleted file mode 100644 (file)
index 8cf4fa9..0000000
+++ /dev/null
@@ -1,722 +0,0 @@
-// 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 <TopLoc_Location.hxx>
-#include <TopoDS_Shape.hxx>
-#include <TopoDS_Vertex.hxx>
-#include <TopoDS.hxx>
-#include <BRepBuilderAPI_MakeVertex.hxx>
-#include <BRep_Tool.hxx>
-#include <gp.hxx>
-#include <gp_Ax1.hxx>
-#include <gp_Trsf.hxx>
-#include <gp_Vec.hxx>
-#include <gp_Pnt.hxx>
-#include <OSD_Parallel.hxx>
-
-#include <gtest/gtest.h>
-
-#include <atomic>
-#include <vector>
-
-namespace
-{
-//! Functor for testing concurrent access to TopLoc_Location::Transformation()
-struct TopLocTransformFunctor
-{
-  TopLocTransformFunctor(const std::vector<TopoDS_Shape>& theShapeVec)
-      : myShapeVec(&theShapeVec),
-        myIsRaceDetected(0)
-  {
-  }
-
-  void operator()(size_t i) const
-  {
-    if (!myIsRaceDetected)
-    {
-      const TopoDS_Vertex& aVertex = TopoDS::Vertex(myShapeVec->at(i));
-      gp_Pnt               aPoint  = BRep_Tool::Pnt(aVertex);
-      if (aPoint.X() != static_cast<double>(i))
-      {
-        ++myIsRaceDetected;
-      }
-    }
-  }
-
-  const std::vector<TopoDS_Shape>* myShapeVec;
-  mutable std::atomic<int>         myIsRaceDetected;
-};
-} // namespace
-
-TEST(TopLoc_Location_Test, OCC25545_ConcurrentTransformationAccess)
-{
-  // Bug OCC25545: TopLoc_Location::Transformation() provokes data races
-  // This test verifies that concurrent access to TopLoc_Location::Transformation()
-  // does not cause data races or incorrect geometry results
-
-  // Place vertices in a vector, giving the i-th vertex the
-  // transformation that translates it on the vector (i,0,0) from the origin
-  Standard_Integer             n = 1000;
-  std::vector<TopoDS_Shape>    aShapeVec(n);
-  std::vector<TopLoc_Location> aLocVec(n);
-  TopoDS_Shape                 aShape = BRepBuilderAPI_MakeVertex(gp::Origin());
-  aShapeVec[0]                        = aShape;
-
-  for (Standard_Integer i = 1; i < n; ++i)
-  {
-    gp_Trsf aTrsf;
-    aTrsf.SetTranslation(gp_Vec(1, 0, 0));
-    aLocVec[i]   = aLocVec[i - 1] * aTrsf;
-    aShapeVec[i] = aShape.Moved(aLocVec[i]);
-  }
-
-  // Evaluator function will access vertices geometry concurrently
-  TopLocTransformFunctor aFunc(aShapeVec);
-
-  // Process concurrently
-  OSD_Parallel::For(0, n, aFunc);
-
-  // Verify no data race was detected
-  EXPECT_EQ(aFunc.myIsRaceDetected, 0)
-    << "Data race detected in concurrent TopLoc_Location::Transformation() access";
-}
-
-//=================================================================================================
-// Tests for Phase 1 & 2 Optimizations
-//=================================================================================================
-
-TEST(TopLoc_Location_Test, DefaultConstructor_CreatesIdentity)
-{
-  // Test that default constructor creates identity location
-  TopLoc_Location aLoc;
-
-  EXPECT_TRUE(aLoc.IsIdentity()) << "Default constructed location should be identity";
-
-  const gp_Trsf& aTrsf = aLoc.Transformation();
-  EXPECT_EQ(aTrsf.Form(), gp_Identity) << "Default location should return identity transformation";
-}
-
-TEST(TopLoc_Location_Test, IdentityTransformation_ReturnsSameInstance)
-{
-  // Test that identity locations return the same transformation instance
-  TopLoc_Location aLoc1;
-  TopLoc_Location aLoc2;
-
-  const gp_Trsf& aTrsf1 = aLoc1.Transformation();
-  const gp_Trsf& aTrsf2 = aLoc2.Transformation();
-
-  // Both should return reference to the same identity transformation
-  EXPECT_EQ(&aTrsf1, &aTrsf2) << "Identity locations should share the same transformation instance";
-}
-
-TEST(TopLoc_Location_Test, Squared_EqualsMultipliedBySelf)
-{
-  // Test that Squared() equals Multiplied(self)
-  gp_Trsf aTrsf;
-  aTrsf.SetTranslation(gp_Vec(1.0, 2.0, 3.0));
-  TopLoc_Location aLoc(aTrsf);
-
-  TopLoc_Location aSquared    = aLoc.Squared();
-  TopLoc_Location aMultiplied = aLoc.Multiplied(aLoc);
-
-  EXPECT_EQ(aSquared, aMultiplied) << "Squared() should equal Multiplied(self)";
-
-  // Verify the transformation is actually squared
-  gp_Pnt anOrigin(0, 0, 0);
-  gp_Pnt aTransformed = anOrigin.Transformed(aSquared.Transformation());
-  gp_Pnt anExpected(2.0, 4.0, 6.0); // Double translation
-
-  EXPECT_NEAR(aTransformed.X(), anExpected.X(), 1e-10);
-  EXPECT_NEAR(aTransformed.Y(), anExpected.Y(), 1e-10);
-  EXPECT_NEAR(aTransformed.Z(), anExpected.Z(), 1e-10);
-}
-
-TEST(TopLoc_Location_Test, Powered2_UsesSquaredFastPath)
-{
-  // Test that Powered(2) equals Squared() (verifies fast path)
-  gp_Trsf aTrsf;
-  aTrsf.SetTranslation(gp_Vec(5.0, -3.0, 1.5));
-  TopLoc_Location aLoc(aTrsf);
-
-  TopLoc_Location aPowered = aLoc.Powered(2);
-  TopLoc_Location aSquared = aLoc.Squared();
-
-  EXPECT_EQ(aPowered, aSquared) << "Powered(2) should equal Squared()";
-}
-
-TEST(TopLoc_Location_Test, Powered_VariousPowers)
-{
-  // Test Powered() with various power values
-  gp_Trsf aTrsf;
-  aTrsf.SetTranslation(gp_Vec(1.0, 0.0, 0.0));
-  TopLoc_Location aLoc(aTrsf);
-
-  // Power 0 should return identity
-  TopLoc_Location aPow0 = aLoc.Powered(0);
-  EXPECT_TRUE(aPow0.IsIdentity()) << "Powered(0) should return identity";
-
-  // Power 1 should return self
-  TopLoc_Location aPow1 = aLoc.Powered(1);
-  EXPECT_EQ(aPow1, aLoc) << "Powered(1) should return self";
-
-  // Power 2 should be double translation
-  TopLoc_Location aPow2 = aLoc.Powered(2);
-  gp_Pnt          anOrigin(0, 0, 0);
-  gp_Pnt          aResult = anOrigin.Transformed(aPow2.Transformation());
-  EXPECT_NEAR(aResult.X(), 2.0, 1e-10) << "Powered(2) should double the translation";
-
-  // Power 3
-  TopLoc_Location aPow3 = aLoc.Powered(3);
-  aResult               = anOrigin.Transformed(aPow3.Transformation());
-  EXPECT_NEAR(aResult.X(), 3.0, 1e-10) << "Powered(3) should triple the translation";
-
-  // Negative power (inverse)
-  TopLoc_Location aPowNeg1   = aLoc.Powered(-1);
-  TopLoc_Location anInverted = aLoc.Inverted();
-  EXPECT_EQ(aPowNeg1, anInverted) << "Powered(-1) should equal Inverted()";
-}
-
-TEST(TopLoc_Location_Test, SharesNode_DetectsSharedStructure)
-{
-  // Test that SharesNode correctly detects shared list nodes
-  gp_Trsf aTrsf;
-  aTrsf.SetTranslation(gp_Vec(1.0, 2.0, 3.0));
-  TopLoc_Location aLoc1(aTrsf);
-
-  // Copy constructor should share the same nodes
-  TopLoc_Location aLoc2 = aLoc1;
-
-  EXPECT_EQ(aLoc1, aLoc2) << "Copied location should equal original";
-
-  // Assignment should also share nodes
-  TopLoc_Location aLoc3;
-  aLoc3 = aLoc1;
-
-  EXPECT_EQ(aLoc3, aLoc1) << "Assigned location should equal original";
-}
-
-TEST(TopLoc_Location_Test, Equality_DifferentTransformations)
-{
-  // Test that different transformations are not equal
-  gp_Trsf aTrsf1;
-  aTrsf1.SetTranslation(gp_Vec(1.0, 0.0, 0.0));
-  TopLoc_Location aLoc1(aTrsf1);
-
-  gp_Trsf aTrsf2;
-  aTrsf2.SetTranslation(gp_Vec(0.0, 1.0, 0.0));
-  TopLoc_Location aLoc2(aTrsf2);
-
-  EXPECT_NE(aLoc1, aLoc2) << "Different transformations should not be equal";
-  EXPECT_TRUE(aLoc1.IsDifferent(aLoc2))
-    << "IsDifferent should return true for different transformations";
-}
-
-TEST(TopLoc_Location_Test, Multiplication_Composition)
-{
-  // Test location multiplication (composition)
-  gp_Trsf aTrsf1;
-  aTrsf1.SetTranslation(gp_Vec(1.0, 0.0, 0.0));
-  TopLoc_Location aLoc1(aTrsf1);
-
-  gp_Trsf aTrsf2;
-  aTrsf2.SetTranslation(gp_Vec(0.0, 2.0, 0.0));
-  TopLoc_Location aLoc2(aTrsf2);
-
-  TopLoc_Location aComposed = aLoc1 * aLoc2;
-
-  // Apply composed transformation
-  gp_Pnt anOrigin(0, 0, 0);
-  gp_Pnt aResult = anOrigin.Transformed(aComposed.Transformation());
-
-  EXPECT_NEAR(aResult.X(), 1.0, 1e-10);
-  EXPECT_NEAR(aResult.Y(), 2.0, 1e-10);
-  EXPECT_NEAR(aResult.Z(), 0.0, 1e-10);
-}
-
-TEST(TopLoc_Location_Test, Inverted_ProducesInverse)
-{
-  // Test that Location * Inverted() = Identity
-  gp_Trsf aTrsf;
-  aTrsf.SetTranslation(gp_Vec(5.0, -3.0, 2.0));
-  TopLoc_Location aLoc(aTrsf);
-
-  TopLoc_Location anInv      = aLoc.Inverted();
-  TopLoc_Location anIdentity = aLoc * anInv;
-
-  EXPECT_TRUE(anIdentity.IsIdentity()) << "Location * Inverted() should produce identity";
-
-  // Also test in reverse order
-  TopLoc_Location anIdentity2 = anInv * aLoc;
-  EXPECT_TRUE(anIdentity2.IsIdentity()) << "Inverted() * Location should also produce identity";
-}
-
-TEST(TopLoc_Location_Test, Divided_EqualsMultipliedByInverse)
-{
-  // Test that Divided equals Multiplied by inverse
-  gp_Trsf aTrsf1;
-  aTrsf1.SetTranslation(gp_Vec(3.0, 4.0, 5.0));
-  TopLoc_Location aLoc1(aTrsf1);
-
-  gp_Trsf aTrsf2;
-  aTrsf2.SetTranslation(gp_Vec(1.0, 1.0, 1.0));
-  TopLoc_Location aLoc2(aTrsf2);
-
-  TopLoc_Location aDivided    = aLoc1 / aLoc2;
-  TopLoc_Location aMultiplied = aLoc1 * aLoc2.Inverted();
-
-  EXPECT_EQ(aDivided, aMultiplied) << "Divided should equal Multiplied by Inverted";
-}
-
-TEST(TopLoc_Location_Test, Predivided_EqualsInverseMultiplied)
-{
-  // Test Predivided = Other.Inverted() * this
-  gp_Trsf aTrsf1;
-  aTrsf1.SetTranslation(gp_Vec(2.0, 3.0, 4.0));
-  TopLoc_Location aLoc1(aTrsf1);
-
-  gp_Trsf aTrsf2;
-  aTrsf2.SetTranslation(gp_Vec(1.0, 1.0, 1.0));
-  TopLoc_Location aLoc2(aTrsf2);
-
-  TopLoc_Location aPredivided = aLoc1.Predivided(aLoc2);
-  TopLoc_Location anExpected  = aLoc2.Inverted() * aLoc1;
-
-  EXPECT_EQ(aPredivided, anExpected) << "Predivided should equal Other.Inverted() * this";
-}
-
-TEST(TopLoc_Location_Test, HashCode_ConsistentForEqualLocations)
-{
-  // Test that locations sharing the same structure have the same hash code
-  gp_Trsf aTrsf;
-  aTrsf.SetTranslation(gp_Vec(1.0, 2.0, 3.0));
-  TopLoc_Location aLoc1(aTrsf);
-
-  // Copy constructor shares the same underlying structure
-  TopLoc_Location aLoc2 = aLoc1;
-
-  EXPECT_EQ(aLoc1.HashCode(), aLoc2.HashCode())
-    << "Copied locations (sharing structure) should have the same hash code";
-
-  // Identity locations should have the same hash code
-  TopLoc_Location anId1;
-  TopLoc_Location anId2;
-  EXPECT_EQ(anId1.HashCode(), anId2.HashCode())
-    << "Identity locations should have the same hash code";
-  EXPECT_EQ(anId1.HashCode(), static_cast<size_t>(0)) << "Identity location hash should be 0";
-}
-
-TEST(TopLoc_Location_Test, HashCode_DifferentForDifferentLocations)
-{
-  // Test that different locations (likely) have different hash codes
-  gp_Trsf aTrsf1;
-  aTrsf1.SetTranslation(gp_Vec(1.0, 0.0, 0.0));
-  TopLoc_Location aLoc1(aTrsf1);
-
-  gp_Trsf aTrsf2;
-  aTrsf2.SetTranslation(gp_Vec(0.0, 1.0, 0.0));
-  TopLoc_Location aLoc2(aTrsf2);
-
-  // Hash codes should (very likely) be different
-  // Note: Hash collision is theoretically possible but highly unlikely
-  EXPECT_NE(aLoc1.HashCode(), aLoc2.HashCode())
-    << "Different locations should (likely) have different hash codes";
-}
-
-TEST(TopLoc_Location_Test, ConstCorrectness_TransformationAccess)
-{
-  // Test const correctness of Transformation() access
-  gp_Trsf aTrsf;
-  aTrsf.SetTranslation(gp_Vec(1.0, 2.0, 3.0));
-  const TopLoc_Location aLoc(aTrsf);
-
-  // This should compile - Transformation() is const
-  const gp_Trsf& aTrsfRef = aLoc.Transformation();
-
-  EXPECT_EQ(aTrsfRef.Form(), gp_Translation);
-
-  // Test identity case
-  const TopLoc_Location anIdLoc;
-  const gp_Trsf&        anIdTrsf = anIdLoc.Transformation();
-
-  EXPECT_EQ(anIdTrsf.Form(), gp_Identity);
-}
-
-TEST(TopLoc_Location_Test, Rotation_Composition)
-{
-  // Test rotation transformations
-  gp_Trsf aRot;
-  gp_Ax1  anAxis(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1));
-  aRot.SetRotation(anAxis, M_PI / 2.0); // 90 degrees around Z
-
-  TopLoc_Location aLoc(aRot);
-  TopLoc_Location aSquared = aLoc.Squared();
-
-  // Point at (1,0,0) rotated 90 degrees twice should be at (-1,0,0)
-  gp_Pnt aPnt(1, 0, 0);
-  gp_Pnt aResult = aPnt.Transformed(aSquared.Transformation());
-
-  EXPECT_NEAR(aResult.X(), -1.0, 1e-10);
-  EXPECT_NEAR(aResult.Y(), 0.0, 1e-10);
-  EXPECT_NEAR(aResult.Z(), 0.0, 1e-10);
-}
-
-TEST(TopLoc_Location_Test, Clear_ResetsToIdentity)
-{
-  // Test that Clear() resets location to identity
-  gp_Trsf aTrsf;
-  aTrsf.SetTranslation(gp_Vec(1.0, 2.0, 3.0));
-  TopLoc_Location aLoc(aTrsf);
-
-  EXPECT_FALSE(aLoc.IsIdentity()) << "Location should not be identity before Clear()";
-
-  aLoc.Clear();
-
-  EXPECT_TRUE(aLoc.IsIdentity()) << "Location should be identity after Clear()";
-}
-
-TEST(TopLoc_Location_Test, Identity_Method)
-{
-  // Test Identity() method
-  gp_Trsf aTrsf;
-  aTrsf.SetTranslation(gp_Vec(1.0, 2.0, 3.0));
-  TopLoc_Location aLoc(aTrsf);
-
-  EXPECT_FALSE(aLoc.IsIdentity());
-
-  aLoc.Identity();
-
-  EXPECT_TRUE(aLoc.IsIdentity()) << "Identity() should reset location to identity";
-}
-
-//=================================================================================================
-// Additional Edge Case Tests
-//=================================================================================================
-
-TEST(TopLoc_Location_Test, ChainedMultiplication_MultipleLocations)
-{
-  // Test multiple location multiplications
-  gp_Trsf aTrsf1, aTrsf2, aTrsf3;
-  aTrsf1.SetTranslation(gp_Vec(1.0, 0.0, 0.0));
-  aTrsf2.SetTranslation(gp_Vec(0.0, 1.0, 0.0));
-  aTrsf3.SetTranslation(gp_Vec(0.0, 0.0, 1.0));
-
-  TopLoc_Location aLoc1(aTrsf1);
-  TopLoc_Location aLoc2(aTrsf2);
-  TopLoc_Location aLoc3(aTrsf3);
-
-  // Chain multiplication
-  TopLoc_Location aResult = aLoc1 * aLoc2 * aLoc3;
-
-  // Apply to point
-  gp_Pnt anOrigin(0, 0, 0);
-  gp_Pnt aTransformed = anOrigin.Transformed(aResult.Transformation());
-
-  EXPECT_NEAR(aTransformed.X(), 1.0, 1e-10);
-  EXPECT_NEAR(aTransformed.Y(), 1.0, 1e-10);
-  EXPECT_NEAR(aTransformed.Z(), 1.0, 1e-10);
-}
-
-TEST(TopLoc_Location_Test, IdentityMultiplication_NeutralElement)
-{
-  // Test that identity is neutral element for multiplication
-  gp_Trsf aTrsf;
-  aTrsf.SetTranslation(gp_Vec(5.0, 3.0, 1.0));
-  TopLoc_Location aLoc(aTrsf);
-  TopLoc_Location anIdentity;
-
-  // Identity * Location = Location
-  TopLoc_Location aResult1 = anIdentity * aLoc;
-  EXPECT_EQ(aResult1, aLoc) << "Identity * Location should equal Location";
-
-  // Location * Identity = Location
-  TopLoc_Location aResult2 = aLoc * anIdentity;
-  EXPECT_EQ(aResult2, aLoc) << "Location * Identity should equal Location";
-}
-
-TEST(TopLoc_Location_Test, CopySemantics)
-{
-  // Test copy construction and assignment
-  gp_Trsf aTrsf;
-  aTrsf.SetTranslation(gp_Vec(2.0, 3.0, 4.0));
-  TopLoc_Location anOriginal(aTrsf);
-
-  // Copy construction
-  TopLoc_Location aCopied(anOriginal);
-  EXPECT_EQ(aCopied, anOriginal);
-  EXPECT_EQ(aCopied.HashCode(), anOriginal.HashCode());
-
-  // Copy assignment
-  TopLoc_Location anAssigned;
-  anAssigned = anOriginal;
-  EXPECT_EQ(anAssigned, anOriginal);
-
-  // All three should be equal (share structure)
-  EXPECT_EQ(aCopied, anAssigned);
-}
-
-TEST(TopLoc_Location_Test, ScaleTransformation_Composition)
-{
-  // Test scale transformations
-  gp_Trsf aScale;
-  aScale.SetScale(gp_Pnt(0, 0, 0), 2.0);
-
-  TopLoc_Location aLoc(aScale);
-  TopLoc_Location aSquared = aLoc.Squared();
-
-  // Point scaled by 2, then by 2 again = scaled by 4
-  gp_Pnt aPnt(1, 1, 1);
-  gp_Pnt aResult = aPnt.Transformed(aSquared.Transformation());
-
-  EXPECT_NEAR(aResult.X(), 4.0, 1e-10);
-  EXPECT_NEAR(aResult.Y(), 4.0, 1e-10);
-  EXPECT_NEAR(aResult.Z(), 4.0, 1e-10);
-}
-
-TEST(TopLoc_Location_Test, ComplexTransformation_TranslationAndRotation)
-{
-  // Test combination of translation and rotation
-  gp_Trsf aTrans;
-  aTrans.SetTranslation(gp_Vec(10.0, 0.0, 0.0));
-
-  gp_Trsf aRot;
-  gp_Ax1  anAxis(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1));
-  aRot.SetRotation(anAxis, M_PI); // 180 degrees
-
-  TopLoc_Location aLocTrans(aTrans);
-  TopLoc_Location aLocRot(aRot);
-
-  // Translate then rotate
-  TopLoc_Location aComposed = aLocRot * aLocTrans;
-
-  // Point at origin, translate to (10,0,0), then rotate 180 degrees -> (-10,0,0)
-  gp_Pnt anOrigin(0, 0, 0);
-  gp_Pnt aResult = anOrigin.Transformed(aComposed.Transformation());
-
-  EXPECT_NEAR(aResult.X(), -10.0, 1e-9);
-  EXPECT_NEAR(aResult.Y(), 0.0, 1e-9);
-  EXPECT_NEAR(aResult.Z(), 0.0, 1e-9);
-}
-
-TEST(TopLoc_Location_Test, PoweredWithLargePower)
-{
-  // Test Powered() with larger power value
-  gp_Trsf aTrsf;
-  aTrsf.SetTranslation(gp_Vec(1.0, 0.0, 0.0));
-  TopLoc_Location aLoc(aTrsf);
-
-  // Power of 10
-  TopLoc_Location aPow10 = aLoc.Powered(10);
-
-  gp_Pnt anOrigin(0, 0, 0);
-  gp_Pnt aResult = anOrigin.Transformed(aPow10.Transformation());
-
-  EXPECT_NEAR(aResult.X(), 10.0, 1e-9) << "Powered(10) should translate 10 units";
-}
-
-TEST(TopLoc_Location_Test, SelfMultiplication_MultipleIterations)
-{
-  // Test repeated self-multiplication
-  gp_Trsf aTrsf;
-  aTrsf.SetTranslation(gp_Vec(1.0, 0.0, 0.0));
-  TopLoc_Location aLoc(aTrsf);
-
-  TopLoc_Location aResult = aLoc;
-  for (int i = 0; i < 5; ++i)
-  {
-    aResult = aResult * aLoc;
-  }
-
-  // Should be 6 times the original (1 + 5 multiplications)
-  gp_Pnt anOrigin(0, 0, 0);
-  gp_Pnt aTransformed = anOrigin.Transformed(aResult.Transformation());
-
-  EXPECT_NEAR(aTransformed.X(), 6.0, 1e-10);
-}
-
-TEST(TopLoc_Location_Test, AssociativityOfMultiplication)
-{
-  // Test that (A * B) * C = A * (B * C)
-  gp_Trsf aTrsf1, aTrsf2, aTrsf3;
-  aTrsf1.SetTranslation(gp_Vec(1.0, 0.0, 0.0));
-  aTrsf2.SetTranslation(gp_Vec(0.0, 2.0, 0.0));
-  aTrsf3.SetTranslation(gp_Vec(0.0, 0.0, 3.0));
-
-  TopLoc_Location aLocA(aTrsf1);
-  TopLoc_Location aLocB(aTrsf2);
-  TopLoc_Location aLocC(aTrsf3);
-
-  TopLoc_Location aLeft  = (aLocA * aLocB) * aLocC;
-  TopLoc_Location aRight = aLocA * (aLocB * aLocC);
-
-  gp_Pnt anOrigin(0, 0, 0);
-  gp_Pnt aResultLeft  = anOrigin.Transformed(aLeft.Transformation());
-  gp_Pnt aResultRight = anOrigin.Transformed(aRight.Transformation());
-
-  EXPECT_NEAR(aResultLeft.X(), aResultRight.X(), 1e-10);
-  EXPECT_NEAR(aResultLeft.Y(), aResultRight.Y(), 1e-10);
-  EXPECT_NEAR(aResultLeft.Z(), aResultRight.Z(), 1e-10);
-}
-
-TEST(TopLoc_Location_Test, InversionIdempotence)
-{
-  // Test that (L.Inverted()).Inverted() = L
-  gp_Trsf aTrsf;
-  aTrsf.SetTranslation(gp_Vec(3.0, 4.0, 5.0));
-  TopLoc_Location aLoc(aTrsf);
-
-  TopLoc_Location aDoubleInverted = aLoc.Inverted().Inverted();
-
-  gp_Pnt aPnt(1, 1, 1);
-  gp_Pnt aResult1 = aPnt.Transformed(aLoc.Transformation());
-  gp_Pnt aResult2 = aPnt.Transformed(aDoubleInverted.Transformation());
-
-  EXPECT_NEAR(aResult1.X(), aResult2.X(), 1e-10);
-  EXPECT_NEAR(aResult1.Y(), aResult2.Y(), 1e-10);
-  EXPECT_NEAR(aResult1.Z(), aResult2.Z(), 1e-10);
-}
-
-//=================================================================================================
-// Tests for Phase 3: Binary Exponentiation Optimization
-//=================================================================================================
-
-TEST(TopLoc_Location_Test, BinaryExponentiation_PowerOf2)
-{
-  // Test binary exponentiation for powers of 2 (4, 8, 16)
-  // These should be most efficient: L^8 requires only 3 squarings instead of 7 multiplications
-  gp_Trsf aTrsf;
-  aTrsf.SetTranslation(gp_Vec(1.0, 0.0, 0.0));
-  TopLoc_Location aLoc(aTrsf);
-
-  // Test power of 4 (2^2)
-  TopLoc_Location aPow4 = aLoc.Powered(4);
-  gp_Pnt          anOrigin(0, 0, 0);
-  gp_Pnt          aResult = anOrigin.Transformed(aPow4.Transformation());
-  EXPECT_NEAR(aResult.X(), 4.0, 1e-9) << "Powered(4) should translate 4 units";
-
-  // Test power of 8 (2^3)
-  TopLoc_Location aPow8 = aLoc.Powered(8);
-  aResult               = anOrigin.Transformed(aPow8.Transformation());
-  EXPECT_NEAR(aResult.X(), 8.0, 1e-9) << "Powered(8) should translate 8 units";
-
-  // Test power of 16 (2^4)
-  TopLoc_Location aPow16 = aLoc.Powered(16);
-  aResult                = anOrigin.Transformed(aPow16.Transformation());
-  EXPECT_NEAR(aResult.X(), 16.0, 1e-9) << "Powered(16) should translate 16 units";
-}
-
-TEST(TopLoc_Location_Test, BinaryExponentiation_OddPowers)
-{
-  // Test binary exponentiation for odd powers (5, 7, 9)
-  gp_Trsf aTrsf;
-  aTrsf.SetTranslation(gp_Vec(1.0, 0.0, 0.0));
-  TopLoc_Location aLoc(aTrsf);
-
-  gp_Pnt anOrigin(0, 0, 0);
-
-  // Test power of 5
-  TopLoc_Location aPow5   = aLoc.Powered(5);
-  gp_Pnt          aResult = anOrigin.Transformed(aPow5.Transformation());
-  EXPECT_NEAR(aResult.X(), 5.0, 1e-9) << "Powered(5) should translate 5 units";
-
-  // Test power of 7
-  TopLoc_Location aPow7 = aLoc.Powered(7);
-  aResult               = anOrigin.Transformed(aPow7.Transformation());
-  EXPECT_NEAR(aResult.X(), 7.0, 1e-9) << "Powered(7) should translate 7 units";
-
-  // Test power of 9
-  TopLoc_Location aPow9 = aLoc.Powered(9);
-  aResult               = anOrigin.Transformed(aPow9.Transformation());
-  EXPECT_NEAR(aResult.X(), 9.0, 1e-9) << "Powered(9) should translate 9 units";
-}
-
-TEST(TopLoc_Location_Test, BinaryExponentiation_VeryLargePower)
-{
-  // Test with very large power to verify efficiency improvement
-  // L^64 with binary exponentiation: only 6 squarings
-  // L^64 with naive: 63 multiplications
-  gp_Trsf aTrsf;
-  aTrsf.SetTranslation(gp_Vec(0.5, 0.0, 0.0));
-  TopLoc_Location aLoc(aTrsf);
-
-  TopLoc_Location aPow64 = aLoc.Powered(64);
-
-  gp_Pnt anOrigin(0, 0, 0);
-  gp_Pnt aResult = anOrigin.Transformed(aPow64.Transformation());
-
-  EXPECT_NEAR(aResult.X(), 32.0, 1e-8) << "Powered(64) should translate 32 units (64 * 0.5)";
-}
-
-TEST(TopLoc_Location_Test, BinaryExponentiation_NegativePowers)
-{
-  // Test binary exponentiation for negative powers
-  gp_Trsf aTrsf;
-  aTrsf.SetTranslation(gp_Vec(1.0, 0.0, 0.0));
-  TopLoc_Location aLoc(aTrsf);
-
-  // Test power of -4 (should be inverse of power 4)
-  TopLoc_Location aPowNeg4 = aLoc.Powered(-4);
-  TopLoc_Location aPow4    = aLoc.Powered(4);
-
-  TopLoc_Location aIdentity = aPowNeg4 * aPow4;
-  EXPECT_TRUE(aIdentity.IsIdentity()) << "L^(-4) * L^4 should produce identity";
-
-  // Verify the transformation
-  gp_Pnt anOrigin(0, 0, 0);
-  gp_Pnt aResult = anOrigin.Transformed(aPowNeg4.Transformation());
-  EXPECT_NEAR(aResult.X(), -4.0, 1e-9) << "Powered(-4) should translate -4 units";
-}
-
-TEST(TopLoc_Location_Test, BinaryExponentiation_RotationPowers)
-{
-  // Test binary exponentiation with rotation transformations
-  // 45 degrees rotation, power of 8 should equal 360 degrees (identity)
-  gp_Trsf aRot;
-  gp_Ax1  anAxis(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1));
-  aRot.SetRotation(anAxis, M_PI / 4.0); // 45 degrees
-
-  TopLoc_Location aLoc(aRot);
-  TopLoc_Location aPow8 = aLoc.Powered(8); // 8 * 45 degrees = 360 degrees
-
-  // Should be close to identity (full rotation)
-  gp_Pnt aPnt(1, 0, 0);
-  gp_Pnt aResult = aPnt.Transformed(aPow8.Transformation());
-
-  EXPECT_NEAR(aResult.X(), 1.0, 1e-8);
-  EXPECT_NEAR(aResult.Y(), 0.0, 1e-8);
-  EXPECT_NEAR(aResult.Z(), 0.0, 1e-8);
-}
-
-TEST(TopLoc_Location_Test, BinaryExponentiation_MixedEvenOdd)
-{
-  // Test a mix of even and odd powers to verify the algorithm handles both cases
-  gp_Trsf aTrsf;
-  aTrsf.SetTranslation(gp_Vec(2.0, 0.0, 0.0));
-  TopLoc_Location aLoc(aTrsf);
-
-  gp_Pnt anOrigin(0, 0, 0);
-
-  // Test 6 (even)
-  TopLoc_Location aPow6   = aLoc.Powered(6);
-  gp_Pnt          aResult = anOrigin.Transformed(aPow6.Transformation());
-  EXPECT_NEAR(aResult.X(), 12.0, 1e-9);
-
-  // Test 11 (odd)
-  TopLoc_Location aPow11 = aLoc.Powered(11);
-  aResult                = anOrigin.Transformed(aPow11.Transformation());
-  EXPECT_NEAR(aResult.X(), 22.0, 1e-9);
-
-  // Test 15 (odd)
-  TopLoc_Location aPow15 = aLoc.Powered(15);
-  aResult                = anOrigin.Transformed(aPow15.Transformation());
-  EXPECT_NEAR(aResult.X(), 30.0, 1e-9);
-}
index e70862c03b918b1e6d19c01eb89abecfad5e17ec..44593cb6e0b72eeb5b34e6de5b3e17a5d927d111 100644 (file)
@@ -14,6 +14,7 @@ set(OCCT_TopLoc_FILES
   TopLoc_MapOfLocation.hxx
   TopLoc_SListNodeOfItemLocation.cxx
   TopLoc_SListNodeOfItemLocation.hxx
+  TopLoc_SListNodeOfItemLocation.lxx
   TopLoc_SListOfItemLocation.cxx
   TopLoc_SListOfItemLocation.hxx
 )
index 79279209e0aebcf0025d57f1e00d2c6d65c36ff5..f8c8abdd31948c6e937ec492afab3ca36822a036 100644 (file)
@@ -44,13 +44,13 @@ public:
   Standard_EXPORT TopLoc_Datum3D(const gp_Trsf& T);
 
   //! Returns a gp_Trsf which, when applied to this datum, produces the default datum.
-  const gp_Trsf& Transformation() const noexcept { return myTrsf; }
+  const gp_Trsf& Transformation() const { return myTrsf; }
 
   //! Returns a gp_Trsf which, when applied to this datum, produces the default datum.
-  const gp_Trsf& Trsf() const noexcept { return myTrsf; }
+  const gp_Trsf& Trsf() const { return myTrsf; }
 
   //! Return transformation form.
-  gp_TrsfForm Form() const noexcept { return myTrsf.Form(); }
+  gp_TrsfForm Form() const { return myTrsf.Form(); }
 
   //! Dumps the content of me into the stream
   Standard_EXPORT void DumpJson(Standard_OStream& theOStream, Standard_Integer theDepth = -1) const;
index bf374e4059d4de7e036990cc6ff889ffb9382e75..9cbd5791fec6f71c482821e3f79ca374d520fb6b 100644 (file)
@@ -24,7 +24,7 @@
 TopLoc_ItemLocation::TopLoc_ItemLocation(const Handle(TopLoc_Datum3D)& D, const Standard_Integer P)
     : myDatum(D),
       myPower(P),
-      myTrsf(P == 1 ? D->Transformation() : D->Transformation().Powered(P))
+      myTrsf(D->Transformation().Powered(P))
 {
 }
 
index 0ada67b56f4e36c8c6907920ae78ffc577bc33d6..f78330be47183507881b5095f9213b232c01ff3e 100644 (file)
 #include <Standard_Dump.hxx>
 #include <TopLoc_Datum3D.hxx>
 #include <TopLoc_Location.hxx>
-#include <TopLoc_SListNodeOfItemLocation.hxx>
 #include <TopLoc_SListOfItemLocation.hxx>
 
 //=================================================================================================
 
+TopLoc_Location::TopLoc_Location() {}
+
+//=================================================================================================
+
 TopLoc_Location::TopLoc_Location(const Handle(TopLoc_Datum3D)& D)
 {
   myItems.Construct(TopLoc_ItemLocation(D, 1));
@@ -105,6 +108,26 @@ TopLoc_Location TopLoc_Location::Multiplied(const TopLoc_Location& Other) const
   return result;
 }
 
+//=======================================================================
+// function : Divided
+// purpose  : operator /   this*Other.Inverted()
+//=======================================================================
+
+TopLoc_Location TopLoc_Location::Divided(const TopLoc_Location& Other) const
+{
+  return Multiplied(Other.Inverted());
+}
+
+//=======================================================================
+// function : Predivided
+// purpose  : return Other.Inverted() * this
+//=======================================================================
+
+TopLoc_Location TopLoc_Location::Predivided(const TopLoc_Location& Other) const
+{
+  return Other.Inverted().Multiplied(*this);
+}
+
 //=================================================================================================
 
 TopLoc_Location TopLoc_Location::Powered(const Standard_Integer pwr) const
@@ -115,8 +138,6 @@ TopLoc_Location TopLoc_Location::Powered(const Standard_Integer pwr) const
     return *this;
   if (pwr == 0)
     return TopLoc_Location();
-  if (pwr == 2)
-    return Squared(); // Fast path for most common case
 
   // optimisation when just one element
   if (myItems.Tail().IsEmpty())
@@ -126,62 +147,41 @@ TopLoc_Location TopLoc_Location::Powered(const Standard_Integer pwr) const
     return result;
   }
 
-  // Binary exponentiation for efficient computation: O(log n) instead of O(n)
   if (pwr > 0)
-  {
-    if (pwr % 2 == 0)
-    {
-      // Even power: L^n = (L^(n/2))^2
-      return Powered(pwr / 2).Squared();
-    }
-    else
-    {
-      // Odd power: L^n = L * L^(n-1)
-      return Multiplied(Powered(pwr - 1));
-    }
-  }
+    return Multiplied(Powered(pwr - 1));
   else
-  {
     return Inverted().Powered(-pwr);
-  }
 }
 
 //=================================================================================================
 
-// Two locations are Equal if the Items have the same LocalValues and Powers
+// two locations are Equal if the Items have the same LocalValues and Powers
+// this is a recursive function to test it
 
-Standard_Boolean TopLoc_Location::IsEqual(const TopLoc_Location& theOther) const noexcept
+Standard_Boolean TopLoc_Location::IsEqual(const TopLoc_Location& Other) const
 {
-  // Fast path: same node means equal (handles identity == identity case too)
-  if (myItems.Node() == theOther.myItems.Node())
+  const void** p = (const void**)&myItems;
+  const void** q = (const void**)&Other.myItems;
+  if (*p == *q)
   {
     return Standard_True;
   }
-  // Fast rejection: different cached hashes means not equal
-  if (myItems.HashCode() != theOther.myItems.HashCode())
+  if (IsIdentity() || Other.IsIdentity())
   {
     return Standard_False;
   }
-  // Hash collision: need element-by-element comparison
-  TopLoc_SListOfItemLocation anIt1 = myItems;
-  TopLoc_SListOfItemLocation anIt2 = theOther.myItems;
-  while (anIt1.More() && anIt2.More())
+  if (FirstDatum() != Other.FirstDatum())
   {
-    if (anIt1.Node() == anIt2.Node())
-    {
-      return Standard_True; // Shared tail
-    }
-    const TopLoc_ItemLocation& aItem1 = anIt1.Value();
-    const TopLoc_ItemLocation& aItem2 = anIt2.Value();
-    if (aItem1.myDatum != aItem2.myDatum || aItem1.myPower != aItem2.myPower)
-    {
-      return Standard_False;
-    }
-    anIt1.Next();
-    anIt2.Next();
+    return Standard_False;
+  }
+  if (FirstPower() != Other.FirstPower())
+  {
+    return Standard_False;
+  }
+  else
+  {
+    return NextLocation() == Other.NextLocation();
   }
-  // Equal only if both exhausted
-  return !anIt1.More() && !anIt2.More();
 }
 
 //=================================================================================================
index 6d566b71bb2bd97ab5e6c19b80a6046ef0df3221..fcbe4d6fb3ad141104c574b3463156fa36b5c064 100644 (file)
@@ -39,7 +39,7 @@ public:
 
   //! Constructs an empty local coordinate system object.
   //! Note: A Location constructed from a default datum is said to be "empty".
-  TopLoc_Location() noexcept = default;
+  Standard_EXPORT TopLoc_Location();
 
   //! Constructs the local coordinate system object defined
   //! by the transformation T. T invokes in turn, a TopLoc_Datum3D object.
@@ -52,10 +52,10 @@ public:
   Standard_EXPORT TopLoc_Location(const Handle(TopLoc_Datum3D)& D);
 
   //! Returns true if this location is equal to the Identity transformation.
-  Standard_Boolean IsIdentity() const noexcept;
+  Standard_Boolean IsIdentity() const;
 
   //! Resets this location to the Identity transformation.
-  void Identity() noexcept;
+  void Identity();
 
   //! Returns the first elementary datum of the
   //! Location. Use the NextLocation function recursively to access
@@ -98,10 +98,7 @@ public:
   }
 
   //! Returns <me> / <Other>.
-  Standard_NODISCARD TopLoc_Location Divided(const TopLoc_Location& Other) const
-  {
-    return Multiplied(Other.Inverted());
-  }
+  Standard_NODISCARD Standard_EXPORT TopLoc_Location Divided(const TopLoc_Location& Other) const;
 
   Standard_NODISCARD TopLoc_Location operator/(const TopLoc_Location& Other) const
   {
@@ -109,30 +106,23 @@ public:
   }
 
   //! Returns <Other>.Inverted() * <me>.
-  Standard_NODISCARD TopLoc_Location Predivided(const TopLoc_Location& Other) const
-  {
-    return Other.Inverted().Multiplied(*this);
-  }
+  Standard_NODISCARD Standard_EXPORT TopLoc_Location Predivided(const TopLoc_Location& Other) const;
 
   //! Returns me at the power <pwr>. If <pwr> is zero
   //! returns Identity. <pwr> can be lower than zero
   //! (usual meaning for powers).
   Standard_NODISCARD Standard_EXPORT TopLoc_Location Powered(const Standard_Integer pwr) const;
 
-  //! Returns the square of this location (optimized version of Powered(2)).
-  //! This is the most common power operation in actual usage.
-  Standard_NODISCARD TopLoc_Location Squared() const { return Multiplied(*this); }
-
   //! Returns a hashed value for this local coordinate system. This value is used, with map tables,
   //! to store and retrieve the object easily
   //! @return a computed hash code
-  size_t HashCode() const noexcept;
+  size_t HashCode() const;
 
   //! Returns true if this location and the location Other
   //! have the same elementary data, i.e. contain the same
   //! series of TopLoc_Datum3D and respective powers.
   //! This method is an alias for operator ==.
-  Standard_EXPORT Standard_Boolean IsEqual(const TopLoc_Location& Other) const noexcept;
+  Standard_EXPORT Standard_Boolean IsEqual(const TopLoc_Location& Other) const;
 
   Standard_Boolean operator==(const TopLoc_Location& Other) const { return IsEqual(Other); }
 
@@ -151,10 +141,11 @@ public:
   Standard_EXPORT void ShallowDump(Standard_OStream& S) const;
 
   //! Clear myItems
-  void Clear() noexcept { myItems.Clear(); }
+  void Clear() { myItems.Clear(); }
 
-  static constexpr Standard_Real ScalePrec() { return 1.e-14; }
+  static Standard_Real ScalePrec() { return 1.e-14; }
 
+protected:
 private:
   TopLoc_SListOfItemLocation myItems;
 };
index e6a6519c4c470ff96d35bd6c07822661b6352b94..bfdc712186c8d1b5bc0ca2bb1b328b5131e5cd22 100644 (file)
 
 //=================================================================================================
 
-inline Standard_Boolean TopLoc_Location::IsIdentity() const noexcept
+inline Standard_Boolean TopLoc_Location::IsIdentity() const
 {
   return myItems.IsEmpty();
 }
 
 //=================================================================================================
 
-inline void TopLoc_Location::Identity() noexcept
+inline void TopLoc_Location::Identity()
 {
   myItems.Clear();
 }
@@ -56,10 +56,25 @@ inline const TopLoc_Location& TopLoc_Location::NextLocation() const
 
 //=================================================================================================
 
-inline size_t TopLoc_Location::HashCode() const noexcept
+inline size_t TopLoc_Location::HashCode() const
 {
-  // Use cached hash from the list node
-  return myItems.HashCode();
+  // Hashing base on IsEqual function
+  if (myItems.IsEmpty())
+  {
+    return 0;
+  }
+  size_t                     aHash = opencascade::MurmurHash::optimalSeed<size_t>();
+  TopLoc_SListOfItemLocation items = myItems;
+  size_t                     aCombined[3];
+  while (items.More())
+  {
+    aCombined[0] = std::hash<Handle(TopLoc_Datum3D)>{}(items.Value().myDatum);
+    aCombined[1] = opencascade::hash(items.Value().myPower);
+    aCombined[2] = aHash;
+    aHash        = opencascade::hashBytes(aCombined, sizeof(aCombined));
+    items.Next();
+  }
+  return aHash;
 }
 
 //=================================================================================================
index d8cd5ac818c3bab7a2a9d0bba60853e5806e540f..d364e57c29502c145d5d68cc294d9dd5c94ade8c 100644 (file)
@@ -31,23 +31,11 @@ class TopLoc_SListNodeOfItemLocation : public Standard_Transient
 
 public:
   TopLoc_SListNodeOfItemLocation(const TopLoc_ItemLocation&        I,
-                                 const TopLoc_SListOfItemLocation& aTail,
-                                 size_t                            theHash)
-      : myTail(aTail),
-        myValue(I),
-        myHash(theHash)
-  {
-  }
+                                 const TopLoc_SListOfItemLocation& aTail);
 
-  TopLoc_SListOfItemLocation& Tail() { return myTail; }
+  TopLoc_SListOfItemLocation& Tail() const;
 
-  const TopLoc_SListOfItemLocation& Tail() const { return myTail; }
-
-  TopLoc_ItemLocation& Value() { return myValue; }
-
-  const TopLoc_ItemLocation& Value() const { return myValue; }
-
-  size_t HashCode() const noexcept { return myHash; }
+  TopLoc_ItemLocation& Value() const;
 
   DEFINE_STANDARD_RTTIEXT(TopLoc_SListNodeOfItemLocation, Standard_Transient)
 
@@ -55,7 +43,8 @@ protected:
 private:
   TopLoc_SListOfItemLocation myTail;
   TopLoc_ItemLocation        myValue;
-  size_t                     myHash;
 };
 
+#include <TopLoc_SListNodeOfItemLocation.lxx>
+
 #endif // _TopLoc_SListNodeOfItemLocation_HeaderFile
diff --git a/src/FoundationClasses/TKMath/TopLoc/TopLoc_SListNodeOfItemLocation.lxx b/src/FoundationClasses/TKMath/TopLoc/TopLoc_SListNodeOfItemLocation.lxx
new file mode 100644 (file)
index 0000000..d25c20e
--- /dev/null
@@ -0,0 +1,33 @@
+// Copyright (c) 1998-1999 Matra Datavision
+// Copyright (c) 1999-2014 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <TopLoc_ItemLocation.hxx>
+
+inline TopLoc_SListNodeOfItemLocation::TopLoc_SListNodeOfItemLocation(
+  const TopLoc_ItemLocation&        I,
+  const TopLoc_SListOfItemLocation& T)
+    : myTail(T),
+      myValue(I)
+{
+}
+
+inline TopLoc_SListOfItemLocation& TopLoc_SListNodeOfItemLocation::Tail() const
+{
+  return (TopLoc_SListOfItemLocation&)myTail;
+}
+
+inline TopLoc_ItemLocation& TopLoc_SListNodeOfItemLocation::Value() const
+{
+  return (TopLoc_ItemLocation&)myValue;
+}
index 92ecae145468f3e2241f47837f96856cbf9a7a45..1ba89df5d27b779b21d8532e5005c493d7293ba0 100644 (file)
 // commercial license or contractual agreement.
 
 #include <Standard_NoSuchObject.hxx>
-#include <Standard_HashUtils.hxx>
 #include <TopLoc_ItemLocation.hxx>
 #include <TopLoc_SListNodeOfItemLocation.hxx>
 #include <TopLoc_SListOfItemLocation.hxx>
-#include <TopLoc_Datum3D.hxx>
 
 //=================================================================================================
 
 TopLoc_SListOfItemLocation::TopLoc_SListOfItemLocation(const TopLoc_ItemLocation&        anItem,
                                                        const TopLoc_SListOfItemLocation& aTail)
+    : myNode(new TopLoc_SListNodeOfItemLocation(anItem, aTail))
 {
-  // Compute hash combining item's datum, power, and tail's cached hash
-  const size_t aCombined[3] = {std::hash<Handle(TopLoc_Datum3D)>{}(anItem.myDatum),
-                               static_cast<size_t>(anItem.myPower),
-                               aTail.HashCode()};
-  const size_t aHash        = opencascade::hashBytes(aCombined, sizeof(aCombined));
-
-  myNode = new TopLoc_SListNodeOfItemLocation(anItem, aTail, aHash);
   if (!myNode->Tail().IsEmpty())
   {
     const gp_Trsf& aT = myNode->Tail().Value().myTrsf;
@@ -70,10 +62,3 @@ const TopLoc_SListOfItemLocation& TopLoc_SListOfItemLocation::Tail() const
   else
     return *this;
 }
-
-//=================================================================================================
-
-size_t TopLoc_SListOfItemLocation::HashCode() const noexcept
-{
-  return myNode.IsNull() ? 0 : myNode->HashCode();
-}
index cbcc7bac61233d29a5d730252de82a9bd18429ec..51f9e31b76f3e15b556342f140aac533abdb89e5 100644 (file)
@@ -46,7 +46,7 @@ public:
   DEFINE_STANDARD_ALLOC
 
   //! Creates an empty List.
-  TopLoc_SListOfItemLocation() = default;
+  TopLoc_SListOfItemLocation() {}
 
   //! Creates a List with <anItem> as value and <aTail> as tail.
   Standard_EXPORT TopLoc_SListOfItemLocation(const TopLoc_ItemLocation&        anItem,
@@ -82,10 +82,10 @@ public:
   }
 
   //! Return true if this list is empty
-  Standard_Boolean IsEmpty() const noexcept { return myNode.IsNull(); }
+  Standard_Boolean IsEmpty() const { return myNode.IsNull(); }
 
   //! Sets the list to be empty.
-  void Clear() noexcept { myNode.Nullify(); }
+  void Clear() { myNode.Nullify(); }
 
   //! Destructor
   ~TopLoc_SListOfItemLocation() { Clear(); }
@@ -98,14 +98,6 @@ public:
   //! list the tail is the list itself.
   Standard_EXPORT const TopLoc_SListOfItemLocation& Tail() const;
 
-  //! Returns the cached hash of this list.
-  //! For an empty list, returns 0.
-  Standard_EXPORT size_t HashCode() const noexcept;
-
-  //! Returns the underlying node handle.
-  //! Used for fast identity comparison between lists.
-  const Handle(TopLoc_SListNodeOfItemLocation)& Node() const noexcept { return myNode; }
-
   //! Replaces the list by a list with <anItem> as Value
   //! and the list <me> as tail.
   void Construct(const TopLoc_ItemLocation& anItem)
@@ -118,7 +110,7 @@ public:
 
   //! Returns True if the iterator has a current value.
   //! This is !IsEmpty()
-  Standard_Boolean More() const noexcept { return !IsEmpty(); }
+  Standard_Boolean More() const { return !IsEmpty(); }
 
   //! Moves the iterator to the next object in the list.
   //! If the iterator is empty it will stay empty. This is ToTail()
index 2f1a9831f1f68e0da3c8b58a56191a34e8cf1d75..5bc29c2ab9d5bacd80eee9ce5b28e687dcb0a933 100644 (file)
@@ -35,5 +35,6 @@ set(OCCT_TKernel_GTests_FILES
   Standard_Handle_Test.cxx
   TCollection_AsciiString_Test.cxx
   TCollection_ExtendedString_Test.cxx
+  TopLoc_Location_Test.cxx
   UnitsAPI_Test.cxx
 )
diff --git a/src/FoundationClasses/TKernel/GTests/TopLoc_Location_Test.cxx b/src/FoundationClasses/TKernel/GTests/TopLoc_Location_Test.cxx
new file mode 100644 (file)
index 0000000..890e3fa
--- /dev/null
@@ -0,0 +1,91 @@
+// 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 <TopLoc_Location.hxx>
+#include <TopoDS_Shape.hxx>
+#include <TopoDS_Vertex.hxx>
+#include <TopoDS.hxx>
+#include <BRepBuilderAPI_MakeVertex.hxx>
+#include <BRep_Tool.hxx>
+#include <gp.hxx>
+#include <gp_Trsf.hxx>
+#include <gp_Vec.hxx>
+#include <gp_Pnt.hxx>
+#include <OSD_Parallel.hxx>
+
+#include <gtest/gtest.h>
+
+#include <atomic>
+#include <vector>
+
+namespace
+{
+//! Functor for testing concurrent access to TopLoc_Location::Transformation()
+struct TopLocTransformFunctor
+{
+  TopLocTransformFunctor(const std::vector<TopoDS_Shape>& theShapeVec)
+      : myShapeVec(&theShapeVec),
+        myIsRaceDetected(0)
+  {
+  }
+
+  void operator()(size_t i) const
+  {
+    if (!myIsRaceDetected)
+    {
+      const TopoDS_Vertex& aVertex = TopoDS::Vertex(myShapeVec->at(i));
+      gp_Pnt               aPoint  = BRep_Tool::Pnt(aVertex);
+      if (aPoint.X() != static_cast<double>(i))
+      {
+        ++myIsRaceDetected;
+      }
+    }
+  }
+
+  const std::vector<TopoDS_Shape>* myShapeVec;
+  mutable std::atomic<int>         myIsRaceDetected;
+};
+} // namespace
+
+TEST(TopLoc_Location_Test, OCC25545_ConcurrentTransformationAccess)
+{
+  // Bug OCC25545: TopLoc_Location::Transformation() provokes data races
+  // This test verifies that concurrent access to TopLoc_Location::Transformation()
+  // does not cause data races or incorrect geometry results
+
+  // Place vertices in a vector, giving the i-th vertex the
+  // transformation that translates it on the vector (i,0,0) from the origin
+  Standard_Integer             n = 1000;
+  std::vector<TopoDS_Shape>    aShapeVec(n);
+  std::vector<TopLoc_Location> aLocVec(n);
+  TopoDS_Shape                 aShape = BRepBuilderAPI_MakeVertex(gp::Origin());
+  aShapeVec[0]                        = aShape;
+
+  for (Standard_Integer i = 1; i < n; ++i)
+  {
+    gp_Trsf aTrsf;
+    aTrsf.SetTranslation(gp_Vec(1, 0, 0));
+    aLocVec[i]   = aLocVec[i - 1] * aTrsf;
+    aShapeVec[i] = aShape.Moved(aLocVec[i]);
+  }
+
+  // Evaluator function will access vertices geometry concurrently
+  TopLocTransformFunctor aFunc(aShapeVec);
+
+  // Process concurrently
+  OSD_Parallel::For(0, n, aFunc);
+
+  // Verify no data race was detected
+  EXPECT_EQ(aFunc.myIsRaceDetected, 0)
+    << "Data race detected in concurrent TopLoc_Location::Transformation() access";
+}