diff --git a/src/DataExchange/TKDESTEP/GTests/FILES.cmake b/src/DataExchange/TKDESTEP/GTests/FILES.cmake index e1c97bde20..008a21f8a1 100644 --- a/src/DataExchange/TKDESTEP/GTests/FILES.cmake +++ b/src/DataExchange/TKDESTEP/GTests/FILES.cmake @@ -2,4 +2,14 @@ set(OCCT_TKDESTEP_GTests_FILES_LOCATION "${CMAKE_CURRENT_LIST_DIR}") set(OCCT_TKDESTEP_GTests_FILES + MergeSTEPEntities_Axis2Placement3dProcessor_Test.cxx + MergeSTEPEntities_BaseTestFixture.hxx + MergeSTEPEntities_CartesianPointProcessor_Test.cxx + MergeSTEPEntities_CircleProcessor_Test.cxx + MergeSTEPEntities_DirectionProcessor_Test.cxx + MergeSTEPEntities_LineProcessor_Test.cxx + MergeSTEPEntities_PlaneProcessor_Test.cxx + MergeSTEPEntities_Merger_Test.cxx + + MergeSTEPEntities_VectorProcessor_Test.cxx ) diff --git a/src/DataExchange/TKDESTEP/GTests/MergeSTEPEntities_Axis2Placement3dProcessor_Test.cxx b/src/DataExchange/TKDESTEP/GTests/MergeSTEPEntities_Axis2Placement3dProcessor_Test.cxx new file mode 100644 index 0000000000..6b27f435ac --- /dev/null +++ b/src/DataExchange/TKDESTEP/GTests/MergeSTEPEntities_Axis2Placement3dProcessor_Test.cxx @@ -0,0 +1,461 @@ +// 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 "MergeSTEPEntities_BaseTestFixture.hxx" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class MergeSTEPEntities_Axis2Placement3dProcessorTest : public MergeSTEPEntities_BaseTestFixture +{ +protected: + //! Perform removal of duplicate entities. + TColStd_MapOfTransient replaceDuplicateAxis2Placement3ds() + { + MergeSTEPEntities_Axis2Placement3dProcessor aProcessor(myWS); + for (Standard_Integer anIndex = 1; anIndex <= myWS->Model()->NbEntities(); ++anIndex) + { + aProcessor.ProcessEntity(myWS->Model()->Value(anIndex)); + } + + TColStd_MapOfTransient aRemovedEntities; + aProcessor.Perform(aRemovedEntities); + return aRemovedEntities; + } +}; + +// Check that Axis2Placement3ds with the same coordinates and different names are not merged. +TEST_F(MergeSTEPEntities_Axis2Placement3dProcessorTest, DifferentNames) +{ + // Creating Axis2Placement3ds. + Handle(StepGeom_Axis2Placement3d) anAxis1 = addAxis2Placement3d("Axis1"); + Handle(StepGeom_Axis2Placement3d) anAxis2 = addAxis2Placement3d("Axis2"); + + // Creating a plane containing the first Axis2Placement3d. + Handle(StepGeom_Plane) aPlane1 = new StepGeom_Plane; + aPlane1->Init(new TCollection_HAsciiString, anAxis1); + addToModel(aPlane1); + + // Creating a plane containing the second Axis2Placement3d. + Handle(StepGeom_Plane) aPlane2 = new StepGeom_Plane; + aPlane2->Init(new TCollection_HAsciiString, anAxis2); + addToModel(aPlane2); + + // Performing removal of duplicate Axis2Placement3ds. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateAxis2Placement3ds(); + + // Check that nothing was removed. + EXPECT_TRUE(aRemovedEntities.IsEmpty()); +} + +// Check that equal Axis2Placement3ds are merged for StepShape_GeometricCurveSet. +TEST_F(MergeSTEPEntities_Axis2Placement3dProcessorTest, StepGeom_Plane) +{ + // Creating Axis2Placement3ds. + Handle(StepGeom_Axis2Placement3d) anAxis1 = addAxis2Placement3d(); + Handle(StepGeom_Axis2Placement3d) anAxis2 = addAxis2Placement3d(); + + // Creating a plane containing the first Axis2Placement3d. + Handle(StepGeom_Plane) aPlane1 = new StepGeom_Plane; + aPlane1->Init(new TCollection_HAsciiString, anAxis1); + addToModel(aPlane1); + + // Creating a plane containing the second Axis2Placement3d. + Handle(StepGeom_Plane) aPlane2 = new StepGeom_Plane; + aPlane2->Init(new TCollection_HAsciiString, anAxis2); + addToModel(aPlane2); + + // Performing removal of duplicate Axis2Placement3ds. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateAxis2Placement3ds(); + + // Check that one Axis2Placement3d was removed. + EXPECT_EQ(aRemovedEntities.Extent(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(anAxis1) || aRemovedEntities.Contains(anAxis2)); +} + +// Check that equal Axis2Placement3ds are merged for StepRepr_ItemDefinedTransformation. +TEST_F(MergeSTEPEntities_Axis2Placement3dProcessorTest, StepRepr_ItemDefinedTransformation) +{ + // Creating Axis2Placement3ds. + Handle(StepGeom_Axis2Placement3d) anAxis1 = addAxis2Placement3d(); + Handle(StepGeom_Axis2Placement3d) anAxis2 = addAxis2Placement3d(); + Handle(StepGeom_Axis2Placement3d) anAxis3 = addAxis2Placement3d(nullptr, gp_XYZ(1., 1., 1.)); + + // Creating ItemDefinedTransformation containing the first Axis2Placement3d. + Handle(StepRepr_ItemDefinedTransformation) aItem1 = new StepRepr_ItemDefinedTransformation; + aItem1->Init(new TCollection_HAsciiString, new TCollection_HAsciiString, anAxis1, anAxis3); + addToModel(aItem1); + + // Creating ItemDefinedTransformation containing the second Axis2Placement3d. + Handle(StepRepr_ItemDefinedTransformation) aItem2 = new StepRepr_ItemDefinedTransformation; + aItem1->Init(new TCollection_HAsciiString, new TCollection_HAsciiString, anAxis2, anAxis3); + addToModel(aItem2); + + // Performing removal of duplicate Axis2Placement3ds. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateAxis2Placement3ds(); + + // Check that one Axis2Placement3d was removed. + EXPECT_EQ(aRemovedEntities.Extent(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(anAxis1) || aRemovedEntities.Contains(anAxis2)); +} + +// Check that equal Axis2Placement3ds are merged for StepGeom_CylindricalSurface. +TEST_F(MergeSTEPEntities_Axis2Placement3dProcessorTest, StepGeom_CylindricalSurface) +{ + // Creating Axis2Placement3ds. + Handle(StepGeom_Axis2Placement3d) anAxis1 = addAxis2Placement3d(); + Handle(StepGeom_Axis2Placement3d) anAxis2 = addAxis2Placement3d(); + + // Creating a cylindrical surface containing the first Axis2Placement3d. + Handle(StepGeom_CylindricalSurface) aCylindricalSurface1 = new StepGeom_CylindricalSurface; + aCylindricalSurface1->Init(new TCollection_HAsciiString, anAxis1, 1.0); + addToModel(aCylindricalSurface1); + + // Creating a cylindrical surface containing the second Axis2Placement3d. + Handle(StepGeom_CylindricalSurface) aCylindricalSurface2 = new StepGeom_CylindricalSurface; + aCylindricalSurface2->Init(new TCollection_HAsciiString, anAxis2, 1.0); + addToModel(aCylindricalSurface2); + + // Performing removal of duplicate Axis2Placement3ds. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateAxis2Placement3ds(); + + // Check that one Axis2Placement3d was removed. + EXPECT_EQ(aRemovedEntities.Extent(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(anAxis1) || aRemovedEntities.Contains(anAxis2)); +} + +// Check that equal Axis2Placement3ds are merged for StepShape_ShapeRepresentation. +TEST_F(MergeSTEPEntities_Axis2Placement3dProcessorTest, StepShape_ShapeRepresentation) +{ + // Creating Axis2Placement3ds. + Handle(StepGeom_Axis2Placement3d) anAxis1 = addAxis2Placement3d(); + Handle(StepGeom_Axis2Placement3d) anAxis2 = addAxis2Placement3d(); + + // Creating a shape representation containing the first Axis2Placement3d. + Handle(StepRepr_HArray1OfRepresentationItem) aItems1 = + new StepRepr_HArray1OfRepresentationItem(1, 1); + aItems1->SetValue(1, anAxis1); + Handle(StepShape_ShapeRepresentation) aShapeRepresentation1 = new StepShape_ShapeRepresentation; + aShapeRepresentation1->Init(new TCollection_HAsciiString, + aItems1, + new StepRepr_RepresentationContext); + addToModel(aShapeRepresentation1); + + // Creating a shape representation containing the second Axis2Placement3d. + Handle(StepRepr_HArray1OfRepresentationItem) aItems2 = + new StepRepr_HArray1OfRepresentationItem(1, 1); + aItems2->SetValue(1, anAxis2); + Handle(StepShape_ShapeRepresentation) aShapeRepresentation2 = new StepShape_ShapeRepresentation; + aShapeRepresentation2->Init(new TCollection_HAsciiString, + aItems2, + new StepRepr_RepresentationContext); + addToModel(aShapeRepresentation2); + + // Performing removal of duplicate Axis2Placement3ds. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateAxis2Placement3ds(); + + // Check that one Axis2Placement3d was removed. + EXPECT_EQ(aRemovedEntities.Extent(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(anAxis1) || aRemovedEntities.Contains(anAxis2)); +} + +// Check that equal Axis2Placement3ds are merged for StepRepr_ConstructiveGeometryRepresentation. +TEST_F(MergeSTEPEntities_Axis2Placement3dProcessorTest, StepRepr_ConstructiveGeometryRepresentation) +{ + // Creating Axis2Placement3ds. + Handle(StepGeom_Axis2Placement3d) anAxis1 = addAxis2Placement3d(); + Handle(StepGeom_Axis2Placement3d) anAxis2 = addAxis2Placement3d(); + + // Creating a constructive geometry representation containing the first Axis2Placement3d. + Handle(StepRepr_HArray1OfRepresentationItem) aItems1 = + new StepRepr_HArray1OfRepresentationItem(1, 1); + aItems1->SetValue(1, anAxis1); + Handle(StepRepr_ConstructiveGeometryRepresentation) aConstructiveGeometryRepresentation1 = + new StepRepr_ConstructiveGeometryRepresentation; + aConstructiveGeometryRepresentation1->Init(new TCollection_HAsciiString, + aItems1, + new StepRepr_RepresentationContext); + addToModel(aConstructiveGeometryRepresentation1); + + // Creating a constructive geometry representation containing the second Axis2Placement3d. + Handle(StepRepr_HArray1OfRepresentationItem) aItems2 = + new StepRepr_HArray1OfRepresentationItem(1, 1); + aItems2->SetValue(1, anAxis2); + Handle(StepRepr_ConstructiveGeometryRepresentation) aConstructiveGeometryRepresentation2 = + new StepRepr_ConstructiveGeometryRepresentation; + aConstructiveGeometryRepresentation2->Init(new TCollection_HAsciiString, + aItems2, + new StepRepr_RepresentationContext); + addToModel(aConstructiveGeometryRepresentation2); + + // Performing removal of duplicate Axis2Placement3ds. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateAxis2Placement3ds(); + + // Check that one Axis2Placement3d was removed. + EXPECT_EQ(aRemovedEntities.Extent(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(anAxis1) || aRemovedEntities.Contains(anAxis2)); +} + +// Check that equal Axis2Placement3ds are merged for StepGeom_Circle. +TEST_F(MergeSTEPEntities_Axis2Placement3dProcessorTest, StepGeom_Circle) +{ + // Creating Axis2Placement3ds. + Handle(StepGeom_Axis2Placement3d) anAxis1 = addAxis2Placement3d(); + Handle(StepGeom_Axis2Placement3d) anAxis2 = addAxis2Placement3d(); + + // Creating a circle containing the first Axis2Placement3d. + StepGeom_Axis2Placement aSelector1; + aSelector1.SetValue(anAxis1); + Handle(StepGeom_Circle) aCircle1 = new StepGeom_Circle; + aCircle1->Init(new TCollection_HAsciiString, aSelector1, 1.0); + addToModel(aCircle1); + + // Creating a circle containing the second Axis2Placement3d. + StepGeom_Axis2Placement aSelector2; + aSelector2.SetValue(anAxis2); + Handle(StepGeom_Circle) aCircle2 = new StepGeom_Circle; + aCircle2->Init(new TCollection_HAsciiString, aSelector2, 1.0); + addToModel(aCircle2); + + // Performing removal of duplicate Axis2Placement3ds. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateAxis2Placement3ds(); + + // Check that one Axis2Placement3d was removed. + EXPECT_EQ(aRemovedEntities.Extent(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(anAxis1) || aRemovedEntities.Contains(anAxis2)); +} + +// Check that equal Axis2Placement3ds are merged for StepVisual_PresentationLayerAssignment. +TEST_F(MergeSTEPEntities_Axis2Placement3dProcessorTest, StepVisual_PresentationLayerAssignment) +{ + // Creating Axis2Placement3ds. + Handle(StepGeom_Axis2Placement3d) anAxis1 = addAxis2Placement3d(); + Handle(StepGeom_Axis2Placement3d) anAxis2 = addAxis2Placement3d(); + + // Creating a presentation layer assignment containing the first Axis2Placement3d. + Handle(StepVisual_HArray1OfLayeredItem) aAssignedItems1 = + new StepVisual_HArray1OfLayeredItem(1, 1); + StepVisual_LayeredItem aLayeredItem1; + aLayeredItem1.SetValue(anAxis1); + aAssignedItems1->SetValue(1, aLayeredItem1); + Handle(StepVisual_PresentationLayerAssignment) aPresentationLayerAssignment1 = + new StepVisual_PresentationLayerAssignment; + aPresentationLayerAssignment1->Init(new TCollection_HAsciiString, + new TCollection_HAsciiString, + aAssignedItems1); + addToModel(aPresentationLayerAssignment1); + + // Creating a presentation layer assignment containing the second Axis2Placement3d. + Handle(StepVisual_HArray1OfLayeredItem) aAssignedItems2 = + new StepVisual_HArray1OfLayeredItem(1, 1); + StepVisual_LayeredItem aLayeredItem2; + aLayeredItem2.SetValue(anAxis2); + aAssignedItems2->SetValue(1, aLayeredItem2); + Handle(StepVisual_PresentationLayerAssignment) aPresentationLayerAssignment2 = + new StepVisual_PresentationLayerAssignment; + aPresentationLayerAssignment2->Init(new TCollection_HAsciiString, + new TCollection_HAsciiString, + aAssignedItems2); + addToModel(aPresentationLayerAssignment2); + + // Performing removal of duplicate Axis2Placement3ds. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateAxis2Placement3ds(); + + // Check that one Axis2Placement3d was removed. + EXPECT_EQ(aRemovedEntities.Extent(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(anAxis1) || aRemovedEntities.Contains(anAxis2)); +} + +// Check that equal Axis2Placement3ds are merged for StepVisual_StyledItem. +TEST_F(MergeSTEPEntities_Axis2Placement3dProcessorTest, StepVisual_StyledItem) +{ + // Creating Axis2Placement3ds. + Handle(StepGeom_Axis2Placement3d) anAxis1 = addAxis2Placement3d(); + Handle(StepGeom_Axis2Placement3d) anAxis2 = addAxis2Placement3d(); + + // Creating a styled item containing the first Axis2Placement3d. + Handle(StepVisual_StyledItem) aStiledItem1 = new StepVisual_StyledItem; + aStiledItem1->Init(new TCollection_HAsciiString, + new StepVisual_HArray1OfPresentationStyleAssignment(1, 1), + anAxis1); + addToModel(aStiledItem1); + + // Creating a styled item containing the second Axis2Placement3d. + Handle(StepVisual_StyledItem) aStiledItem2 = new StepVisual_StyledItem; + aStiledItem2->Init(new TCollection_HAsciiString, + new StepVisual_HArray1OfPresentationStyleAssignment(1, 1), + anAxis2); + addToModel(aStiledItem2); + + // Performing removal of duplicate Axis2Placement3ds. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateAxis2Placement3ds(); + + // Check that one Axis2Placement3d was removed. + EXPECT_EQ(aRemovedEntities.Extent(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(anAxis1) || aRemovedEntities.Contains(anAxis2)); +} + +// Check that equal Axis2Placement3ds are merged for StepGeom_Ellipse. +TEST_F(MergeSTEPEntities_Axis2Placement3dProcessorTest, StepGeom_Ellipse) +{ + // Creating Axis2Placement3ds. + Handle(StepGeom_Axis2Placement3d) anAxis1 = addAxis2Placement3d(); + Handle(StepGeom_Axis2Placement3d) anAxis2 = addAxis2Placement3d(); + + // Creating an ellipse containing the first Axis2Placement3d. + StepGeom_Axis2Placement aSelector1; + aSelector1.SetValue(anAxis1); + Handle(StepGeom_Ellipse) aEllipse1 = new StepGeom_Ellipse; + aEllipse1->Init(new TCollection_HAsciiString, aSelector1, 1.0, 2.0); + addToModel(aEllipse1); + + // Creating an ellipse containing the second Axis2Placement3d. + StepGeom_Axis2Placement aSelector2; + aSelector2.SetValue(anAxis2); + Handle(StepGeom_Ellipse) aEllipse2 = new StepGeom_Ellipse; + aEllipse2->Init(new TCollection_HAsciiString, aSelector2, 1.0, 2.0); + addToModel(aEllipse2); + + // Performing removal of duplicate Axis2Placement3ds. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateAxis2Placement3ds(); + + // Check that one Axis2Placement3d was removed. + EXPECT_EQ(aRemovedEntities.Extent(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(anAxis1) || aRemovedEntities.Contains(anAxis2)); +} + +// Check that equal Axis2Placement3ds are merged for StepGeom_ConicalSurface. +TEST_F(MergeSTEPEntities_Axis2Placement3dProcessorTest, StepGeom_ConicalSurface) +{ + // Creating Axis2Placement3ds. + Handle(StepGeom_Axis2Placement3d) anAxis1 = addAxis2Placement3d(); + Handle(StepGeom_Axis2Placement3d) anAxis2 = addAxis2Placement3d(); + + // Creating a conical surface containing the first Axis2Placement3d. + Handle(StepGeom_ConicalSurface) aConicalSurface1 = new StepGeom_ConicalSurface; + aConicalSurface1->Init(new TCollection_HAsciiString, anAxis1, 1.0, 1.0); + addToModel(aConicalSurface1); + + // Creating a conical surface containing the second Axis2Placement3d. + Handle(StepGeom_ConicalSurface) aConicalSurface2 = new StepGeom_ConicalSurface; + aConicalSurface2->Init(new TCollection_HAsciiString, anAxis2, 1.0, 1.0); + addToModel(aConicalSurface2); + + // Performing removal of duplicate Axis2Placement3ds. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateAxis2Placement3ds(); + + // Check that one Axis2Placement3d was removed. + EXPECT_EQ(aRemovedEntities.Extent(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(anAxis1) || aRemovedEntities.Contains(anAxis2)); +} + +// Check that equal Axis2Placement3ds are merged for StepGeom_ToroidalSurface. +TEST_F(MergeSTEPEntities_Axis2Placement3dProcessorTest, StepGeom_ToroidalSurface) +{ + // Creating Axis2Placement3ds. + Handle(StepGeom_Axis2Placement3d) anAxis1 = addAxis2Placement3d(); + Handle(StepGeom_Axis2Placement3d) anAxis2 = addAxis2Placement3d(); + + // Creating a toroidal surface containing the first Axis2Placement3d. + Handle(StepGeom_ToroidalSurface) aToroidalSurface1 = new StepGeom_ToroidalSurface; + aToroidalSurface1->Init(new TCollection_HAsciiString, anAxis1, 1.0, 1.0); + addToModel(aToroidalSurface1); + + // Creating a toroidal surface containing the second Axis2Placement3d. + Handle(StepGeom_ToroidalSurface) aToroidalSurface2 = new StepGeom_ToroidalSurface; + aToroidalSurface2->Init(new TCollection_HAsciiString, anAxis2, 1.0, 1.0); + addToModel(aToroidalSurface2); + + // Performing removal of duplicate Axis2Placement3ds. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateAxis2Placement3ds(); + + // Check that one Axis2Placement3d was removed. + EXPECT_EQ(aRemovedEntities.Extent(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(anAxis1) || aRemovedEntities.Contains(anAxis2)); +} + +// Check that equal Axis2Placement3ds are merged for StepShape_AdvancedBrepShapeRepresentation. +TEST_F(MergeSTEPEntities_Axis2Placement3dProcessorTest, StepShape_AdvancedBrepShapeRepresentation) +{ + // Creating Axis2Placement3ds. + Handle(StepGeom_Axis2Placement3d) anAxis1 = addAxis2Placement3d(); + Handle(StepGeom_Axis2Placement3d) anAxis2 = addAxis2Placement3d(); + + // Creating a shape representation containing the first Axis2Placement3d. + Handle(StepRepr_HArray1OfRepresentationItem) aItems1 = + new StepRepr_HArray1OfRepresentationItem(1, 1); + aItems1->SetValue(1, anAxis1); + Handle(StepShape_AdvancedBrepShapeRepresentation) aShapeRepresentation1 = + new StepShape_AdvancedBrepShapeRepresentation; + aShapeRepresentation1->Init(new TCollection_HAsciiString, + aItems1, + new StepRepr_RepresentationContext); + addToModel(aShapeRepresentation1); + + // Creating a shape representation containing the second Axis2Placement3d. + Handle(StepRepr_HArray1OfRepresentationItem) aItems2 = + new StepRepr_HArray1OfRepresentationItem(1, 1); + aItems2->SetValue(1, anAxis2); + Handle(StepShape_AdvancedBrepShapeRepresentation) aShapeRepresentation2 = + new StepShape_AdvancedBrepShapeRepresentation; + aShapeRepresentation2->Init(new TCollection_HAsciiString, + aItems2, + new StepRepr_RepresentationContext); + addToModel(aShapeRepresentation2); + + // Performing removal of duplicate Axis2Placement3ds. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateAxis2Placement3ds(); + + // Check that one Axis2Placement3d was removed. + EXPECT_EQ(aRemovedEntities.Extent(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(anAxis1) || aRemovedEntities.Contains(anAxis2)); +} + +// Check that equal Axis2Placement3ds are merged for StepGeom_SphericalSurface. +TEST_F(MergeSTEPEntities_Axis2Placement3dProcessorTest, StepGeom_SphericalSurface) +{ + // Creating Axis2Placement3ds. + Handle(StepGeom_Axis2Placement3d) anAxis1 = addAxis2Placement3d(); + Handle(StepGeom_Axis2Placement3d) anAxis2 = addAxis2Placement3d(); + + // Creating a spherical surface containing the first Axis2Placement3d. + Handle(StepGeom_SphericalSurface) aSphericalSurface1 = new StepGeom_SphericalSurface; + aSphericalSurface1->Init(new TCollection_HAsciiString, anAxis1, 1.0); + addToModel(aSphericalSurface1); + + // Creating a spherical surface containing the second Axis2Placement3d. + Handle(StepGeom_SphericalSurface) aSphericalSurface2 = new StepGeom_SphericalSurface; + aSphericalSurface2->Init(new TCollection_HAsciiString, anAxis2, 1.0); + addToModel(aSphericalSurface2); + + // Performing removal of duplicate Axis2Placement3ds. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateAxis2Placement3ds(); + + // Check that one Axis2Placement3d was removed. + EXPECT_EQ(aRemovedEntities.Extent(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(anAxis1) || aRemovedEntities.Contains(anAxis2)); +} diff --git a/src/DataExchange/TKDESTEP/GTests/MergeSTEPEntities_BaseTestFixture.hxx b/src/DataExchange/TKDESTEP/GTests/MergeSTEPEntities_BaseTestFixture.hxx new file mode 100644 index 0000000000..f0d38929ab --- /dev/null +++ b/src/DataExchange/TKDESTEP/GTests/MergeSTEPEntities_BaseTestFixture.hxx @@ -0,0 +1,194 @@ +// 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 _MergeSTEPEntities_BaseTestFixture_HeaderFile +#define _MergeSTEPEntities_BaseTestFixture_HeaderFile + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +class MergeSTEPEntities_BaseTestFixture : public testing::Test +{ +protected: + // Initialize the work session and model. + MergeSTEPEntities_BaseTestFixture() + : myWS() + { + STEPControl_Controller::Init(); + myWS = new XSControl_WorkSession; + myWS->SelectNorm("STEP"); + myWS->SetModel(myWS->NormAdaptor()->NewModel()); + } + + // Add a Cartesian point to the model. + // @param theName the name of the Cartesian point. + // @param thePoint the coordinates of the Cartesian point. + // @return the added Cartesian point. + Handle(StepGeom_CartesianPoint) addCartesianPoint(const char* theName = nullptr, + const gp_XYZ& thePoint = gp_XYZ(0., + 0., + 0.)) const + { + const Handle(StepGeom_CartesianPoint) aCartesianPoint = new StepGeom_CartesianPoint; + const Handle(TCollection_HAsciiString) aName = + theName ? new TCollection_HAsciiString(theName) : new TCollection_HAsciiString(); + aCartesianPoint->Init3D(aName, thePoint.X(), thePoint.Y(), thePoint.Z()); + myWS->Model()->AddWithRefs(aCartesianPoint); + return aCartesianPoint; + } + + // Add a direction to the model. + // @param theName the name of the direction. + // @param theDirection the direction ratios. + // @return the added direction. + Handle(StepGeom_Direction) addDirection(const char* theName = nullptr, + const gp_XYZ& theDirection = gp_XYZ(0., 0., 1.)) const + { + const Handle(StepGeom_Direction) aDirection = new StepGeom_Direction; + const Handle(TCollection_HAsciiString) aName = + theName ? new TCollection_HAsciiString(theName) : new TCollection_HAsciiString(); + Handle(TColStd_HArray1OfReal) aDirectionRatios = new TColStd_HArray1OfReal(1, 3); + aDirectionRatios->SetValue(1, theDirection.X()); + aDirectionRatios->SetValue(2, theDirection.Y()); + aDirectionRatios->SetValue(3, theDirection.Z()); + aDirection->Init(aName, aDirectionRatios); + myWS->Model()->AddWithRefs(aDirection); + return aDirection; + } + + // Add a vector to the model. + // @param theName the name of the vector. + // @param theOrientation the orientation of the vector. + // @param aMagnitude the magnitude of the vector. + // @return the added vector. + Handle(StepGeom_Vector) addVector(const char* theName = nullptr, + const gp_XYZ& theOrientation = gp_XYZ(0., 0., 1.), + const double aMagnitude = 1.) const + { + const Handle(StepGeom_Vector) aVector = new StepGeom_Vector; + const Handle(TCollection_HAsciiString) aName = + theName ? new TCollection_HAsciiString(theName) : new TCollection_HAsciiString(); + aVector->Init(aName, addDirection(nullptr, theOrientation), aMagnitude); + myWS->Model()->AddWithRefs(aVector); + return aVector; + } + + // Add an Axis2Placement3d to the model. + // @param theName the name of the Axis2Placement3d. + // @param theLocation the location of the Axis2Placement3d. + // @param theAxis the axis of the Axis2Placement3d. + // @param theRefDirection the reference direction of the Axis2Placement3d. + // @return the added Axis2Placement3d. + Handle(StepGeom_Axis2Placement3d) addAxis2Placement3d( + const char* theName = nullptr, + const gp_XYZ& theLocation = gp_XYZ(0., 0., 0.), + const gp_XYZ& theAxis = gp_XYZ(0., 0., 1.), + const gp_XYZ& theRefDirection = gp_XYZ(0., 1., 0.)) const + { + const Handle(StepGeom_Axis2Placement3d) aAxis2Placement3d = new StepGeom_Axis2Placement3d; + const Handle(TCollection_HAsciiString) aName = + theName ? new TCollection_HAsciiString(theName) : new TCollection_HAsciiString(); + aAxis2Placement3d->Init(aName, + addCartesianPoint(nullptr, theLocation), + true, + addDirection(nullptr, theAxis), + true, + addDirection(nullptr, theRefDirection)); + myWS->Model()->AddWithRefs(aAxis2Placement3d); + return aAxis2Placement3d; + } + + // Add a line to the model. + // @param theName the name of the line. + // @param theLocation the location of the line. + // @param theOrientation the orientation of the line vector. + // @param theMagnitude the magnitude of the line vector. + // @return the added line. + Handle(StepGeom_Line) addLine(const char* theName = nullptr, + const gp_XYZ& theLocation = gp_XYZ(0., 0., 0.), + const gp_XYZ& theOrientation = gp_XYZ(0., 0., 1.), + const double aMagnitude = 1.) const + { + const Handle(StepGeom_Line) aLine = new StepGeom_Line; + const Handle(TCollection_HAsciiString) aName = + theName ? new TCollection_HAsciiString(theName) : new TCollection_HAsciiString(); + aLine->Init(aName, + addCartesianPoint(nullptr, theLocation), + addVector(nullptr, theOrientation, aMagnitude)); + myWS->Model()->AddWithRefs(aLine); + return aLine; + } + + // Add a circle to the model. + // @param theName the name of the circle. + // @param theLocation the location of the circle. + // @param theAxis the axis of the circle. + // @param theRefDirection the reference direction of the circle. + // @param theRadius the radius of the circle. + // @return the added circle. + Handle(StepGeom_Circle) addCircle(const char* theName = nullptr, + const gp_XYZ& theLocation = gp_XYZ(0., 0., 0.), + const gp_XYZ& theAxis = gp_XYZ(0., 0., 1.), + const gp_XYZ& theRefDirection = gp_XYZ(0., 1., 0.), + const double theRadius = 1.) const + { + const Handle(StepGeom_Circle) aCircle = new StepGeom_Circle; + const Handle(TCollection_HAsciiString) aName = + theName ? new TCollection_HAsciiString(theName) : new TCollection_HAsciiString(); + StepGeom_Axis2Placement aSelector; + aSelector.SetValue(addAxis2Placement3d(nullptr, theLocation, theAxis, theRefDirection)); + aCircle->Init(aName, aSelector, theRadius); + myWS->Model()->AddWithRefs(aCircle); + return aCircle; + } + + // Add a plane to the model. + // @param theName the name of the plane. + // @param theLocation the location of the plane. + // @param theAxis the axis of the plane. + // @param theRefDirection the reference direction of the plane. + // @return the added plane. + Handle(StepGeom_Plane) addPlane(const char* theName = nullptr, + const gp_XYZ& theLocation = gp_XYZ(0., 0., 0.), + const gp_XYZ& theAxis = gp_XYZ(0., 0., 1.), + const gp_XYZ& theRefDirection = gp_XYZ(0., 1., 0.)) const + { + const Handle(StepGeom_Plane) aPlane = new StepGeom_Plane; + const Handle(TCollection_HAsciiString) aName = + theName ? new TCollection_HAsciiString(theName) : new TCollection_HAsciiString(); + aPlane->Init(aName, addAxis2Placement3d(nullptr, theLocation, theAxis, theRefDirection)); + myWS->Model()->AddWithRefs(aPlane); + return aPlane; + } + + // Add an entity to the model. + // @param theEntity the entity to add. + void addToModel(const Handle(Standard_Transient)& theEntity) const + { + myWS->Model()->AddWithRefs(theEntity); + } + +protected: + Handle(XSControl_WorkSession) myWS; +}; + +#endif // _MergeSTEPEntities_BaseTestFixture_HeaderFile diff --git a/src/DataExchange/TKDESTEP/GTests/MergeSTEPEntities_CartesianPointProcessor_Test.cxx b/src/DataExchange/TKDESTEP/GTests/MergeSTEPEntities_CartesianPointProcessor_Test.cxx new file mode 100644 index 0000000000..0a45609828 --- /dev/null +++ b/src/DataExchange/TKDESTEP/GTests/MergeSTEPEntities_CartesianPointProcessor_Test.cxx @@ -0,0 +1,594 @@ +// 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 "MergeSTEPEntities_BaseTestFixture.hxx" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class MergeSTEPEntities_CartesianPointProcessorTest : public MergeSTEPEntities_BaseTestFixture +{ +protected: + // Perform removal of duplicate Cartesian points. + TColStd_MapOfTransient replaceDuplicateCartesianPoints() + { + MergeSTEPEntities_CartesianPointProcessor aProcessor(myWS); + for (Standard_Integer anIndex = 1; anIndex <= myWS->Model()->NbEntities(); ++anIndex) + { + aProcessor.ProcessEntity(myWS->Model()->Value(anIndex)); + } + + TColStd_MapOfTransient aRemovedEntities; + aProcessor.Perform(aRemovedEntities); + return aRemovedEntities; + } +}; + +// Check that points with the same coordinates and different names are not merged. +TEST_F(MergeSTEPEntities_CartesianPointProcessorTest, DifferentNames) +{ + // Creating Cartesian points. + Handle(StepGeom_CartesianPoint) aPt1 = addCartesianPoint("FirstPt"); + Handle(StepGeom_CartesianPoint) aPt2 = addCartesianPoint("SecondPt"); + + // Creating direction. + Handle(TColStd_HArray1OfReal) aDirCoords = new TColStd_HArray1OfReal(1, 3); + aDirCoords->SetValue(1, 0.); + aDirCoords->SetValue(2, 0.); + aDirCoords->SetValue(3, 1.); + Handle(StepGeom_Direction) aDir = new StepGeom_Direction; + aDir->Init(new TCollection_HAsciiString, aDirCoords); + addToModel(aDir); + + // Creating axis containing the first Cartesian point. + Handle(StepGeom_Axis2Placement3d) aFirstAxis = new StepGeom_Axis2Placement3d; + aFirstAxis + ->Init(new TCollection_HAsciiString, aPt1, Standard_True, aDir, Standard_False, nullptr); + addToModel(aFirstAxis); + + // Creating axis containing the second Cartesian point. + Handle(StepGeom_Axis2Placement3d) aSecondAxis = new StepGeom_Axis2Placement3d; + aSecondAxis + ->Init(new TCollection_HAsciiString, aPt2, Standard_True, aDir, Standard_False, nullptr); + addToModel(aSecondAxis); + + // Performing removal of duplicate Cartesian points. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateCartesianPoints(); + + // Check that nothing was removed. + EXPECT_TRUE(aRemovedEntities.IsEmpty()); +} + +// Check that points with the same coordinates and same names are +// merged for StepGeom_Axis2Placement3d. +TEST_F(MergeSTEPEntities_CartesianPointProcessorTest, StepGeom_Axis2Placement3d) +{ + // Creating Cartesian points. + Handle(StepGeom_CartesianPoint) aPt1 = addCartesianPoint(); + Handle(StepGeom_CartesianPoint) aPt2 = addCartesianPoint(); + + // Creating direction. + Handle(TColStd_HArray1OfReal) aDirCoords = new TColStd_HArray1OfReal(1, 3); + aDirCoords->SetValue(1, 0.); + aDirCoords->SetValue(2, 0.); + aDirCoords->SetValue(3, 1.); + Handle(StepGeom_Direction) aDir = new StepGeom_Direction; + aDir->Init(new TCollection_HAsciiString, aDirCoords); + addToModel(aDir); + + // Creating axis containing the first Cartesian point. + Handle(StepGeom_Axis2Placement3d) aFirstAxis = new StepGeom_Axis2Placement3d; + aFirstAxis + ->Init(new TCollection_HAsciiString, aPt1, Standard_True, aDir, Standard_False, nullptr); + addToModel(aFirstAxis); + + // Creating axis containing the second Cartesian point. + Handle(StepGeom_Axis2Placement3d) aSecondAxis = new StepGeom_Axis2Placement3d; + aSecondAxis + ->Init(new TCollection_HAsciiString, aPt2, Standard_True, aDir, Standard_False, nullptr); + addToModel(aSecondAxis); + + // Performing removal of duplicate Cartesian points. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateCartesianPoints(); + + // Check that duplicate was removed. + EXPECT_EQ(aRemovedEntities.Size(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(aPt1) || aRemovedEntities.Contains(aPt2)); +} + +// Check that points with the same coordinates and same names are merged +// for StepShape_VertexPoint. +TEST_F(MergeSTEPEntities_CartesianPointProcessorTest, StepShape_VertexPoint) +{ + // Creating Cartesian points. + Handle(StepGeom_CartesianPoint) aPt1 = addCartesianPoint(); + Handle(StepGeom_CartesianPoint) aPt2 = addCartesianPoint(); + + // Creating vertex containing the first Cartesian point. + Handle(StepShape_VertexPoint) aFirstVertex = new StepShape_VertexPoint; + aFirstVertex->Init(new TCollection_HAsciiString, aPt1); + addToModel(aFirstVertex); + + // Creating vertex containing the second Cartesian point. + Handle(StepShape_VertexPoint) aSecondVertex = new StepShape_VertexPoint; + aSecondVertex->Init(new TCollection_HAsciiString, aPt2); + addToModel(aSecondVertex); + + // Performing removal of duplicate Cartesian points. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateCartesianPoints(); + + // Check that duplicate was removed. + EXPECT_EQ(aRemovedEntities.Size(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(aPt1) || aRemovedEntities.Contains(aPt2)); +} + +// Check that points with the same coordinates and same names are merged +// for StepShape_GeometricCurveSet. +TEST_F(MergeSTEPEntities_CartesianPointProcessorTest, StepShape_GeometricCurveSet) +{ + // Creating Cartesian points. + Handle(StepGeom_CartesianPoint) aPt1 = addCartesianPoint(); + Handle(StepGeom_CartesianPoint) aPt2 = addCartesianPoint(); + + // Creating curve set containing the first Cartesian point. + Handle(StepShape_HArray1OfGeometricSetSelect) aFirstElements = + new StepShape_HArray1OfGeometricSetSelect(1, 1); + StepShape_GeometricSetSelect aFirstSelect; + aFirstSelect.SetValue(aPt1); + aFirstElements->SetValue(1, aFirstSelect); + addToModel(aFirstElements); + Handle(StepShape_GeometricCurveSet) aFirstCurveSet = new StepShape_GeometricCurveSet; + aFirstCurveSet->Init(new TCollection_HAsciiString, aFirstElements); + addToModel(aFirstCurveSet); + + // Creating curve set containing the second Cartesian point. + Handle(StepShape_HArray1OfGeometricSetSelect) aSecondElements = + new StepShape_HArray1OfGeometricSetSelect(1, 1); + StepShape_GeometricSetSelect aSecondSelect; + aSecondSelect.SetValue(aPt2); + aSecondElements->SetValue(1, aSecondSelect); + addToModel(aSecondElements); + Handle(StepShape_GeometricCurveSet) aSecondCurveSet = new StepShape_GeometricCurveSet; + aSecondCurveSet->Init(new TCollection_HAsciiString, aSecondElements); + addToModel(aSecondCurveSet); + + // Performing removal of duplicate Cartesian points. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateCartesianPoints(); + + // Check that duplicate was removed. + EXPECT_EQ(aRemovedEntities.Size(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(aPt1) || aRemovedEntities.Contains(aPt2)); +} + +// Check that points with the same coordinates and same names are merged +// for StepVisual_PresentationLayerAssignment. +TEST_F(MergeSTEPEntities_CartesianPointProcessorTest, StepVisual_PresentationLayerAssignment) +{ + // Creating Cartesian points. + Handle(StepGeom_CartesianPoint) aPt1 = addCartesianPoint(); + Handle(StepGeom_CartesianPoint) aPt2 = addCartesianPoint(); + + // Creating presentation layer assignment containing the first Cartesian point. + Handle(StepVisual_HArray1OfLayeredItem) aFirstAssignedItems = + new StepVisual_HArray1OfLayeredItem(1, 1); + StepVisual_LayeredItem aFirstLayeredItem; + aFirstLayeredItem.SetValue(aPt1); + aFirstAssignedItems->SetValue(1, aFirstLayeredItem); + addToModel(aFirstAssignedItems); + Handle(StepVisual_PresentationLayerAssignment) aFirstAssignment = + new StepVisual_PresentationLayerAssignment; + aFirstAssignment->Init(new TCollection_HAsciiString, + new TCollection_HAsciiString, + aFirstAssignedItems); + addToModel(aFirstAssignment); + + // Creating presentation layer assignment containing the second Cartesian point. + Handle(StepVisual_HArray1OfLayeredItem) aSecondAssignedItems = + new StepVisual_HArray1OfLayeredItem(1, 1); + StepVisual_LayeredItem aSecondLayeredItem; + aSecondLayeredItem.SetValue(aPt2); + aSecondAssignedItems->SetValue(1, aSecondLayeredItem); + addToModel(aSecondAssignedItems); + Handle(StepVisual_PresentationLayerAssignment) aSecondAssignment = + new StepVisual_PresentationLayerAssignment; + aSecondAssignment->Init(new TCollection_HAsciiString, + new TCollection_HAsciiString, + aSecondAssignedItems); + addToModel(aSecondAssignment); + + // Performing removal of duplicate Cartesian points. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateCartesianPoints(); + + // Check that duplicate was removed. + EXPECT_EQ(aRemovedEntities.Size(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(aPt1) || aRemovedEntities.Contains(aPt2)); +} + +// Check that points with the same coordinates and same names are merged +// for StepVisual_StyledItem. +TEST_F(MergeSTEPEntities_CartesianPointProcessorTest, StepVisual_StyledItem) +{ + // Creating Cartesian points. + Handle(StepGeom_CartesianPoint) aPt1 = addCartesianPoint(); + Handle(StepGeom_CartesianPoint) aPt2 = addCartesianPoint(); + + // Creating styled item containing the first Cartesian point. + Handle(StepVisual_HArray1OfPresentationStyleAssignment) aFirstAssignedItems = + new StepVisual_HArray1OfPresentationStyleAssignment(1, 1); + Handle(StepVisual_StyledItem) aFirstStyledItem = new StepVisual_StyledItem; + aFirstStyledItem->Init(new TCollection_HAsciiString, aFirstAssignedItems, aPt1); + addToModel(aFirstStyledItem); + + // Creating styled item containing the second Cartesian point. + Handle(StepVisual_HArray1OfPresentationStyleAssignment) aSecondAssignedItems = + new StepVisual_HArray1OfPresentationStyleAssignment(1, 1); + Handle(StepVisual_StyledItem) aSecondStyledItem = new StepVisual_StyledItem; + aSecondStyledItem->Init(new TCollection_HAsciiString, aSecondAssignedItems, aPt2); + addToModel(aSecondStyledItem); + + // Performing removal of duplicate Cartesian points. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateCartesianPoints(); + + // Check that duplicate was removed. + EXPECT_EQ(aRemovedEntities.Size(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(aPt1) || aRemovedEntities.Contains(aPt2)); +} + +// Check that points with the same coordinates and same names are merged +// for StepGeom_BSplineCurveWithKnots. +TEST_F(MergeSTEPEntities_CartesianPointProcessorTest, StepGeom_BSplineCurveWithKnots) +{ + // Creating Cartesian points. + Handle(StepGeom_CartesianPoint) aPt1 = addCartesianPoint(); + Handle(StepGeom_CartesianPoint) aPt2 = addCartesianPoint(); + + // Creating curve containing the first Cartesian point. + Handle(StepGeom_HArray1OfCartesianPoint) aFirstControlPoints = + new StepGeom_HArray1OfCartesianPoint(1, 1); + aFirstControlPoints->SetValue(1, aPt1); + addToModel(aFirstControlPoints); + Handle(StepGeom_BSplineCurveWithKnots) aFirstCurve = new StepGeom_BSplineCurveWithKnots; + aFirstCurve->Init(new TCollection_HAsciiString, + 1, + aFirstControlPoints, + StepGeom_bscfUnspecified, + StepData_LUnknown, + StepData_LUnknown, + new TColStd_HArray1OfInteger, + new TColStd_HArray1OfReal, + StepGeom_ktUnspecified); + addToModel(aFirstCurve); + + // Creating curve containing the second Cartesian point. + Handle(StepGeom_HArray1OfCartesianPoint) aSecondControlPoints = + new StepGeom_HArray1OfCartesianPoint(1, 1); + aSecondControlPoints->SetValue(1, aPt2); + addToModel(aSecondControlPoints); + Handle(StepGeom_BSplineCurveWithKnots) aSecondCurve = new StepGeom_BSplineCurveWithKnots; + aSecondCurve->Init(new TCollection_HAsciiString, + 1, + aSecondControlPoints, + StepGeom_bscfUnspecified, + StepData_LUnknown, + StepData_LUnknown, + new TColStd_HArray1OfInteger, + new TColStd_HArray1OfReal, + StepGeom_ktUnspecified); + addToModel(aSecondCurve); + + // Performing removal of duplicate Cartesian points. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateCartesianPoints(); + + // Check that duplicate was removed. + EXPECT_EQ(aRemovedEntities.Size(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(aPt1) || aRemovedEntities.Contains(aPt2)); +} + +// Check that points with the same coordinates and same names are merged +// for StepGeom_Line. +TEST_F(MergeSTEPEntities_CartesianPointProcessorTest, StepGeom_Line) +{ + // Creating Cartesian points. + Handle(StepGeom_CartesianPoint) aPt1 = addCartesianPoint(); + Handle(StepGeom_CartesianPoint) aPt2 = addCartesianPoint(); + + // Creating line containing the first Cartesian point. + Handle(StepGeom_Line) aFirstLine = new StepGeom_Line; + aFirstLine->Init(new TCollection_HAsciiString, aPt1, new StepGeom_Vector); + addToModel(aFirstLine); + + // Creating line containing the second Cartesian point. + Handle(StepGeom_Line) aSecondLine = new StepGeom_Line; + aSecondLine->Init(new TCollection_HAsciiString, aPt2, new StepGeom_Vector); + addToModel(aSecondLine); + + // Performing removal of duplicate Cartesian points. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateCartesianPoints(); + + // Check that duplicate was removed. + EXPECT_EQ(aRemovedEntities.Size(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(aPt1) || aRemovedEntities.Contains(aPt2)); +} + +// Check that points with the same coordinates and same names are merged +// for StepGeom_BSplineSurfaceWithKnots. +TEST_F(MergeSTEPEntities_CartesianPointProcessorTest, StepGeom_BSplineSurfaceWithKnots) +{ + // Creating Cartesian points. + Handle(StepGeom_CartesianPoint) aPt1 = addCartesianPoint(); + Handle(StepGeom_CartesianPoint) aPt2 = addCartesianPoint(); + + // Creating surface containing the first Cartesian point. + Handle(StepGeom_HArray2OfCartesianPoint) aFirstControlPoints = + new StepGeom_HArray2OfCartesianPoint(1, 1, 1, 1); + aFirstControlPoints->SetValue(1, 1, aPt1); + addToModel(aFirstControlPoints); + Handle(StepGeom_BSplineSurfaceWithKnots) aFirstSurface = new StepGeom_BSplineSurfaceWithKnots; + aFirstSurface->Init(new TCollection_HAsciiString, + 1, + 1, + aFirstControlPoints, + StepGeom_bssfUnspecified, + StepData_LUnknown, + StepData_LUnknown, + StepData_LUnknown, + new TColStd_HArray1OfInteger, + new TColStd_HArray1OfInteger, + new TColStd_HArray1OfReal, + new TColStd_HArray1OfReal, + StepGeom_ktUnspecified); + addToModel(aFirstSurface); + + // Creating surface containing the second Cartesian point. + Handle(StepGeom_HArray2OfCartesianPoint) aSecondControlPoints = + new StepGeom_HArray2OfCartesianPoint(1, 1, 1, 1); + aSecondControlPoints->SetValue(1, 1, aPt2); + addToModel(aSecondControlPoints); + Handle(StepGeom_BSplineSurfaceWithKnots) aSecondSurface = new StepGeom_BSplineSurfaceWithKnots; + aSecondSurface->Init(new TCollection_HAsciiString, + 1, + 1, + aSecondControlPoints, + StepGeom_bssfUnspecified, + StepData_LUnknown, + StepData_LUnknown, + StepData_LUnknown, + new TColStd_HArray1OfInteger, + new TColStd_HArray1OfInteger, + new TColStd_HArray1OfReal, + new TColStd_HArray1OfReal, + StepGeom_ktUnspecified); + addToModel(aSecondSurface); + + // Performing removal of duplicate Cartesian points. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateCartesianPoints(); + + // Check that duplicate was removed. + EXPECT_EQ(aRemovedEntities.Size(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(aPt1) || aRemovedEntities.Contains(aPt2)); +} + +// Check that points with the same coordinates and same names are merged +// for StepGeom_Axis1Placement. +TEST_F(MergeSTEPEntities_CartesianPointProcessorTest, StepGeom_Axis1Placement) +{ + // Creating Cartesian points. + Handle(StepGeom_CartesianPoint) aPt1 = addCartesianPoint(); + Handle(StepGeom_CartesianPoint) aPt2 = addCartesianPoint(); + + // Creating axis containing the first Cartesian point. + Handle(StepGeom_Axis1Placement) aFirstAxis = new StepGeom_Axis1Placement; + aFirstAxis->Init(new TCollection_HAsciiString, aPt1, false, new StepGeom_Direction); + addToModel(aFirstAxis); + + // Creating axis containing the second Cartesian point. + Handle(StepGeom_Axis1Placement) aSecondAxis = new StepGeom_Axis1Placement; + aSecondAxis->Init(new TCollection_HAsciiString, aPt2, false, new StepGeom_Direction); + addToModel(aSecondAxis); + + // Performing removal of duplicate Cartesian points. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateCartesianPoints(); + + // Check that duplicate was removed. + EXPECT_EQ(aRemovedEntities.Size(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(aPt1) || aRemovedEntities.Contains(aPt2)); +} + +// Check that points with the same coordinates and same names are merged +// for StepRepr_Representation. +TEST_F(MergeSTEPEntities_CartesianPointProcessorTest, StepRepr_Representation) +{ + // Creating Cartesian points. + Handle(StepGeom_CartesianPoint) aPt1 = addCartesianPoint(); + Handle(StepGeom_CartesianPoint) aPt2 = addCartesianPoint(); + + // Creating representation containing the first Cartesian point. + Handle(StepRepr_HArray1OfRepresentationItem) aFirstItems = + new StepRepr_HArray1OfRepresentationItem(1, 1); + aFirstItems->SetValue(1, aPt1); + Handle(StepRepr_Representation) aFirstRepresentation = new StepRepr_Representation; + aFirstRepresentation->Init(new TCollection_HAsciiString, + aFirstItems, + new StepRepr_RepresentationContext); + addToModel(aFirstRepresentation); + + // Creating representation containing the second Cartesian point. + Handle(StepRepr_HArray1OfRepresentationItem) aSecondItems = + new StepRepr_HArray1OfRepresentationItem(1, 1); + aSecondItems->SetValue(1, aPt2); + Handle(StepRepr_Representation) aSecondRepresentation = new StepRepr_Representation; + aSecondRepresentation->Init(new TCollection_HAsciiString, + aSecondItems, + new StepRepr_RepresentationContext); + addToModel(aSecondRepresentation); + + // Performing removal of duplicate Cartesian points. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateCartesianPoints(); + + // Check that duplicate was removed. + EXPECT_EQ(aRemovedEntities.Size(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(aPt1) || aRemovedEntities.Contains(aPt2)); +} + +// Check that points with the same coordinates and same names are merged +// for StepGeom_BSplineCurveWithKnotsAndRationalBSplineCurve. +TEST_F(MergeSTEPEntities_CartesianPointProcessorTest, + StepGeom_BSplineCurveWithKnotsAndRationalBSplineCurve) +{ + // Creating Cartesian points. + Handle(StepGeom_CartesianPoint) aPt1 = addCartesianPoint(); + Handle(StepGeom_CartesianPoint) aPt2 = addCartesianPoint(); + + // Creating curve containing the first Cartesian point. + Handle(StepGeom_HArray1OfCartesianPoint) aFirstControlPoints = + new StepGeom_HArray1OfCartesianPoint(1, 1); + aFirstControlPoints->SetValue(1, aPt1); + addToModel(aFirstControlPoints); + Handle(StepGeom_BSplineCurveWithKnotsAndRationalBSplineCurve) aFirstCurve = + new StepGeom_BSplineCurveWithKnotsAndRationalBSplineCurve; + aFirstCurve->Init(new TCollection_HAsciiString, + 1, + aFirstControlPoints, + StepGeom_bscfUnspecified, + StepData_LUnknown, + StepData_LUnknown, + new TColStd_HArray1OfInteger, + new TColStd_HArray1OfReal, + StepGeom_ktUnspecified, + new TColStd_HArray1OfReal); + addToModel(aFirstCurve); + + // Creating curve containing the second Cartesian point. + Handle(StepGeom_HArray1OfCartesianPoint) aSecondControlPoints = + new StepGeom_HArray1OfCartesianPoint(1, 1); + aSecondControlPoints->SetValue(1, aPt2); + addToModel(aSecondControlPoints); + Handle(StepGeom_BSplineCurveWithKnotsAndRationalBSplineCurve) aSecondCurve = + new StepGeom_BSplineCurveWithKnotsAndRationalBSplineCurve; + aSecondCurve->Init(new TCollection_HAsciiString, + 1, + aSecondControlPoints, + StepGeom_bscfUnspecified, + StepData_LUnknown, + StepData_LUnknown, + new TColStd_HArray1OfInteger, + new TColStd_HArray1OfReal, + StepGeom_ktUnspecified, + new TColStd_HArray1OfReal); + addToModel(aSecondCurve); + + // Performing removal of duplicate Cartesian points. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateCartesianPoints(); + + // Check that duplicate was removed. + EXPECT_EQ(aRemovedEntities.Size(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(aPt1) || aRemovedEntities.Contains(aPt2)); +} + +// Check that points with the same coordinates and same names are merged +// for StepGeom_BSplineSurfaceWithKnotsAndRationalBSplineSurface. +TEST_F(MergeSTEPEntities_CartesianPointProcessorTest, + StepGeom_BSplineSurfaceWithKnotsAndRationalBSplineSurface) +{ + // Creating Cartesian points. + Handle(StepGeom_CartesianPoint) aPt1 = addCartesianPoint(); + Handle(StepGeom_CartesianPoint) aPt2 = addCartesianPoint(); + + // Creating surface containing the first Cartesian point. + Handle(StepGeom_HArray2OfCartesianPoint) aFirstControlPoints = + new StepGeom_HArray2OfCartesianPoint(1, 1, 1, 1); + aFirstControlPoints->SetValue(1, 1, aPt1); + addToModel(aFirstControlPoints); + Handle(StepGeom_BSplineSurfaceWithKnots) aFirstBSSWN = new StepGeom_BSplineSurfaceWithKnots; + aFirstBSSWN->Init(new TCollection_HAsciiString, + 1, + 1, + aFirstControlPoints, + StepGeom_bssfUnspecified, + StepData_LUnknown, + StepData_LUnknown, + StepData_LUnknown, + new TColStd_HArray1OfInteger, + new TColStd_HArray1OfInteger, + new TColStd_HArray1OfReal, + new TColStd_HArray1OfReal, + StepGeom_ktUnspecified); + addToModel(aFirstBSSWN); + Handle(StepGeom_BSplineSurfaceWithKnotsAndRationalBSplineSurface) aFirstSurface = + new StepGeom_BSplineSurfaceWithKnotsAndRationalBSplineSurface; + aFirstSurface->Init(new TCollection_HAsciiString, + 1, + 1, + aFirstControlPoints, + StepGeom_bssfUnspecified, + StepData_LUnknown, + StepData_LUnknown, + StepData_LUnknown, + aFirstBSSWN, + new StepGeom_RationalBSplineSurface); + addToModel(aFirstSurface); + + // Creating surface containing the second Cartesian point. + Handle(StepGeom_HArray2OfCartesianPoint) aSecondControlPoints = + new StepGeom_HArray2OfCartesianPoint(1, 1, 1, 1); + aSecondControlPoints->SetValue(1, 1, aPt2); + addToModel(aSecondControlPoints); + Handle(StepGeom_BSplineSurfaceWithKnots) aSecondBSSWN = new StepGeom_BSplineSurfaceWithKnots; + aSecondBSSWN->Init(new TCollection_HAsciiString, + 1, + 1, + aSecondControlPoints, + StepGeom_bssfUnspecified, + StepData_LUnknown, + StepData_LUnknown, + StepData_LUnknown, + new TColStd_HArray1OfInteger, + new TColStd_HArray1OfInteger, + new TColStd_HArray1OfReal, + new TColStd_HArray1OfReal, + StepGeom_ktUnspecified); + addToModel(aSecondBSSWN); + Handle(StepGeom_BSplineSurfaceWithKnotsAndRationalBSplineSurface) aSecondSurface = + new StepGeom_BSplineSurfaceWithKnotsAndRationalBSplineSurface; + aSecondSurface->Init(new TCollection_HAsciiString, + 1, + 1, + aSecondControlPoints, + StepGeom_bssfUnspecified, + StepData_LUnknown, + StepData_LUnknown, + StepData_LUnknown, + new StepGeom_BSplineSurfaceWithKnots, + new StepGeom_RationalBSplineSurface); + + // Performing removal of duplicate Cartesian points. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateCartesianPoints(); + + // Check that duplicate was removed. + EXPECT_EQ(aRemovedEntities.Size(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(aPt1) || aRemovedEntities.Contains(aPt2)); +} diff --git a/src/DataExchange/TKDESTEP/GTests/MergeSTEPEntities_CircleProcessor_Test.cxx b/src/DataExchange/TKDESTEP/GTests/MergeSTEPEntities_CircleProcessor_Test.cxx new file mode 100644 index 0000000000..4d8e3d9e87 --- /dev/null +++ b/src/DataExchange/TKDESTEP/GTests/MergeSTEPEntities_CircleProcessor_Test.cxx @@ -0,0 +1,165 @@ +// 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 "MergeSTEPEntities_BaseTestFixture.hxx" + +#include + +#include +#include +#include + +class MergeSTEPEntities_CircleProcessorTest : public MergeSTEPEntities_BaseTestFixture +{ +protected: + //! Perform removal of duplicate entities. + TColStd_MapOfTransient replaceDuplicateCircles() + { + MergeSTEPEntities_CircleProcessor aProcessor(myWS); + for (Standard_Integer anIndex = 1; anIndex <= myWS->Model()->NbEntities(); ++anIndex) + { + aProcessor.ProcessEntity(myWS->Model()->Value(anIndex)); + } + + TColStd_MapOfTransient aRemovedEntities; + aProcessor.Perform(aRemovedEntities); + return aRemovedEntities; + } +}; + +// Check that Circles with the same coordinates and different names are not merged. +TEST_F(MergeSTEPEntities_CircleProcessorTest, DifferentNames) +{ + // Creating Circles. + Handle(StepGeom_Circle) aCircle1 = addCircle("Circle1"); + Handle(StepGeom_Circle) aCircle2 = addCircle("Circle2"); + + // Creating EdgeCurve containing the first Circle. + Handle(StepShape_EdgeCurve) aFirstEdgeCurve = new StepShape_EdgeCurve; + aFirstEdgeCurve->Init(new TCollection_HAsciiString, + new StepShape_Vertex, + new StepShape_Vertex, + aCircle1, + Standard_True); + addToModel(aFirstEdgeCurve); + + // Creating EdgeCurve containing the second Circle. + Handle(StepShape_EdgeCurve) aSecondEdgeCurve = new StepShape_EdgeCurve; + aSecondEdgeCurve->Init(new TCollection_HAsciiString, + new StepShape_Vertex, + new StepShape_Vertex, + aCircle2, + Standard_True); + addToModel(aSecondEdgeCurve); + + // Performing removal of duplicate Circles. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateCircles(); + + // Check that nothing was removed. + EXPECT_TRUE(aRemovedEntities.IsEmpty()); +} + +// Check that equal Circles are merged for StepShape_EdgeCurve. +TEST_F(MergeSTEPEntities_CircleProcessorTest, StepShape_EdgeCurve) +{ + // Creating Circles. + Handle(StepGeom_Circle) aCircle1 = addCircle(); + Handle(StepGeom_Circle) aCircle2 = addCircle(); + + // Creating EdgeCurve containing the first Circle. + Handle(StepShape_EdgeCurve) aFirstEdgeCurve = new StepShape_EdgeCurve; + aFirstEdgeCurve->Init(new TCollection_HAsciiString, + new StepShape_Vertex, + new StepShape_Vertex, + aCircle1, + Standard_True); + addToModel(aFirstEdgeCurve); + + // Creating EdgeCurve containing the second Circle. + Handle(StepShape_EdgeCurve) aSecondEdgeCurve = new StepShape_EdgeCurve; + aSecondEdgeCurve->Init(new TCollection_HAsciiString, + new StepShape_Vertex, + new StepShape_Vertex, + aCircle2, + Standard_True); + addToModel(aSecondEdgeCurve); + + // Performing removal of duplicate Circles. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateCircles(); + + // Check that one Circle was removed. + EXPECT_EQ(aRemovedEntities.Size(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(aCircle1) || aRemovedEntities.Contains(aCircle2)); +} + +// Check that equal Circles are merged for StepGeom_SurfaceCurve. +TEST_F(MergeSTEPEntities_CircleProcessorTest, StepGeom_SurfaceCurve) +{ + // Creating Circles. + Handle(StepGeom_Circle) aCircle1 = addCircle(); + Handle(StepGeom_Circle) aCircle2 = addCircle(); + + // Creating SurfaceCurve containing the first Circle. + Handle(StepGeom_SurfaceCurve) aFirstSurfaceCurve = new StepGeom_SurfaceCurve; + aFirstSurfaceCurve->Init(new TCollection_HAsciiString, + aCircle1, + new StepGeom_HArray1OfPcurveOrSurface, + StepGeom_pscrCurve3d); + addToModel(aFirstSurfaceCurve); + + // Creating SurfaceCurve containing the second Circle. + Handle(StepGeom_SurfaceCurve) aSecondSurfaceCurve = new StepGeom_SurfaceCurve; + aSecondSurfaceCurve->Init(new TCollection_HAsciiString, + aCircle2, + new StepGeom_HArray1OfPcurveOrSurface, + StepGeom_pscrCurve3d); + addToModel(aSecondSurfaceCurve); + + // Performing removal of duplicate Circles. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateCircles(); + + // Check that one Circle was removed. + EXPECT_EQ(aRemovedEntities.Size(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(aCircle1) || aRemovedEntities.Contains(aCircle2)); +} + +// Check that equal Circles are merged for StepGeom_SeamCurve. +TEST_F(MergeSTEPEntities_CircleProcessorTest, StepGeom_SeamCurve) +{ + // Creating Circles. + Handle(StepGeom_Circle) aCircle1 = addCircle(); + Handle(StepGeom_Circle) aCircle2 = addCircle(); + + // Creating SeamCurve containing the first Circle. + Handle(StepGeom_SeamCurve) aFirstSeamCurve = new StepGeom_SeamCurve; + aFirstSeamCurve->Init(new TCollection_HAsciiString, + aCircle1, + new StepGeom_HArray1OfPcurveOrSurface, + StepGeom_pscrCurve3d); + addToModel(aFirstSeamCurve); + + // Creating SeamCurve containing the second Circle. + Handle(StepGeom_SeamCurve) aSecondSeamCurve = new StepGeom_SeamCurve; + aSecondSeamCurve->Init(new TCollection_HAsciiString, + aCircle2, + new StepGeom_HArray1OfPcurveOrSurface, + StepGeom_pscrCurve3d); + addToModel(aSecondSeamCurve); + + // Performing removal of duplicate Circles. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateCircles(); + + // Check that one Circle was removed. + EXPECT_EQ(aRemovedEntities.Size(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(aCircle1) || aRemovedEntities.Contains(aCircle2)); +} diff --git a/src/DataExchange/TKDESTEP/GTests/MergeSTEPEntities_DirectionProcessor_Test.cxx b/src/DataExchange/TKDESTEP/GTests/MergeSTEPEntities_DirectionProcessor_Test.cxx new file mode 100644 index 0000000000..876cd29230 --- /dev/null +++ b/src/DataExchange/TKDESTEP/GTests/MergeSTEPEntities_DirectionProcessor_Test.cxx @@ -0,0 +1,157 @@ +// 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 "MergeSTEPEntities_BaseTestFixture.hxx" + +#include + +#include +#include + +class MergeSTEPEntities_DirectionProcessorTest : public MergeSTEPEntities_BaseTestFixture +{ +protected: + //! Perform removal of duplicate entities. + TColStd_MapOfTransient replaceDuplicateDirections() + { + MergeSTEPEntities_DirectionProcessor aProcessor(myWS); + for (Standard_Integer anIndex = 1; anIndex <= myWS->Model()->NbEntities(); ++anIndex) + { + aProcessor.ProcessEntity(myWS->Model()->Value(anIndex)); + } + + TColStd_MapOfTransient aRemovedEntities; + aProcessor.Perform(aRemovedEntities); + return aRemovedEntities; + } +}; + +// Check that directions with the same coordinates and different names are not merged. +TEST_F(MergeSTEPEntities_DirectionProcessorTest, DifferentNames) +{ + // Creating directions. + Handle(StepGeom_Direction) aDir1 = addDirection("dir1"); + Handle(StepGeom_Direction) aDir2 = addDirection("dir2"); + + // Creating vector containing the first direction. + Handle(StepGeom_Vector) aFirstVector = new StepGeom_Vector; + aFirstVector->Init(new TCollection_HAsciiString, aDir1, 1.); + addToModel(aFirstVector); + + // Creating vector containing the second direction. + Handle(StepGeom_Vector) aSecondVector = new StepGeom_Vector; + aSecondVector->Init(new TCollection_HAsciiString, aDir2, 1.); + addToModel(aSecondVector); + + // Performing removal of duplicate directions. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateDirections(); + + // Check that nothing was removed. + EXPECT_TRUE(aRemovedEntities.IsEmpty()); +} + +// Check that directions with the same coordinates and same names are +// merged for StepGeom_Axis1Placement. +TEST_F(MergeSTEPEntities_DirectionProcessorTest, StepGeom_Axis1Placement) +{ + // Creating directions. + Handle(StepGeom_Direction) aDir1 = addDirection(); + Handle(StepGeom_Direction) aDir2 = addDirection(); + + // Creating Cartesian point for the location. + Handle(StepGeom_CartesianPoint) aLocation = new StepGeom_CartesianPoint; + Handle(TColStd_HArray1OfReal) aLocationCoords = new TColStd_HArray1OfReal(1, 3); + aLocationCoords->SetValue(1, 0.); + aLocationCoords->SetValue(2, 0.); + aLocationCoords->SetValue(3, 0.); + aLocation->Init(new TCollection_HAsciiString, aLocationCoords); + addToModel(aLocation); + + // Creating axis containing the first direction. + Handle(StepGeom_Axis1Placement) aFirstAxis = new StepGeom_Axis1Placement; + aFirstAxis->Init(new TCollection_HAsciiString, aLocation, true, aDir1); + addToModel(aFirstAxis); + + // Creating axis containing the second direction. + Handle(StepGeom_Axis1Placement) aSecondAxis = new StepGeom_Axis1Placement; + aSecondAxis->Init(new TCollection_HAsciiString, aLocation, true, aDir2); + addToModel(aSecondAxis); + + // Performing removal of duplicate Cartesian points. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateDirections(); + + // Check that duplicate was removed. + EXPECT_EQ(aRemovedEntities.Size(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(aDir1) || aRemovedEntities.Contains(aDir2)); +} + +// Check that directions with the same coordinates and same names are +// merged for StepGeom_Axis2Placement. +TEST_F(MergeSTEPEntities_DirectionProcessorTest, StepGeom_Axis2Placement) +{ + // Creating directions. + Handle(StepGeom_Direction) aDir1 = addDirection(); + Handle(StepGeom_Direction) aDir2 = addDirection(); + + // Creating Cartesian point for the location. + Handle(StepGeom_CartesianPoint) aLocation = new StepGeom_CartesianPoint; + Handle(TColStd_HArray1OfReal) aLocationCoords = new TColStd_HArray1OfReal(1, 3); + aLocationCoords->SetValue(1, 0.); + aLocationCoords->SetValue(2, 0.); + aLocationCoords->SetValue(3, 0.); + aLocation->Init(new TCollection_HAsciiString, aLocationCoords); + addToModel(aLocation); + + // Creating axis containing the first direction. + Handle(StepGeom_Axis2Placement3d) aFirstAxis = new StepGeom_Axis2Placement3d; + aFirstAxis->Init(new TCollection_HAsciiString, aLocation, true, aDir1, false, nullptr); + addToModel(aFirstAxis); + + // Creating axis containing the second direction. + Handle(StepGeom_Axis2Placement3d) aSecondAxis = new StepGeom_Axis2Placement3d; + aSecondAxis->Init(new TCollection_HAsciiString, aLocation, true, aDir2, false, nullptr); + addToModel(aSecondAxis); + + // Performing removal of duplicate Cartesian points. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateDirections(); + + // Check that duplicate was removed. + EXPECT_EQ(aRemovedEntities.Size(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(aDir1) || aRemovedEntities.Contains(aDir2)); +} + +// Check that points with the same coordinates and same names are +// merged for StepGeom_Vector. +TEST_F(MergeSTEPEntities_DirectionProcessorTest, StepGeom_Vector) +{ + // Creating directions. + Handle(StepGeom_Direction) aDir1 = addDirection(); + Handle(StepGeom_Direction) aDir2 = addDirection(); + + // Creating vector containing the first direction. + Handle(StepGeom_Vector) aFirstVector = new StepGeom_Vector; + aFirstVector->Init(new TCollection_HAsciiString, aDir1, 1.); + addToModel(aFirstVector); + + // Creating vector containing the second direction. + Handle(StepGeom_Vector) aSecondVector = new StepGeom_Vector; + aSecondVector->Init(new TCollection_HAsciiString, aDir2, 1.); + addToModel(aSecondVector); + + // Performing removal of duplicate Cartesian points. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateDirections(); + + // Check that duplicate was removed. + EXPECT_EQ(aRemovedEntities.Size(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(aDir1) || aRemovedEntities.Contains(aDir2)); +} diff --git a/src/DataExchange/TKDESTEP/GTests/MergeSTEPEntities_LineProcessor_Test.cxx b/src/DataExchange/TKDESTEP/GTests/MergeSTEPEntities_LineProcessor_Test.cxx new file mode 100644 index 0000000000..ed3ec53ee4 --- /dev/null +++ b/src/DataExchange/TKDESTEP/GTests/MergeSTEPEntities_LineProcessor_Test.cxx @@ -0,0 +1,239 @@ +// 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 "MergeSTEPEntities_BaseTestFixture.hxx" + +#include + +#include +#include +#include +#include +#include +#include + +class MergeSTEPEntities_LineProcessorTest : public MergeSTEPEntities_BaseTestFixture +{ +protected: + //! Perform removal of duplicate entities. + TColStd_MapOfTransient replaceDuplicateLines() + { + MergeSTEPEntities_LineProcessor aProcessor(myWS); + for (Standard_Integer anIndex = 1; anIndex <= myWS->Model()->NbEntities(); ++anIndex) + { + aProcessor.ProcessEntity(myWS->Model()->Value(anIndex)); + } + + TColStd_MapOfTransient aRemovedEntities; + aProcessor.Perform(aRemovedEntities); + return aRemovedEntities; + } +}; + +// Check that Lines with different names are not merged. +TEST_F(MergeSTEPEntities_LineProcessorTest, DifferentNames) +{ + // Creating Lines. + Handle(StepGeom_Line) aLine1 = addLine("Line1"); + Handle(StepGeom_Line) aLine2 = addLine("Line2"); + + // Creating EdgeCurve containing the first Line. + Handle(StepShape_EdgeCurve) aFirstEdgeCurve = new StepShape_EdgeCurve; + aFirstEdgeCurve->Init(new TCollection_HAsciiString, + new StepShape_Vertex, + new StepShape_Vertex, + aLine1, + Standard_True); + addToModel(aFirstEdgeCurve); + + // Creating EdgeCurve containing the second Line. + Handle(StepShape_EdgeCurve) aSecondEdgeCurve = new StepShape_EdgeCurve; + aSecondEdgeCurve->Init(new TCollection_HAsciiString, + new StepShape_Vertex, + new StepShape_Vertex, + aLine2, + Standard_True); + addToModel(aSecondEdgeCurve); + + // Performing removal of duplicate Lines. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateLines(); + + // Check that nothing was removed. + EXPECT_TRUE(aRemovedEntities.IsEmpty()); +} + +// Check that equal Lines are merged for StepShape_EdgeCurve. +TEST_F(MergeSTEPEntities_LineProcessorTest, StepShape_EdgeCurve) +{ + // Creating Lines. + Handle(StepGeom_Line) aLine1 = addLine(); + Handle(StepGeom_Line) aLine2 = addLine(); + + // Creating EdgeCurve containing the first Line. + Handle(StepShape_EdgeCurve) aFirstEdgeCurve = new StepShape_EdgeCurve; + aFirstEdgeCurve->Init(new TCollection_HAsciiString, + new StepShape_Vertex, + new StepShape_Vertex, + aLine1, + Standard_True); + addToModel(aFirstEdgeCurve); + + // Creating EdgeCurve containing the second Line. + Handle(StepShape_EdgeCurve) aSecondEdgeCurve = new StepShape_EdgeCurve; + aSecondEdgeCurve->Init(new TCollection_HAsciiString, + new StepShape_Vertex, + new StepShape_Vertex, + aLine2, + Standard_True); + addToModel(aSecondEdgeCurve); + + // Performing removal of duplicate Lines. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateLines(); + + // Check that one Line was removed. + EXPECT_EQ(aRemovedEntities.Size(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(aLine1) || aRemovedEntities.Contains(aLine2)); +} + +// Check that equal Lines are merged for StepGeom_TrimmedCurve. +TEST_F(MergeSTEPEntities_LineProcessorTest, StepGeom_TrimmedCurve) +{ + // Creating Lines. + Handle(StepGeom_Line) aLine1 = addLine(); + Handle(StepGeom_Line) aLine2 = addLine(); + + // Creating TrimmedCurve containing the first Line. + Handle(StepGeom_TrimmedCurve) aFirstTrimmedCurve = new StepGeom_TrimmedCurve; + aFirstTrimmedCurve->Init(new TCollection_HAsciiString, + aLine1, + new StepGeom_HArray1OfTrimmingSelect, + new StepGeom_HArray1OfTrimmingSelect, + Standard_True, + StepGeom_tpUnspecified); + addToModel(aFirstTrimmedCurve); + + // Creating TrimmedCurve containing the second Line. + Handle(StepGeom_TrimmedCurve) aSecondTrimmedCurve = new StepGeom_TrimmedCurve; + aSecondTrimmedCurve->Init(new TCollection_HAsciiString, + aLine2, + new StepGeom_HArray1OfTrimmingSelect, + new StepGeom_HArray1OfTrimmingSelect, + Standard_True, + StepGeom_tpUnspecified); + addToModel(aSecondTrimmedCurve); + + // Performing removal of duplicate Lines. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateLines(); + + // Check that one Line was removed. + EXPECT_EQ(aRemovedEntities.Size(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(aLine1) || aRemovedEntities.Contains(aLine2)); +} + +// Check that equal Lines are merged for StepGeom_SurfaceCurve. +TEST_F(MergeSTEPEntities_LineProcessorTest, StepGeom_SurfaceCurve) +{ + // Creating Lines. + Handle(StepGeom_Line) aLine1 = addLine(); + Handle(StepGeom_Line) aLine2 = addLine(); + + // Creating SurfaceCurve containing the first Line. + Handle(StepGeom_SurfaceCurve) aFirstSurfaceCurve = new StepGeom_SurfaceCurve; + aFirstSurfaceCurve->Init(new TCollection_HAsciiString, + aLine1, + new StepGeom_HArray1OfPcurveOrSurface, + StepGeom_pscrCurve3d); + addToModel(aFirstSurfaceCurve); + + // Creating SurfaceCurve containing the second Line. + Handle(StepGeom_SurfaceCurve) aSecondSurfaceCurve = new StepGeom_SurfaceCurve; + aSecondSurfaceCurve->Init(new TCollection_HAsciiString, + aLine2, + new StepGeom_HArray1OfPcurveOrSurface, + StepGeom_pscrCurve3d); + addToModel(aSecondSurfaceCurve); + + // Performing removal of duplicate Lines. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateLines(); + + // Check that one Line was removed. + EXPECT_EQ(aRemovedEntities.Size(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(aLine1) || aRemovedEntities.Contains(aLine2)); +} + +// Check that equal Lines are merged for StepRepr_DefinitionalRepresentation. +TEST_F(MergeSTEPEntities_LineProcessorTest, StepRepr_DefinitionalRepresentation) +{ + // Creating Lines. + Handle(StepGeom_Line) aLine1 = addLine(); + Handle(StepGeom_Line) aLine2 = addLine(); + + // Creating DefinitionalRepresentation containing the first Line. + Handle(StepRepr_HArray1OfRepresentationItem) aFirstItems = + new StepRepr_HArray1OfRepresentationItem(1, 1); + aFirstItems->SetValue(1, aLine1); + Handle(StepRepr_DefinitionalRepresentation) aFirstDefinitionalRepresentation = + new StepRepr_DefinitionalRepresentation; + aFirstDefinitionalRepresentation->Init(new TCollection_HAsciiString, + aFirstItems, + new StepRepr_RepresentationContext); + addToModel(aFirstDefinitionalRepresentation); + + // Creating DefinitionalRepresentation containing the second Line. + Handle(StepRepr_HArray1OfRepresentationItem) aSecondItems = + new StepRepr_HArray1OfRepresentationItem(1, 1); + aSecondItems->SetValue(1, aLine2); + Handle(StepRepr_DefinitionalRepresentation) aSecondDefinitionalRepresentation = + new StepRepr_DefinitionalRepresentation; + aSecondDefinitionalRepresentation->Init(new TCollection_HAsciiString, + aSecondItems, + new StepRepr_RepresentationContext); + + // Performing removal of duplicate Lines. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateLines(); + + // Check that one Line was removed. + EXPECT_EQ(aRemovedEntities.Size(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(aLine1) || aRemovedEntities.Contains(aLine2)); +} + +// Check that equal Lines are merged for StepGeom_SeamCurve. +TEST_F(MergeSTEPEntities_LineProcessorTest, StepGeom_SeamCurve) +{ + // Creating Lines. + Handle(StepGeom_Line) aLine1 = addLine(); + Handle(StepGeom_Line) aLine2 = addLine(); + + // Creating SeamCurve containing the first Line. + Handle(StepGeom_SeamCurve) aFirstSeamCurve = new StepGeom_SeamCurve; + aFirstSeamCurve->Init(new TCollection_HAsciiString, + aLine1, + new StepGeom_HArray1OfPcurveOrSurface, + StepGeom_pscrCurve3d); + addToModel(aFirstSeamCurve); + + // Creating SeamCurve containing the second Line. + Handle(StepGeom_SeamCurve) aSecondSeamCurve = new StepGeom_SeamCurve; + aSecondSeamCurve->Init(new TCollection_HAsciiString, + aLine2, + new StepGeom_HArray1OfPcurveOrSurface, + StepGeom_pscrCurve3d); + addToModel(aSecondSeamCurve); + + // Performing removal of duplicate Lines. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateLines(); + + // Check that one Line was removed. + EXPECT_EQ(aRemovedEntities.Size(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(aLine1) || aRemovedEntities.Contains(aLine2)); +} diff --git a/src/DataExchange/TKDESTEP/GTests/MergeSTEPEntities_Merger_Test.cxx b/src/DataExchange/TKDESTEP/GTests/MergeSTEPEntities_Merger_Test.cxx new file mode 100644 index 0000000000..1ce9630db6 --- /dev/null +++ b/src/DataExchange/TKDESTEP/GTests/MergeSTEPEntities_Merger_Test.cxx @@ -0,0 +1,113 @@ +// 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 "MergeSTEPEntities_BaseTestFixture.hxx" + +#include + +#include + +class MergeSTEPEntities_MergerTest : public MergeSTEPEntities_BaseTestFixture +{ +protected: + // Get the number of entities of the specified type. + // @param theType the type of entities to count. + // @return the number of entities of the specified type. + int getEntitiesCount(const Handle(Standard_Type)& theType) const + { + int aCount = 0; + for (Standard_Integer i = 1; i <= myWS->Model()->NbEntities(); i++) + { + if (myWS->Model()->Value(i)->IsKind(theType)) + { + aCount++; + } + } + return aCount; + } + + //! Perform removal of duplicate entities points. + void performRemoval() + { + MergeSTEPEntities_Merger aMerger(myWS); + aMerger.Perform(); + } +}; + +// Check that entities with the same coordinates and different names are not merged. +TEST_F(MergeSTEPEntities_MergerTest, DifferentEntities) +{ + // Creating directions. + Handle(StepGeom_Direction) aDir1 = addDirection("dir1"); + Handle(StepGeom_Direction) aDir2 = addDirection("dir2"); + + // Creating vector containing the first direction. + Handle(StepGeom_Vector) aFirstVector = new StepGeom_Vector; + aFirstVector->Init(new TCollection_HAsciiString, aDir1, 1.); + addToModel(aFirstVector); + + // Creating vector containing the second direction. + Handle(StepGeom_Vector) aSecondVector = new StepGeom_Vector; + aSecondVector->Init(new TCollection_HAsciiString, aDir2, 1.); + addToModel(aSecondVector); + + const int aDirectionCountBefore = getEntitiesCount(STANDARD_TYPE(StepGeom_Direction)); + + // Performing removal of duplicate directions. + performRemoval(); + + const int aDirectionCountAfter = getEntitiesCount(STANDARD_TYPE(StepGeom_Direction)); + + // Check that nothing was removed. + EXPECT_EQ(aDirectionCountBefore, 2); + EXPECT_EQ(aDirectionCountBefore, aDirectionCountAfter); +} + +// Check that entities with the same coordinates and same names are +// merged for StepGeom_Axis1Placement. +TEST_F(MergeSTEPEntities_MergerTest, EqualEntities) +{ + // Creating directions. + Handle(StepGeom_Direction) aDir1 = addDirection(); + Handle(StepGeom_Direction) aDir2 = addDirection(); + + // Creating Cartesian point for the location. + Handle(StepGeom_CartesianPoint) aLocation = new StepGeom_CartesianPoint; + Handle(TColStd_HArray1OfReal) aLocationCoords = new TColStd_HArray1OfReal(1, 3); + aLocationCoords->SetValue(1, 0.); + aLocationCoords->SetValue(2, 0.); + aLocationCoords->SetValue(3, 0.); + aLocation->Init(new TCollection_HAsciiString, aLocationCoords); + addToModel(aLocation); + + // Creating axis containing the first direction. + Handle(StepGeom_Axis1Placement) aFirstAxis = new StepGeom_Axis1Placement; + aFirstAxis->Init(new TCollection_HAsciiString, aLocation, true, aDir1); + addToModel(aFirstAxis); + + // Creating axis containing the second direction. + Handle(StepGeom_Axis1Placement) aSecondAxis = new StepGeom_Axis1Placement; + aSecondAxis->Init(new TCollection_HAsciiString, aLocation, true, aDir2); + addToModel(aSecondAxis); + + const int aDirectionCountBefore = getEntitiesCount(STANDARD_TYPE(StepGeom_Direction)); + + // Performing removal of duplicate directions. + performRemoval(); + + const int aDirectionCountAfter = getEntitiesCount(STANDARD_TYPE(StepGeom_Direction)); + + // Check that one direction was removed. + EXPECT_EQ(aDirectionCountBefore, 2); + EXPECT_EQ(aDirectionCountAfter, 1); +} diff --git a/src/DataExchange/TKDESTEP/GTests/MergeSTEPEntities_PlaneProcessor_Test.cxx b/src/DataExchange/TKDESTEP/GTests/MergeSTEPEntities_PlaneProcessor_Test.cxx new file mode 100644 index 0000000000..64388d5494 --- /dev/null +++ b/src/DataExchange/TKDESTEP/GTests/MergeSTEPEntities_PlaneProcessor_Test.cxx @@ -0,0 +1,128 @@ +// 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 "MergeSTEPEntities_BaseTestFixture.hxx" + +#include + +#include +#include +#include + +class MergeSTEPEntities_PlaneProcessorTest : public MergeSTEPEntities_BaseTestFixture +{ +protected: + //! Perform removal of duplicate entities. + TColStd_MapOfTransient replaceDuplicatePlanes() + { + MergeSTEPEntities_PlaneProcessor aProcessor(myWS); + for (Standard_Integer anIndex = 1; anIndex <= myWS->Model()->NbEntities(); ++anIndex) + { + aProcessor.ProcessEntity(myWS->Model()->Value(anIndex)); + } + + TColStd_MapOfTransient aRemovedEntities; + aProcessor.Perform(aRemovedEntities); + return aRemovedEntities; + } +}; + +// Check that Planes with different names are not merged. +TEST_F(MergeSTEPEntities_PlaneProcessorTest, DifferentNames) +{ + // Creating Planes. + Handle(StepGeom_Plane) aPlane1 = addPlane("Plane1"); + Handle(StepGeom_Plane) aPlane2 = addPlane("Plane2"); + + // Creating StepShape_AdvancedFace containing the first Plane. + Handle(StepShape_AdvancedFace) aFirstAdvancedFace = new StepShape_AdvancedFace; + aFirstAdvancedFace->Init(new TCollection_HAsciiString, + new StepShape_HArray1OfFaceBound, + aPlane1, + Standard_True); + addToModel(aFirstAdvancedFace); + + // Creating StepShape_AdvancedFace containing the second Plane. + Handle(StepShape_AdvancedFace) aSecondAdvancedFace = new StepShape_AdvancedFace; + aSecondAdvancedFace->Init(new TCollection_HAsciiString, + new StepShape_HArray1OfFaceBound, + aPlane2, + Standard_True); + addToModel(aSecondAdvancedFace); + + // Performing removal of duplicate Planes. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicatePlanes(); + + // Check that nothing was removed. + EXPECT_TRUE(aRemovedEntities.IsEmpty()); +} + +// Check that equal Planes are merged for StepShape_AdvancedFace. +TEST_F(MergeSTEPEntities_PlaneProcessorTest, StepShape_AdvancedFace) +{ + // Creating Planes. + Handle(StepGeom_Plane) aPlane1 = addPlane(); + Handle(StepGeom_Plane) aPlane2 = addPlane(); + + // Creating StepShape_AdvancedFace containing the first Plane. + Handle(StepShape_AdvancedFace) aFirstAdvancedFace = new StepShape_AdvancedFace; + aFirstAdvancedFace->Init(new TCollection_HAsciiString, + new StepShape_HArray1OfFaceBound, + aPlane1, + Standard_True); + addToModel(aFirstAdvancedFace); + + // Creating StepShape_AdvancedFace containing the second Plane. + Handle(StepShape_AdvancedFace) aSecondAdvancedFace = new StepShape_AdvancedFace; + aSecondAdvancedFace->Init(new TCollection_HAsciiString, + new StepShape_HArray1OfFaceBound, + aPlane2, + Standard_True); + addToModel(aSecondAdvancedFace); + + // Performing removal of duplicate Planes. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicatePlanes(); + + // Check that one Plane was removed. + EXPECT_EQ(aRemovedEntities.Size(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(aPlane1) || aRemovedEntities.Contains(aPlane2)); +} + +// Check that equal Planes are merged for StepGeom_Pcurve. +TEST_F(MergeSTEPEntities_PlaneProcessorTest, StepGeom_Pcurve) +{ + // Creating Planes. + Handle(StepGeom_Plane) aPlane1 = addPlane(); + Handle(StepGeom_Plane) aPlane2 = addPlane(); + + // Creating StepGeom_Pcurve containing the first Plane. + Handle(StepGeom_Pcurve) aFirstPcurve = new StepGeom_Pcurve; + aFirstPcurve->Init(new TCollection_HAsciiString, + aPlane1, + new StepRepr_DefinitionalRepresentation); + addToModel(aFirstPcurve); + + // Creating StepGeom_Pcurve containing the second Plane. + Handle(StepGeom_Pcurve) aSecondPcurve = new StepGeom_Pcurve; + aSecondPcurve->Init(new TCollection_HAsciiString, + aPlane2, + new StepRepr_DefinitionalRepresentation); + addToModel(aSecondPcurve); + + // Performing removal of duplicate Planes. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicatePlanes(); + + // Check that one Plane was removed. + EXPECT_EQ(aRemovedEntities.Size(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(aPlane1) || aRemovedEntities.Contains(aPlane2)); +} diff --git a/src/DataExchange/TKDESTEP/GTests/MergeSTEPEntities_VectorProcessor_Test.cxx b/src/DataExchange/TKDESTEP/GTests/MergeSTEPEntities_VectorProcessor_Test.cxx new file mode 100644 index 0000000000..c66edf06eb --- /dev/null +++ b/src/DataExchange/TKDESTEP/GTests/MergeSTEPEntities_VectorProcessor_Test.cxx @@ -0,0 +1,92 @@ +// 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 "MergeSTEPEntities_BaseTestFixture.hxx" + +#include + +#include + +class MergeSTEPEntities_VectorProcessorTest : public MergeSTEPEntities_BaseTestFixture +{ +protected: + //! Perform removal of duplicate entities. + TColStd_MapOfTransient replaceDuplicateVectors() + { + MergeSTEPEntities_VectorProcessor aProcessor(myWS); + for (Standard_Integer anIndex = 1; anIndex <= myWS->Model()->NbEntities(); ++anIndex) + { + aProcessor.ProcessEntity(myWS->Model()->Value(anIndex)); + } + + TColStd_MapOfTransient aRemovedEntities; + aProcessor.Perform(aRemovedEntities); + return aRemovedEntities; + } +}; + +// Check that Vectors with the same coordinates and different names are not merged. +TEST_F(MergeSTEPEntities_VectorProcessorTest, DifferentNames) +{ + // Creating Vectors. + Handle(StepGeom_Vector) aVec1 = addVector("vec1"); + Handle(StepGeom_Vector) aVec2 = addVector("vec2"); + + // Creating a cartesian point for the lines. + Handle(StepGeom_CartesianPoint) aPnt = addCartesianPoint(nullptr, {0., 0., 0.}); + + // Creating aLine containing the first Vector. + Handle(StepGeom_Line) aLine1 = new StepGeom_Line; + aLine1->Init(new TCollection_HAsciiString, aPnt, aVec1); + addToModel(aLine1); + + // Creating aLine containing the second Vector. + Handle(StepGeom_Line) aLine2 = new StepGeom_Line; + aLine2->Init(new TCollection_HAsciiString, aPnt, aVec2); + addToModel(aLine2); + + // Performing removal of duplicate Vectors. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateVectors(); + + // Check that nothing was removed. + EXPECT_TRUE(aRemovedEntities.IsEmpty()); +} + +// Check that Vectors with the same coordinates and same names are +// merged for StepGeom_Axis1Placement. +TEST_F(MergeSTEPEntities_VectorProcessorTest, StepGeom_Line) +{ + // Creating Vectors. + Handle(StepGeom_Vector) aVec1 = addVector(); + Handle(StepGeom_Vector) aVec2 = addVector(); + + // Creating a cartesian point for the lines. + Handle(StepGeom_CartesianPoint) aPnt = addCartesianPoint(nullptr, {0., 0., 0.}); + + // Creating aLine containing the first Vector. + Handle(StepGeom_Line) aLine1 = new StepGeom_Line; + aLine1->Init(new TCollection_HAsciiString, aPnt, aVec1); + addToModel(aLine1); + + // Creating aLine containing the second Vector. + Handle(StepGeom_Line) aLine2 = new StepGeom_Line; + aLine2->Init(new TCollection_HAsciiString, aPnt, aVec2); + addToModel(aLine2); + + // Performing removal of duplicate Vectors. + TColStd_MapOfTransient aRemovedEntities = replaceDuplicateVectors(); + + // Check that duplicate was removed. + EXPECT_EQ(aRemovedEntities.Size(), 1); + EXPECT_TRUE(aRemovedEntities.Contains(aVec1) || aRemovedEntities.Contains(aVec2)); +} diff --git a/src/DataExchange/TKDESTEP/MergeSTEPEntities/FILES.cmake b/src/DataExchange/TKDESTEP/MergeSTEPEntities/FILES.cmake new file mode 100644 index 0000000000..c55d38ebbd --- /dev/null +++ b/src/DataExchange/TKDESTEP/MergeSTEPEntities/FILES.cmake @@ -0,0 +1,30 @@ +# Source files for STEPControl package +set(OCCT_MergeSTEPEntities_FILES_LOCATION "${CMAKE_CURRENT_LIST_DIR}") + +set(OCCT_MergeSTEPEntities_FILES + MergeSTEPEntities_Axis2Placement2dHasher.hxx + MergeSTEPEntities_Axis2Placement3dHasher.hxx + MergeSTEPEntities_Axis2Placement3dProcessor.cxx + MergeSTEPEntities_Axis2Placement3dProcessor.hxx + MergeSTEPEntities_CartesianPointHasher.hxx + MergeSTEPEntities_CartesianPointProcessor.cxx + MergeSTEPEntities_CartesianPointProcessor.hxx + MergeSTEPEntities_CircleHasher.hxx + MergeSTEPEntities_CircleProcessor.cxx + MergeSTEPEntities_CircleProcessor.hxx + MergeSTEPEntities_DirectionHasher.hxx + MergeSTEPEntities_DirectionProcessor.cxx + MergeSTEPEntities_DirectionProcessor.hxx + MergeSTEPEntities_EntityProcessor.hxx + MergeSTEPEntities_LineHasher.hxx + MergeSTEPEntities_LineProcessor.cxx + MergeSTEPEntities_LineProcessor.hxx + MergeSTEPEntities_Merger.cxx + MergeSTEPEntities_Merger.hxx + MergeSTEPEntities_PlaneHasher.hxx + MergeSTEPEntities_PlaneProcessor.cxx + MergeSTEPEntities_PlaneProcessor.hxx + MergeSTEPEntities_VectorHasher.hxx + MergeSTEPEntities_VectorProcessor.cxx + MergeSTEPEntities_VectorProcessor.hxx +) diff --git a/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_Axis2Placement2dHasher.hxx b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_Axis2Placement2dHasher.hxx new file mode 100644 index 0000000000..a98f3bfe0c --- /dev/null +++ b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_Axis2Placement2dHasher.hxx @@ -0,0 +1,80 @@ +// 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 _MergeSTEPEntities_Axis2Placement2dHasher_HeaderFile +#define _MergeSTEPEntities_Axis2Placement2dHasher_HeaderFile + +#include +#include + +#include +#include + +//! OCCT-style hasher for StepGeom_Axis2Placement2d entities. +//! Currently only used for implementation of hasher for StepGeom_Circle. +struct MergeSTEPEntities_Axis2Placement2dHasher +{ + // Hashes the axis placements. + std::size_t operator()(const Handle(StepGeom_Axis2Placement2d)& thePlacement) const noexcept + { + // Prepare an array of hashes for the location, axis, and ref direction. + // Optimal seed is used for the axis and ref direction if they are not present. + const size_t aHashes[2]{MergeSTEPEntities_CartesianPointHasher{}(thePlacement->Location()), + thePlacement->HasRefDirection() + ? MergeSTEPEntities_DirectionHasher{}(thePlacement->RefDirection()) + : opencascade::MurmurHash::optimalSeed()}; + const size_t aHash = opencascade::hashBytes(aHashes, sizeof(aHashes)); + if (thePlacement->Name().IsNull()) + { + // If the name is not present, return the hash. + return aHash; + } + // Add the name to the hash if it is present. + const size_t aHashWithName[2]{ + aHash, + std::hash{}(thePlacement->Name()->String())}; + return opencascade::hashBytes(aHashWithName, sizeof(aHashWithName)); + } + + // Compares two axis placements. + bool operator()(const Handle(StepGeom_Axis2Placement2d)& thePlacement1, + const Handle(StepGeom_Axis2Placement2d)& thePlacement2) const noexcept + { + // Compare names. + if (thePlacement1->Name().IsNull() != thePlacement2->Name().IsNull()) + { + return false; + } + if (!thePlacement1->Name()->IsSameString(thePlacement2->Name())) + { + return false; + } + + // Compare location, axis, and ref direction. + const bool isSameLocation = MergeSTEPEntities_CartesianPointHasher{}(thePlacement1->Location(), + thePlacement2->Location()); + // Have to check if the axis is present and compare it. + // Have to check if the ref direction is present and compare it. + const bool isSameRefDirectionFlag = + thePlacement1->HasRefDirection() == thePlacement2->HasRefDirection(); + const bool isSameRefDirection = + isSameRefDirectionFlag + && (!thePlacement1->HasRefDirection() + || MergeSTEPEntities_DirectionHasher{}(thePlacement1->RefDirection(), + thePlacement2->RefDirection())); + + return isSameLocation && isSameRefDirection; + } +}; + +#endif // _MergeSTEPEntities_Axis2Placement2dHasher_HeaderFile diff --git a/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_Axis2Placement3dHasher.hxx b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_Axis2Placement3dHasher.hxx new file mode 100644 index 0000000000..9d0c70d14f --- /dev/null +++ b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_Axis2Placement3dHasher.hxx @@ -0,0 +1,87 @@ +// 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 _MergeSTEPEntities_Axis2Placement3dHasher_HeaderFile +#define _MergeSTEPEntities_Axis2Placement3dHasher_HeaderFile + +#include +#include + +#include +#include + +//! OCCT-style hasher for StepGeom_Axis2Placement3d entities. +struct MergeSTEPEntities_Axis2Placement3dHasher +{ + // Hashes the axis placements. + std::size_t operator()(const Handle(StepGeom_Axis2Placement3d)& thePlacement) const noexcept + { + // Prepare an array of hashes for the location, axis, and ref direction. + // Optimal seed is used for the axis and ref direction if they are not present. + const size_t aHashes[3]{MergeSTEPEntities_CartesianPointHasher{}(thePlacement->Location()), + thePlacement->HasAxis() + ? MergeSTEPEntities_DirectionHasher{}(thePlacement->Axis()) + : opencascade::MurmurHash::optimalSeed(), + thePlacement->HasRefDirection() + ? MergeSTEPEntities_DirectionHasher{}(thePlacement->RefDirection()) + : opencascade::MurmurHash::optimalSeed()}; + const size_t aHash = opencascade::hashBytes(aHashes, sizeof(aHashes)); + if (thePlacement->Name().IsNull()) + { + // If the name is not present, return the hash. + return aHash; + } + // Add the name to the hash if it is present. + const size_t aHashWithName[2]{ + aHash, + std::hash{}(thePlacement->Name()->String())}; + return opencascade::hashBytes(aHashWithName, sizeof(aHashWithName)); + } + + // Compares two axis placements. + bool operator()(const Handle(StepGeom_Axis2Placement3d)& thePlacement1, + const Handle(StepGeom_Axis2Placement3d)& thePlacement2) const noexcept + { + // Compare names. + if (thePlacement1->Name().IsNull() != thePlacement2->Name().IsNull()) + { + return false; + } + if (!thePlacement1->Name()->IsSameString(thePlacement2->Name())) + { + return false; + } + + // Compare location, axis, and ref direction. + const bool isSameLocation = MergeSTEPEntities_CartesianPointHasher{}(thePlacement1->Location(), + thePlacement2->Location()); + // Have to check if the axis is present and compare it. + const bool isSameAxisFlag = thePlacement1->HasAxis() == thePlacement2->HasAxis(); + const bool isSameAxis = + isSameAxisFlag + && (!thePlacement1->HasAxis() + || MergeSTEPEntities_DirectionHasher{}(thePlacement1->Axis(), thePlacement2->Axis())); + // Have to check if the ref direction is present and compare it. + const bool isSameRefDirectionFlag = + thePlacement1->HasRefDirection() == thePlacement2->HasRefDirection(); + const bool isSameRefDirection = + isSameRefDirectionFlag + && (!thePlacement1->HasRefDirection() + || MergeSTEPEntities_DirectionHasher{}(thePlacement1->RefDirection(), + thePlacement2->RefDirection())); + + return isSameLocation && isSameAxis && isSameRefDirection; + } +}; + +#endif // _MergeSTEPEntities_Axis2Placement3dHasher_HeaderFile diff --git a/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_Axis2Placement3dProcessor.cxx b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_Axis2Placement3dProcessor.cxx new file mode 100644 index 0000000000..b1e49a4bdc --- /dev/null +++ b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_Axis2Placement3dProcessor.cxx @@ -0,0 +1,299 @@ +// 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//================================================================================================== + +MergeSTEPEntities_Axis2Placement3dProcessor::MergeSTEPEntities_Axis2Placement3dProcessor( + const Handle(XSControl_WorkSession)& theWS) + : MergeSTEPEntities_EntityProcessor(theWS) +{ + registerReplacer(STANDARD_TYPE(StepGeom_Plane), replacePlane); + registerReplacer(STANDARD_TYPE(StepRepr_ItemDefinedTransformation), + replaceItemDefinedTransformation); + registerReplacer(STANDARD_TYPE(StepGeom_CylindricalSurface), replaceCylindricalSurface); + registerReplacer(STANDARD_TYPE(StepShape_ShapeRepresentation), replaceShapeRepresentation); + registerReplacer(STANDARD_TYPE(StepRepr_ConstructiveGeometryRepresentation), + replaceConstructiveGeometryRepresentation); + registerReplacer(STANDARD_TYPE(StepGeom_Circle), replaceCircle); + registerReplacer(STANDARD_TYPE(StepVisual_PresentationLayerAssignment), + replacePresentationLayerAssignment); + registerReplacer(STANDARD_TYPE(StepVisual_StyledItem), replaceStyledItem); + registerReplacer(STANDARD_TYPE(StepGeom_Ellipse), replaceEllipse); + registerReplacer(STANDARD_TYPE(StepGeom_ConicalSurface), replaceConicalSurface); + registerReplacer(STANDARD_TYPE(StepGeom_ToroidalSurface), replaceToroidalSurface); + registerReplacer(STANDARD_TYPE(StepShape_AdvancedBrepShapeRepresentation), + replaceAdvancedBrepShapeRepresentation); + registerReplacer(STANDARD_TYPE(StepGeom_SphericalSurface), replaceSphericalSurface); +} + +//================================================================================================== + +bool MergeSTEPEntities_Axis2Placement3dProcessor::replacePlane( + const Handle(StepGeom_Axis2Placement3d)& theOldEntity, + const Handle(StepGeom_Axis2Placement3d)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepGeom_Plane) aSharing = Handle(StepGeom_Plane)::DownCast(theSharing); + if (aSharing->Position() == theOldEntity) + { + aSharing->SetPosition(theNewEntity); + return true; + } + return false; +} + +//================================================================================================== + +bool MergeSTEPEntities_Axis2Placement3dProcessor::replaceItemDefinedTransformation( + const Handle(StepGeom_Axis2Placement3d)& theOldEntity, + const Handle(StepGeom_Axis2Placement3d)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepRepr_ItemDefinedTransformation) aSharing = + Handle(StepRepr_ItemDefinedTransformation)::DownCast(theSharing); + bool isReplaced = false; + if (aSharing->TransformItem1() == theOldEntity) + { + aSharing->SetTransformItem1(theNewEntity); + isReplaced = true; + } + if (aSharing->TransformItem2() == theOldEntity) + { + aSharing->SetTransformItem2(theNewEntity); + isReplaced = true; + } + return isReplaced; +} + +//================================================================================================== + +bool MergeSTEPEntities_Axis2Placement3dProcessor::replaceCylindricalSurface( + const Handle(StepGeom_Axis2Placement3d)& theOldEntity, + const Handle(StepGeom_Axis2Placement3d)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepGeom_CylindricalSurface) aSharing = + Handle(StepGeom_CylindricalSurface)::DownCast(theSharing); + if (aSharing->Position() == theOldEntity) + { + aSharing->SetPosition(theNewEntity); + return true; + } + return false; +} + +//================================================================================================== + +bool MergeSTEPEntities_Axis2Placement3dProcessor::replaceShapeRepresentation( + const Handle(StepGeom_Axis2Placement3d)& theOldEntity, + const Handle(StepGeom_Axis2Placement3d)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepShape_ShapeRepresentation) aSharing = + Handle(StepShape_ShapeRepresentation)::DownCast(theSharing); + bool isReplaced = false; + for (Standard_Integer anIndex = aSharing->Items()->Lower(); anIndex <= aSharing->Items()->Upper(); + ++anIndex) + { + if (aSharing->Items()->Value(anIndex) == theOldEntity) + { + aSharing->Items()->SetValue(anIndex, theNewEntity); + isReplaced = true; + } + } + return isReplaced; +} + +//================================================================================================== + +bool MergeSTEPEntities_Axis2Placement3dProcessor::replaceConstructiveGeometryRepresentation( + const Handle(StepGeom_Axis2Placement3d)& theOldEntity, + const Handle(StepGeom_Axis2Placement3d)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepRepr_ConstructiveGeometryRepresentation) aSharing = + Handle(StepRepr_ConstructiveGeometryRepresentation)::DownCast(theSharing); + bool isReplaced = false; + for (Standard_Integer anIndex = aSharing->Items()->Lower(); anIndex <= aSharing->Items()->Upper(); + ++anIndex) + { + if (aSharing->Items()->Value(anIndex) == theOldEntity) + { + aSharing->Items()->SetValue(anIndex, theNewEntity); + isReplaced = true; + } + } + return isReplaced; +} + +//================================================================================================== + +bool MergeSTEPEntities_Axis2Placement3dProcessor::replaceCircle( + const Handle(StepGeom_Axis2Placement3d)& theOldEntity, + const Handle(StepGeom_Axis2Placement3d)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepGeom_Circle) aSharing = Handle(StepGeom_Circle)::DownCast(theSharing); + StepGeom_Axis2Placement aSelector = aSharing->Position(); + if (aSelector.Axis2Placement3d() == theOldEntity) + { + aSelector.SetValue(theNewEntity); + return true; + } + return false; +} + +//================================================================================================== + +bool MergeSTEPEntities_Axis2Placement3dProcessor::replacePresentationLayerAssignment( + const Handle(StepGeom_Axis2Placement3d)& theOldEntity, + const Handle(StepGeom_Axis2Placement3d)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepVisual_PresentationLayerAssignment) aSharing = + Handle(StepVisual_PresentationLayerAssignment)::DownCast(theSharing); + bool isReplaced = false; + Handle(StepVisual_HArray1OfLayeredItem) anItems = aSharing->AssignedItems(); + for (Standard_Integer anIndex = anItems->Lower(); anIndex <= anItems->Upper(); ++anIndex) + { + StepVisual_LayeredItem& aLayeredItem = anItems->ChangeValue(anIndex); + if (aLayeredItem.RepresentationItem() == theOldEntity) + { + aLayeredItem.SetValue(theNewEntity); + isReplaced = true; + } + } + return isReplaced; +} + +//================================================================================================== + +bool MergeSTEPEntities_Axis2Placement3dProcessor::replaceStyledItem( + const Handle(StepGeom_Axis2Placement3d)& theOldEntity, + const Handle(StepGeom_Axis2Placement3d)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepVisual_StyledItem) aSharing = Handle(StepVisual_StyledItem)::DownCast(theSharing); + if (aSharing->Item() == theOldEntity) + { + aSharing->SetItem(theNewEntity); + return true; + } + return false; +} + +//================================================================================================== + +bool MergeSTEPEntities_Axis2Placement3dProcessor::replaceEllipse( + const Handle(StepGeom_Axis2Placement3d)& theOldEntity, + const Handle(StepGeom_Axis2Placement3d)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepGeom_Ellipse) aSharing = Handle(StepGeom_Ellipse)::DownCast(theSharing); + StepGeom_Axis2Placement aSelector = aSharing->Position(); + if (aSelector.Axis2Placement3d() == theOldEntity) + { + aSelector.SetValue(theNewEntity); + return true; + } + return false; +} + +//================================================================================================== + +bool MergeSTEPEntities_Axis2Placement3dProcessor::replaceConicalSurface( + const Handle(StepGeom_Axis2Placement3d)& theOldEntity, + const Handle(StepGeom_Axis2Placement3d)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepGeom_ConicalSurface) aSharing = Handle(StepGeom_ConicalSurface)::DownCast(theSharing); + if (aSharing->Position() == theOldEntity) + { + aSharing->SetPosition(theNewEntity); + return true; + } + return false; +} + +//================================================================================================== + +bool MergeSTEPEntities_Axis2Placement3dProcessor::replaceToroidalSurface( + const Handle(StepGeom_Axis2Placement3d)& theOldEntity, + const Handle(StepGeom_Axis2Placement3d)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepGeom_ToroidalSurface) aSharing = + Handle(StepGeom_ToroidalSurface)::DownCast(theSharing); + if (aSharing->Position() == theOldEntity) + { + aSharing->SetPosition(theNewEntity); + return true; + } + return false; +} + +//================================================================================================== + +bool MergeSTEPEntities_Axis2Placement3dProcessor::replaceAdvancedBrepShapeRepresentation( + const Handle(StepGeom_Axis2Placement3d)& theOldEntity, + const Handle(StepGeom_Axis2Placement3d)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepShape_AdvancedBrepShapeRepresentation) aSharing = + Handle(StepShape_AdvancedBrepShapeRepresentation)::DownCast(theSharing); + bool isReplaced = false; + for (Standard_Integer anIndex = aSharing->Items()->Lower(); anIndex <= aSharing->Items()->Upper(); + ++anIndex) + { + if (aSharing->Items()->Value(anIndex) == theOldEntity) + { + aSharing->Items()->SetValue(anIndex, theNewEntity); + isReplaced = true; + } + } + return isReplaced; +} + +//================================================================================================== + +bool MergeSTEPEntities_Axis2Placement3dProcessor::replaceSphericalSurface( + const Handle(StepGeom_Axis2Placement3d)& theOldEntity, + const Handle(StepGeom_Axis2Placement3d)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepGeom_SphericalSurface) aSharing = + Handle(StepGeom_SphericalSurface)::DownCast(theSharing); + if (aSharing->Position() == theOldEntity) + { + aSharing->SetPosition(theNewEntity); + return true; + } + return false; +} diff --git a/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_Axis2Placement3dProcessor.hxx b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_Axis2Placement3dProcessor.hxx new file mode 100644 index 0000000000..c46247c8d4 --- /dev/null +++ b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_Axis2Placement3dProcessor.hxx @@ -0,0 +1,160 @@ +// 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 _MergeSTEPEntities_Axis2Placement3dProcessor_HeaderFile +#define _MergeSTEPEntities_Axis2Placement3dProcessor_HeaderFile + +#include +#include + +#include + +//! Processor for merging StepGeom_Axis2Placement3d entities. +//! This processor merges axis placements with the same location, axis, and ref direction. +class MergeSTEPEntities_Axis2Placement3dProcessor + : public MergeSTEPEntities_EntityProcessor +{ +public: + //! Constructor. Stores the work session and registers replacer functions. + //! @param theWS the work session. + Standard_EXPORT MergeSTEPEntities_Axis2Placement3dProcessor( + const Handle(XSControl_WorkSession)& theWS); + +private: + //! Replaces the old axis placement with the new one in the StepGeom_Plane entity. + //! @param theOldEntity the old axis placement. + //! @param theNewEntity the new axis placement to replace the old one. + //! @param theSharing the StepGeom_Plane entity to update. + //! @return true if the axis placement was replaced, false otherwise. + static bool replacePlane(const Handle(StepGeom_Axis2Placement3d)& theOldEntity, + const Handle(StepGeom_Axis2Placement3d)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replaces the old axis placement with the new one in the StepRepr_ItemDefinedTransformation + //! entity. + //! @param theOldEntity the old axis placement. + //! @param theNewEntity the new axis placement to replace the old one. + //! @param theSharing the StepRepr_ItemDefinedTransformation entity to update. + //! @return true if the axis placement was replaced, false otherwise. + static bool replaceItemDefinedTransformation( + const Handle(StepGeom_Axis2Placement3d)& theOldEntity, + const Handle(StepGeom_Axis2Placement3d)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replaces the old axis placement with the new one in the StepGeom_CylindricalSurface entity. + //! @param theOldEntity the old axis placement. + //! @param theNewEntity the new axis placement to replace the old one. + //! @param theSharing the StepGeom_CylindricalSurface entity to update. + //! @return true if the axis placement was replaced, false otherwise. + static bool replaceCylindricalSurface(const Handle(StepGeom_Axis2Placement3d)& theOldEntity, + const Handle(StepGeom_Axis2Placement3d)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replaces the old axis placement with the new one in the StepShape_ShapeRepresentation entity. + //! @param theOldEntity the old axis placement. + //! @param theNewEntity the new axis placement to replace the old one. + //! @param theSharing the StepShape_ShapeRepresentation entity to update. + //! @return true if the axis placement was replaced, false otherwise. + static bool replaceShapeRepresentation(const Handle(StepGeom_Axis2Placement3d)& theOldEntity, + const Handle(StepGeom_Axis2Placement3d)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replaces the old axis placement with the new one in the + //! StepRepr_ConstructiveGeometryRepresentation entity. + //! @param theOldEntity the old axis placement. + //! @param theNewEntity the new axis placement to replace the old one. + //! @param theSharing the StepRepr_ConstructiveGeometryRepresentation entity to update. + //! @return true if the axis placement was replaced, false otherwise. + static bool replaceConstructiveGeometryRepresentation( + const Handle(StepGeom_Axis2Placement3d)& theOldEntity, + const Handle(StepGeom_Axis2Placement3d)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replaces the old axis placement with the new one in the StepGeom_Circle entity. + //! @param theOldEntity the old axis placement. + //! @param theNewEntity the new axis placement to replace the old one. + //! @param theSharing the StepGeom_Circle entity to update. + //! @return true if the axis placement was replaced, false otherwise. + static bool replaceCircle(const Handle(StepGeom_Axis2Placement3d)& theOldEntity, + const Handle(StepGeom_Axis2Placement3d)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replaces the old axis placement with the new one in the StepGeom_PresentationLayerAssignment + //! @param theOldEntity the old axis placement. + //! @param theNewEntity the new axis placement to replace the old one. + //! @param theSharing the StepGeom_PresentationLayerAssignment entity to update. + //! @return true if the axis placement was replaced, false otherwise. + static bool replacePresentationLayerAssignment( + const Handle(StepGeom_Axis2Placement3d)& theOldEntity, + const Handle(StepGeom_Axis2Placement3d)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replaces the old axis placement with the new one in the StepVisual_StyledItem entity. + //! @param theOldEntity the old axis placement. + //! @param theNewEntity the new axis placement to replace the old one. + //! @param theSharing the StepVisual_StyledItem entity to update. + //! @return true if the axis placement was replaced, false otherwise. + static bool replaceStyledItem(const Handle(StepGeom_Axis2Placement3d)& theOldEntity, + const Handle(StepGeom_Axis2Placement3d)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replaces the old axis placement with the new one in the StepGeom_Ellipse entity. + //! @param theOldEntity the old axis placement. + //! @param theNewEntity the new axis placement to replace the old one. + //! @param theSharing the StepGeom_Ellipse entity to update. + //! @return true if the axis placement was replaced, false otherwise. + static bool replaceEllipse(const Handle(StepGeom_Axis2Placement3d)& theOldEntity, + const Handle(StepGeom_Axis2Placement3d)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replaces the old axis placement with the new one in the StepGeom_ConicalSurface entity. + //! @param theOldEntity the old axis placement. + //! @param theNewEntity the new axis placement to replace the old one. + //! @param theSharing the StepGeom_ConicalSurface entity to update. + //! @return true if the axis placement was replaced, false otherwise. + static bool replaceConicalSurface(const Handle(StepGeom_Axis2Placement3d)& theOldEntity, + const Handle(StepGeom_Axis2Placement3d)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replaces the old axis placement with the new one in the StepGeom_ToroidalSurface entity. + //! @param theOldEntity the old axis placement. + //! @param theNewEntity the new axis placement to replace the old one. + //! @param theSharing the StepGeom_ToroidalSurface entity to update. + //! @return true if the axis placement was replaced, false otherwise. + static bool replaceToroidalSurface(const Handle(StepGeom_Axis2Placement3d)& theOldEntity, + const Handle(StepGeom_Axis2Placement3d)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replaces the old axis placement with the new one in the + //! StepShape_AdvancedBrepShapeRepresentation entity. + //! @param theOldEntity the old axis placement. + //! @param theNewEntity the new axis placement to replace the old one. + //! @param theSharing the StepShape_AdvancedBrepShapeRepresentation entity to update. + //! @return true if the axis placement was replaced, false otherwise. + static bool replaceAdvancedBrepShapeRepresentation( + const Handle(StepGeom_Axis2Placement3d)& theOldEntity, + const Handle(StepGeom_Axis2Placement3d)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replaces the old axis placement with the new one in the StepGeom_SphericalSurface entity. + //! @param theOldEntity the old axis placement. + //! @param theNewEntity the new axis placement to replace the old one. + //! @param theSharing the StepGeom_SphericalSurface entity to update. + //! @return true if the axis placement was replaced, false otherwise. + static bool replaceSphericalSurface(const Handle(StepGeom_Axis2Placement3d)& theOldEntity, + const Handle(StepGeom_Axis2Placement3d)& theNewEntity, + Handle(Standard_Transient) theSharing); +}; + +#endif // _MergeSTEPEntities_DirectionProcessor_HeaderFile diff --git a/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_CartesianPointHasher.hxx b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_CartesianPointHasher.hxx new file mode 100644 index 0000000000..25b533ee25 --- /dev/null +++ b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_CartesianPointHasher.hxx @@ -0,0 +1,74 @@ +// 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 _MergeSTEPEntities_CartesianPointHasher_HeaderFile +#define _MergeSTEPEntities_CartesianPointHasher_HeaderFile + +#include +#include +#include + +//! OCCT-style hasher for StepGeom_CartesianPoint entities. +struct MergeSTEPEntities_CartesianPointHasher +{ + // Hashes the Cartesian point by its name and coordinates. + std::size_t operator()(const Handle(StepGeom_CartesianPoint)& theCartesianPoint) const noexcept + { + const std::array& aCoords = theCartesianPoint->Coordinates(); + // If Cartesian point has no name, hash only coordinates. + if (theCartesianPoint->Name().IsNull()) + { + return opencascade::hashBytes(aCoords.data(), static_cast(aCoords.size())); + } + // Otherwise, hash both coordinates and name. + const size_t aHashes[2]{ + opencascade::hashBytes(aCoords.data(), static_cast(aCoords.size())), + std::hash{}(theCartesianPoint->Name()->String())}; + + return opencascade::hashBytes(aHashes, sizeof(aHashes)); + } + + // Compares two Cartesian points by their names and coordinates. + bool operator()(const Handle(StepGeom_CartesianPoint)& theCartesianPoint1, + const Handle(StepGeom_CartesianPoint)& theCartesianPoint2) const noexcept + { + // Compare names. + if (theCartesianPoint1->Name().IsNull() != theCartesianPoint2->Name().IsNull()) + { + return false; + } + if (!theCartesianPoint1->Name()->IsSameString(theCartesianPoint2->Name())) + { + return false; + } + + // Compare coordinates. + constexpr double aTolerance = 1e-12; + const std::array& aCoords1 = theCartesianPoint1->Coordinates(); + const std::array& aCoords2 = theCartesianPoint2->Coordinates(); + if (aCoords1.size() != aCoords2.size()) + { + return false; + } + for (Standard_Integer anIndex = 0; anIndex < aCoords1.size(); ++anIndex) + { + if (std::abs(aCoords1[anIndex] - aCoords2[anIndex]) > aTolerance) + { + return false; + } + } + return true; + } +}; + +#endif // _MergeSTEPEntities_CartesianPointHasher_HeaderFile diff --git a/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_CartesianPointProcessor.cxx b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_CartesianPointProcessor.cxx new file mode 100644 index 0000000000..54fc8c6c69 --- /dev/null +++ b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_CartesianPointProcessor.cxx @@ -0,0 +1,303 @@ +// 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//================================================================================================== + +MergeSTEPEntities_CartesianPointProcessor::MergeSTEPEntities_CartesianPointProcessor( + const Handle(XSControl_WorkSession)& theWS) + : MergeSTEPEntities_EntityProcessor(theWS) +{ + registerReplacer(STANDARD_TYPE(StepGeom_Axis1Placement), replaceAxis1Placement); + registerReplacer(STANDARD_TYPE(StepGeom_Axis2Placement3d), replaceAxis2Placement3d); + registerReplacer(STANDARD_TYPE(StepShape_VertexPoint), replaceVertexPoint); + registerReplacer(STANDARD_TYPE(StepShape_GeometricCurveSet), replaceGeometricCurveSet); + registerReplacer(STANDARD_TYPE(StepVisual_PresentationLayerAssignment), + replacePresentationLayerAssignment); + registerReplacer(STANDARD_TYPE(StepVisual_StyledItem), replaceStyledItem); + registerReplacer(STANDARD_TYPE(StepGeom_BSplineCurveWithKnots), replaceBSplineCurveWithKnots); + registerReplacer(STANDARD_TYPE(StepGeom_Line), replaceLine); + registerReplacer(STANDARD_TYPE(StepGeom_BSplineSurfaceWithKnots), replaceBSplineSurfaceWithKnots); + registerReplacer(STANDARD_TYPE(StepGeom_BSplineCurveWithKnotsAndRationalBSplineCurve), + replaceBSplineCurveWithKnotsAndRationalBSplineCurve); + registerReplacer(STANDARD_TYPE(StepGeom_BSplineSurfaceWithKnotsAndRationalBSplineSurface), + replaceBSplineSurfaceWithKnotsAndRationalBSplineSurface); + registerReplacer(STANDARD_TYPE(StepRepr_Representation), replaceRepresentation); +} + +//================================================================================================== + +bool MergeSTEPEntities_CartesianPointProcessor::replaceAxis2Placement3d( + const Handle(StepGeom_CartesianPoint)& theOldEntity, + const Handle(StepGeom_CartesianPoint)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepGeom_Axis2Placement3d) aSharing = + Handle(StepGeom_Axis2Placement3d)::DownCast(theSharing); + if (aSharing->Location() == theOldEntity) + { + aSharing->SetLocation(theNewEntity); + return true; + } + return false; +} + +//================================================================================================== + +bool MergeSTEPEntities_CartesianPointProcessor::replaceVertexPoint( + const Handle(StepGeom_CartesianPoint)& theOldEntity, + const Handle(StepGeom_CartesianPoint)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepShape_VertexPoint) aSharing = Handle(StepShape_VertexPoint)::DownCast(theSharing); + if (aSharing->VertexGeometry() == theOldEntity) + { + aSharing->SetVertexGeometry(theNewEntity); + return true; + } + return false; +} + +//================================================================================================== + +bool MergeSTEPEntities_CartesianPointProcessor::replaceGeometricCurveSet( + const Handle(StepGeom_CartesianPoint)& theOldEntity, + const Handle(StepGeom_CartesianPoint)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepShape_GeometricSet) aSharing = Handle(StepShape_GeometricSet)::DownCast(theSharing); + bool isReplaced = false; + for (auto& anElement : *aSharing->Elements()) + { + const Handle(StepGeom_Point) aCurrentPoint = anElement.Point(); + if (aCurrentPoint && aCurrentPoint == theOldEntity) + { + anElement.SetValue(theNewEntity); + isReplaced = true; + } + } + return isReplaced; +} + +//================================================================================================== + +bool MergeSTEPEntities_CartesianPointProcessor::replacePresentationLayerAssignment( + const Handle(StepGeom_CartesianPoint)& theOldEntity, + const Handle(StepGeom_CartesianPoint)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepVisual_PresentationLayerAssignment) aSharing = + Handle(StepVisual_PresentationLayerAssignment)::DownCast(theSharing); + bool isReplaced = false; + for (auto& anAssignedItem : *aSharing->AssignedItems()) + { + const Handle(StepRepr_RepresentationItem) aRepItem = anAssignedItem.RepresentationItem(); + if (aRepItem == theOldEntity) + { + anAssignedItem.SetValue(theNewEntity); + isReplaced = true; + } + } + return isReplaced; +} + +//================================================================================================== + +bool MergeSTEPEntities_CartesianPointProcessor::replaceStyledItem( + const Handle(StepGeom_CartesianPoint)& theOldEntity, + const Handle(StepGeom_CartesianPoint)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepVisual_StyledItem) aSharing = Handle(StepVisual_StyledItem)::DownCast(theSharing); + if (aSharing->Item() == theOldEntity) + { + aSharing->SetItem(theNewEntity); + return true; + } + return false; +} + +//================================================================================================== + +bool MergeSTEPEntities_CartesianPointProcessor::replaceBSplineCurveWithKnots( + const Handle(StepGeom_CartesianPoint)& theOldEntity, + const Handle(StepGeom_CartesianPoint)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepGeom_BSplineCurveWithKnots) aSharing = + Handle(StepGeom_BSplineCurveWithKnots)::DownCast(theSharing); + bool isReplaced = false; + Handle(StepGeom_HArray1OfCartesianPoint) aControlPoints = aSharing->ControlPointsList(); + for (Standard_Integer anIndex = aControlPoints->Lower(); anIndex <= aControlPoints->Upper(); + ++anIndex) + { + if (aControlPoints->Value(anIndex) == theOldEntity) + { + aControlPoints->SetValue(anIndex, theNewEntity); + isReplaced = true; + } + } + return isReplaced; +} + +//================================================================================================== + +bool MergeSTEPEntities_CartesianPointProcessor::replaceLine( + const Handle(StepGeom_CartesianPoint)& theOldEntity, + const Handle(StepGeom_CartesianPoint)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepGeom_Line) aSharing = Handle(StepGeom_Line)::DownCast(theSharing); + if (aSharing->Pnt() == theOldEntity) + { + aSharing->SetPnt(theNewEntity); + return true; + } + return false; +} + +//================================================================================================== + +bool MergeSTEPEntities_CartesianPointProcessor::replaceBSplineSurfaceWithKnots( + const Handle(StepGeom_CartesianPoint)& theOldEntity, + const Handle(StepGeom_CartesianPoint)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepGeom_BSplineSurfaceWithKnots) aSharing = + Handle(StepGeom_BSplineSurfaceWithKnots)::DownCast(theSharing); + bool isReplaced = false; + Handle(StepGeom_HArray2OfCartesianPoint) aControlPoints = aSharing->ControlPointsList(); + for (Standard_Integer anIndexI = aControlPoints->LowerRow(); + anIndexI <= aControlPoints->UpperRow(); + ++anIndexI) + { + for (Standard_Integer anIndexJ = aControlPoints->LowerCol(); + anIndexJ <= aControlPoints->UpperCol(); + ++anIndexJ) + { + if (aControlPoints->Value(anIndexI, anIndexJ) == theOldEntity) + { + aControlPoints->SetValue(anIndexI, anIndexJ, theNewEntity); + isReplaced = true; + } + } + } + return isReplaced; +} + +//================================================================================================== + +bool MergeSTEPEntities_CartesianPointProcessor::replaceAxis1Placement( + const Handle(StepGeom_CartesianPoint)& theOldEntity, + const Handle(StepGeom_CartesianPoint)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepGeom_Axis1Placement) aSharing = Handle(StepGeom_Axis1Placement)::DownCast(theSharing); + if (aSharing->Location() == theOldEntity) + { + aSharing->SetLocation(theNewEntity); + return true; + } + return false; +} + +//================================================================================================== + +bool MergeSTEPEntities_CartesianPointProcessor::replaceRepresentation( + const Handle(StepGeom_CartesianPoint)& theOldEntity, + const Handle(StepGeom_CartesianPoint)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepRepr_Representation) aSharing = Handle(StepRepr_Representation)::DownCast(theSharing); + bool isReplaced = false; + Handle(StepRepr_HArray1OfRepresentationItem) anItems = aSharing->Items(); + for (Standard_Integer anIndex = 1; anIndex <= aSharing->NbItems(); ++anIndex) + { + const Handle(StepRepr_RepresentationItem) aRepItem = anItems->Value(anIndex); + if (aRepItem == theOldEntity) + { + anItems->SetValue(anIndex, theNewEntity); + isReplaced = true; + } + } + return isReplaced; +} + +//================================================================================================== + +bool MergeSTEPEntities_CartesianPointProcessor::replaceBSplineCurveWithKnotsAndRationalBSplineCurve( + const Handle(StepGeom_CartesianPoint)& theOldEntity, + const Handle(StepGeom_CartesianPoint)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepGeom_BSplineCurveWithKnotsAndRationalBSplineCurve) aSharing = + Handle(StepGeom_BSplineCurveWithKnotsAndRationalBSplineCurve)::DownCast(theSharing); + bool isReplaced = false; + Handle(StepGeom_HArray1OfCartesianPoint) aControlPoints = aSharing->ControlPointsList(); + for (Standard_Integer anIndex = aControlPoints->Lower(); anIndex <= aControlPoints->Upper(); + ++anIndex) + { + if (aControlPoints->Value(anIndex) == theOldEntity) + { + aControlPoints->SetValue(anIndex, theNewEntity); + isReplaced = true; + } + } + return isReplaced; +} + +//================================================================================================== + +bool MergeSTEPEntities_CartesianPointProcessor:: + replaceBSplineSurfaceWithKnotsAndRationalBSplineSurface( + const Handle(StepGeom_CartesianPoint)& theOldEntity, + const Handle(StepGeom_CartesianPoint)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepGeom_BSplineSurfaceWithKnotsAndRationalBSplineSurface) aSharing = + Handle(StepGeom_BSplineSurfaceWithKnotsAndRationalBSplineSurface)::DownCast(theSharing); + bool isReplaced = false; + Handle(StepGeom_HArray2OfCartesianPoint) aControlPoints = aSharing->ControlPointsList(); + for (Standard_Integer anIndexI = aControlPoints->LowerRow(); + anIndexI <= aControlPoints->UpperRow(); + ++anIndexI) + { + for (Standard_Integer anIndexJ = aControlPoints->LowerCol(); + anIndexJ <= aControlPoints->UpperCol(); + ++anIndexJ) + { + if (aControlPoints->Value(anIndexI, anIndexJ) == theOldEntity) + { + aControlPoints->SetValue(anIndexI, anIndexJ, theNewEntity); + isReplaced = true; + } + } + } + return isReplaced; +} diff --git a/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_CartesianPointProcessor.hxx b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_CartesianPointProcessor.hxx new file mode 100644 index 0000000000..55c8d72768 --- /dev/null +++ b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_CartesianPointProcessor.hxx @@ -0,0 +1,163 @@ +// 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 _MergeSTEPEntities_CartesianPointProcessor_HeaderFile +#define _MergeSTEPEntities_CartesianPointProcessor_HeaderFile + +#include +#include + +#include + +//! Processor for merging StepGeom_CartesianPoint entities. +//! This processor is responsible for merging Cartesian points with the same coordinates and names. +//! It is used to remove duplicate Cartesian points from the STEP model. +//! See MergeSTEPEntities_EntityProcessor for the description of the processor workflow. +class MergeSTEPEntities_CartesianPointProcessor + : public MergeSTEPEntities_EntityProcessor +{ +public: + //! Constructor. Accepts a work session containing the model to process. + //! Registers replacer functions for all supported sharing entities. + //! @param theWS Work session. + Standard_EXPORT MergeSTEPEntities_CartesianPointProcessor( + const Handle(XSControl_WorkSession)& theWS); + +private: + //! Replaces the old Cartesian point with the new Cartesian point in + //! the sharing StepGeom_Axis2Placement3d. + //! @param theOldEntity Old Cartesian point to replace. + //! @param theNewEntity New Cartesian point to replace the old entity with. + //! @param theSharing The StepGeom_Axis2Placement3d sharing the old entity. + //! @return True if the entity was replaced, false if it was not. + static bool replaceAxis2Placement3d(const Handle(StepGeom_CartesianPoint)& theOldEntity, + const Handle(StepGeom_CartesianPoint)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replaces the old Cartesian point with the new Cartesian point in + //! the sharing StepShape_VertexPoint. + //! @param theOldEntity Old Cartesian point to replace. + //! @param theNewEntity New Cartesian point to replace the old entity with. + //! @param theSharing The StepShape_VertexPoint sharing the old entity. + //! @return True if the entity was replaced, false if it was not. + static bool replaceVertexPoint(const Handle(StepGeom_CartesianPoint)& theOldEntity, + const Handle(StepGeom_CartesianPoint)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replaces the old Cartesian point with the new Cartesian point in + //! the sharing StepShape_GeometricSet. + //! @param theOldEntity Old Cartesian point to replace. + //! @param theNewEntity New Cartesian point to replace the old entity with. + //! @param theSharing The StepShape_GeometricSet sharing the old entity. + //! @return True if the entity was replaced, false if it was not. + static bool replaceGeometricCurveSet(const Handle(StepGeom_CartesianPoint)& theOldEntity, + const Handle(StepGeom_CartesianPoint)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replaces the old Cartesian point with the new Cartesian point in + //! the sharing StepVisual_PresentationLayerAssignment. + //! @param theOldEntity Old Cartesian point to replace. + //! @param theNewEntity New Cartesian point to replace the old entity with. + //! @param theSharing The StepVisual_PresentationLayerAssignment sharing the old entity. + //! @return True if the entity was replaced, false if it was not. + static bool replacePresentationLayerAssignment( + const Handle(StepGeom_CartesianPoint)& theOldEntity, + const Handle(StepGeom_CartesianPoint)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replaces the old Cartesian point with the new Cartesian point in + //! the sharing StepVisual_StyledItem. + //! @param theOldEntity Old Cartesian point to replace. + //! @param theNewEntity New Cartesian point to replace the old entity with. + //! @param theSharing The StepVisual_StyledItem sharing the old entity. + //! @return True if the entity was replaced, false if it was not. + static bool replaceStyledItem(const Handle(StepGeom_CartesianPoint)& theOldEntity, + const Handle(StepGeom_CartesianPoint)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replaces the old Cartesian point with the new Cartesian point in + //! the sharing StepGeom_BSplineCurveWithKnots. + //! @param theOldEntity Old Cartesian point to replace. + //! @param theNewEntity New Cartesian point to replace the old entity with. + //! @param theSharing The StepGeom_BSplineCurveWithKnots sharing the old entity. + //! @return True if the entity was replaced, false if it was not. + static bool replaceBSplineCurveWithKnots(const Handle(StepGeom_CartesianPoint)& theOldEntity, + const Handle(StepGeom_CartesianPoint)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replaces the old Cartesian point with the new Cartesian point in + //! the sharing StepGeom_Line. + //! @param theOldEntity Old Cartesian point to replace. + //! @param theNewEntity New Cartesian point to replace the old entity with. + //! @param theSharing The StepGeom_Line sharing the old entity. + //! @return True if the entity was replaced, false if it was not. + static bool replaceLine(const Handle(StepGeom_CartesianPoint)& theOldEntity, + const Handle(StepGeom_CartesianPoint)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replaces the old Cartesian point with the new Cartesian point in + //! the sharing StepGeom_BSplineSurfaceWithKnots. + //! @param theOldEntity Old Cartesian point to replace. + //! @param theNewEntity New Cartesian point to replace the old entity with. + //! @param theSharing The StepGeom_BSplineSurfaceWithKnots sharing the old entity. + //! @return True if the entity was replaced, false if it was not. + static bool replaceBSplineSurfaceWithKnots(const Handle(StepGeom_CartesianPoint)& theOldEntity, + const Handle(StepGeom_CartesianPoint)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replaces the old Cartesian point with the new Cartesian point in + //! the sharing StepGeom_Axis1Placement. + //! @param theOldEntity Old Cartesian point to replace. + //! @param theNewEntity New Cartesian point to replace the old entity with. + //! @param theSharing The StepGeom_Axis1Placement sharing the old entity. + //! @return True if the entity was replaced, false if it was not. + static bool replaceAxis1Placement(const Handle(StepGeom_CartesianPoint)& theOldEntity, + const Handle(StepGeom_CartesianPoint)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replaces the old Cartesian point with the new Cartesian point in + //! the sharing StepRepr_Representation. + //! @param theOldEntity Old Cartesian point to replace. + //! @param theNewEntity New Cartesian point to replace the old entity with. + //! @param theSharing The StepRepr_Representation sharing the old entity. + //! @return True if the entity was replaced, false if it was not. + static bool replaceRepresentation(const Handle(StepGeom_CartesianPoint)& theOldEntity, + const Handle(StepGeom_CartesianPoint)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replaces the old Cartesian point with the new Cartesian point in + //! the sharing StepGeom_BSplineCurveWithKnotsAndRationalBSplineCurve. + //! @param theOldEntity Old Cartesian point to replace. + //! @param theNewEntity New Cartesian point to replace the old entity with. + //! @param theSharing The StepGeom_BSplineCurveWithKnotsAndRationalBSplineCurve sharing the old + //! entity. + //! @return True if the entity was replaced, false if it was not. + static bool replaceBSplineCurveWithKnotsAndRationalBSplineCurve( + const Handle(StepGeom_CartesianPoint)& theOldEntity, + const Handle(StepGeom_CartesianPoint)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replaces the old Cartesian point with the new Cartesian point in + //! the sharing StepGeom_BSplineSurfaceWithKnotsAndRationalBSplineSurface. + //! @param theOldEntity Old Cartesian point to replace. + //! @param theNewEntity New Cartesian point to replace the old entity with. + //! @param theSharing The StepGeom_BSplineSurfaceWithKnotsAndRationalBSplineSurface sharing the + //! old entity. + static bool replaceBSplineSurfaceWithKnotsAndRationalBSplineSurface( + const Handle(StepGeom_CartesianPoint)& theOldEntity, + const Handle(StepGeom_CartesianPoint)& theNewEntity, + Handle(Standard_Transient) theSharing); +}; + +#endif // _MergeSTEPEntities_CartesianPointProcessor_HeaderFile diff --git a/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_CircleHasher.hxx b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_CircleHasher.hxx new file mode 100644 index 0000000000..31208dd527 --- /dev/null +++ b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_CircleHasher.hxx @@ -0,0 +1,101 @@ +// 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 _MergeSTEPEntities_CircleHasher_HeaderFile +#define _MergeSTEPEntities_CircleHasher_HeaderFile + +#include +#include + +#include +#include +#include + +//! OCCT-style hasher for StepGeom_Circle entities. +struct MergeSTEPEntities_CircleHasher +{ + //! Returns hash for a Circle entity. + std::size_t operator()(const Handle(StepGeom_Circle)& theCircle) const noexcept + { + const size_t aPositionHash = + !theCircle->Position().Axis2Placement2d().IsNull() + ? MergeSTEPEntities_Axis2Placement2dHasher{}(theCircle->Position().Axis2Placement2d()) + : !theCircle->Position().Axis2Placement3d().IsNull() + ? MergeSTEPEntities_Axis2Placement3dHasher{}(theCircle->Position().Axis2Placement3d()) + : opencascade::MurmurHash::optimalSeed(); + + const size_t aRadiusHash = opencascade::hash(static_cast(theCircle->Radius())); + + const size_t aHashes[2]{aPositionHash, aRadiusHash}; + const size_t aCombinedHash = opencascade::hashBytes(aHashes, sizeof(aHashes)); + if (theCircle->Name().IsNull()) + { + // If the name is not present, return the hash. + return aCombinedHash; + } + // Add the name to the hash if it is present. + const size_t aHashWithName[2]{ + aCombinedHash, + std::hash{}(theCircle->Name()->String())}; + return opencascade::hashBytes(aHashWithName, sizeof(aHashWithName)); + } + + //! Compares two Circle entities. + bool operator()(const Handle(StepGeom_Circle)& theCircle1, + const Handle(StepGeom_Circle)& theCircle2) const noexcept + { + // Compare names. + if (theCircle1->Name().IsNull() != theCircle2->Name().IsNull()) + { + return false; + } + if (!theCircle1->Name()->IsSameString(theCircle2->Name())) + { + return false; + } + + // Compare axis placements. + if (theCircle1->Position().CaseNumber() != theCircle2->Position().CaseNumber()) + { + return false; + } + + if (theCircle1->Position().CaseNumber() == 1) + { + if (!MergeSTEPEntities_Axis2Placement2dHasher{}(theCircle1->Position().Axis2Placement2d(), + theCircle2->Position().Axis2Placement2d())) + { + return false; + } + } + else if (theCircle1->Position().CaseNumber() == 2) + { + if (!MergeSTEPEntities_Axis2Placement3dHasher{}(theCircle1->Position().Axis2Placement3d(), + theCircle2->Position().Axis2Placement3d())) + { + return false; + } + } + + // Compare radius. + constexpr Standard_Real aTolerance = 1e-12; + if (Abs(theCircle1->Radius() - theCircle2->Radius()) > aTolerance) + { + return false; + } + + return true; + } +}; + +#endif // _MergeSTEPEntities_CircleHasher_HeaderFile diff --git a/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_CircleProcessor.cxx b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_CircleProcessor.cxx new file mode 100644 index 0000000000..32dbbc4fc6 --- /dev/null +++ b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_CircleProcessor.cxx @@ -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. + +// 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 + +#include +#include +#include +#include + +//================================================================================================== + +MergeSTEPEntities_CircleProcessor::MergeSTEPEntities_CircleProcessor( + const Handle(XSControl_WorkSession)& theWS) + : MergeSTEPEntities_EntityProcessor(theWS) +{ + registerReplacer(STANDARD_TYPE(StepShape_EdgeCurve), replaceEdgeCurve); + registerReplacer(STANDARD_TYPE(StepGeom_SurfaceCurve), replaceSurfaceCurve); + registerReplacer(STANDARD_TYPE(StepGeom_SeamCurve), replaceSeamCurve); +} + +//================================================================================================== + +bool MergeSTEPEntities_CircleProcessor::replaceEdgeCurve( + const Handle(StepGeom_Circle)& theOldEntity, + const Handle(StepGeom_Circle)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepShape_EdgeCurve) aSharing = Handle(StepShape_EdgeCurve)::DownCast(theSharing); + if (aSharing->EdgeGeometry() == theOldEntity) + { + aSharing->SetEdgeGeometry(theNewEntity); + return true; + } + return false; +} + +//================================================================================================== + +bool MergeSTEPEntities_CircleProcessor::replaceSurfaceCurve( + const Handle(StepGeom_Circle)& theOldEntity, + const Handle(StepGeom_Circle)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepGeom_SurfaceCurve) aSharing = Handle(StepGeom_SurfaceCurve)::DownCast(theSharing); + if (aSharing->Curve3d() == theOldEntity) + { + aSharing->SetCurve3d(theNewEntity); + return true; + } + return false; +} + +//================================================================================================== + +bool MergeSTEPEntities_CircleProcessor::replaceSeamCurve( + const Handle(StepGeom_Circle)& theOldEntity, + const Handle(StepGeom_Circle)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepGeom_SeamCurve) aSharing = Handle(StepGeom_SeamCurve)::DownCast(theSharing); + if (aSharing->Curve3d() == theOldEntity) + { + aSharing->SetCurve3d(theNewEntity); + return true; + } + return false; +} diff --git a/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_CircleProcessor.hxx b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_CircleProcessor.hxx new file mode 100644 index 0000000000..f46c03a3d1 --- /dev/null +++ b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_CircleProcessor.hxx @@ -0,0 +1,64 @@ +// 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 _MergeSTEPEntities_CircleProcessor_HeaderFile +#define _MergeSTEPEntities_CircleProcessor_HeaderFile + +#include +#include + +#include + +//! Processor for merging StepGeom_Circle entities. +//! This processor merges circles with the same position and radius and names. +class MergeSTEPEntities_CircleProcessor + : public MergeSTEPEntities_EntityProcessor +{ +public: + //! Constructor. Stores the work session and registers replacer functions. + //! @param theWS the work session. + Standard_EXPORT MergeSTEPEntities_CircleProcessor(const Handle(XSControl_WorkSession)& theWS); + +private: + //! Replacer function for StepShape_EdgeCurve entities. + //! Replaces the old entity with the new one in the sharing entity. + //! @param theOldEntity the old entity to replace. + //! @param theNewEntity the new entity to replace with. + //! @param theSharing the sharing entity in which to replace the old entity. + //! @return true if the entity was replaced, false otherwise. + static bool replaceEdgeCurve(const Handle(StepGeom_Circle)& theOldEntity, + const Handle(StepGeom_Circle)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replacer function for StepGeom_SurfaceCurve entities. + //! Replaces the old entity with the new one in the sharing entity. + //! @param theOldEntity the old entity to replace. + //! @param theNewEntity the new entity to replace with. + //! @param theSharing the sharing entity in which to replace the old entity. + //! @return true if the entity was replaced, false otherwise. + static bool replaceSurfaceCurve(const Handle(StepGeom_Circle)& theOldEntity, + const Handle(StepGeom_Circle)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replacer function for StepGeom_SeamCurve entities. + //! Replaces the old entity with the new one in the sharing entity. + //! @param theOldEntity the old entity to replace. + //! @param theNewEntity the new entity to replace with. + //! @param theSharing the sharing entity in which to replace the old entity. + //! @return true if the entity was replaced, false otherwise. + static bool replaceSeamCurve(const Handle(StepGeom_Circle)& theOldEntity, + const Handle(StepGeom_Circle)& theNewEntity, + Handle(Standard_Transient) theSharing); +}; + +#endif // _MergeSTEPEntities_CircleProcessor_HeaderFile diff --git a/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_DirectionHasher.hxx b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_DirectionHasher.hxx new file mode 100644 index 0000000000..a9a93c56ad --- /dev/null +++ b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_DirectionHasher.hxx @@ -0,0 +1,78 @@ +// 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 _MergeSTEPEntities_DirectionHasher_HeaderFile +#define _MergeSTEPEntities_DirectionHasher_HeaderFile + +#include +#include +#include + +//! OCCT-style hasher for StepGeom_Direction entities. +struct MergeSTEPEntities_DirectionHasher +{ + // Hashes the direction by its name and direction ratios. + std::size_t operator()(const Handle(StepGeom_Direction)& theDirection) const noexcept + { + // Prepare an array of direction ratios. + const Handle(TColStd_HArray1OfReal) aCoords = theDirection->DirectionRatios(); + int anArray[3]{}; + for (int anIndex = aCoords->Lower(); anIndex < aCoords->Upper(); ++anIndex) + { + anArray[anIndex] = static_cast(aCoords->Value(anIndex)); + } + // If direction has no name, hash only direction ratios. + if (theDirection->Name().IsNull()) + { + return opencascade::hashBytes(anArray, sizeof(anArray)); + } + // Otherwise, hash both direction ratios and name. + const size_t aHashes[2]{opencascade::hashBytes(anArray, sizeof(anArray)), + std::hash{}(theDirection->Name()->String())}; + return opencascade::hashBytes(aHashes, sizeof(aHashes)); + } + + // Compares two directions by their names and direction ratios. + bool operator()(const Handle(StepGeom_Direction)& theDirection1, + const Handle(StepGeom_Direction)& theDirection2) const noexcept + { + // Compare names. + if (theDirection1->Name().IsNull() != theDirection2->Name().IsNull()) + { + return false; + } + if (!theDirection1->Name()->IsSameString(theDirection2->Name())) + { + return false; + } + + // Compare coordinates. + constexpr double aTolerance = 1e-12; + const Handle(TColStd_HArray1OfReal) aCoords1 = theDirection1->DirectionRatios(); + const Handle(TColStd_HArray1OfReal) aCoords2 = theDirection2->DirectionRatios(); + if (aCoords1->Length() != aCoords2->Length()) + { + return false; + } + for (Standard_Integer i = aCoords1->Lower(); i <= aCoords1->Upper(); ++i) + { + if (std::abs(aCoords1->Value(i) - aCoords2->Value(i)) > aTolerance) + { + return false; + } + } + return true; + } +}; + +#endif // _MergeSTEPEntities_DirectionHasher_HeaderFile diff --git a/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_DirectionProcessor.cxx b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_DirectionProcessor.cxx new file mode 100644 index 0000000000..c561818db8 --- /dev/null +++ b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_DirectionProcessor.cxx @@ -0,0 +1,98 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +// 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 + +#include +#include +#include +#include + +//================================================================================================== + +MergeSTEPEntities_DirectionProcessor::MergeSTEPEntities_DirectionProcessor( + const Handle(XSControl_WorkSession)& theWS) + : MergeSTEPEntities_EntityProcessor( + theWS) +{ + registerReplacer(STANDARD_TYPE(StepGeom_Axis1Placement), replaceAxis1Placement); + registerReplacer(STANDARD_TYPE(StepGeom_Axis2Placement3d), replaceAxis2Placement3d); + registerReplacer(STANDARD_TYPE(StepGeom_Vector), replaceVector); +} + +//================================================================================================== + +bool MergeSTEPEntities_DirectionProcessor::replaceAxis1Placement( + const Handle(StepGeom_Direction)& theOldEntity, + const Handle(StepGeom_Direction)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepGeom_Axis1Placement) aSharing = Handle(StepGeom_Axis1Placement)::DownCast(theSharing); + if (aSharing->Axis() == theOldEntity) + { + aSharing->SetAxis(theNewEntity); + return true; + } + return false; +} + +//================================================================================================== + +bool MergeSTEPEntities_DirectionProcessor::replaceAxis2Placement3d( + const Handle(StepGeom_Direction)& theOldEntity, + const Handle(StepGeom_Direction)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepGeom_Axis2Placement3d) aSharing = + Handle(StepGeom_Axis2Placement3d)::DownCast(theSharing); + if (aSharing->Axis() == theOldEntity) + { + aSharing->SetAxis(theNewEntity); + return true; + } + else if (aSharing->RefDirection() == theOldEntity) + { + aSharing->SetRefDirection(theNewEntity); + return true; + } + return false; +} + +//================================================================================================== + +bool MergeSTEPEntities_DirectionProcessor::replaceVector( + const Handle(StepGeom_Direction)& theOldEntity, + const Handle(StepGeom_Direction)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepGeom_Vector) aSharing = Handle(StepGeom_Vector)::DownCast(theSharing); + if (aSharing->Orientation() == theOldEntity) + { + aSharing->SetOrientation(theNewEntity); + return true; + } + return false; +} diff --git a/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_DirectionProcessor.hxx b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_DirectionProcessor.hxx new file mode 100644 index 0000000000..2e69e5a69f --- /dev/null +++ b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_DirectionProcessor.hxx @@ -0,0 +1,65 @@ +// 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 _MergeSTEPEntities_DirectionProcessor_HeaderFile +#define _MergeSTEPEntities_DirectionProcessor_HeaderFile + +#include +#include + +#include + +//! Processor for merging StepGeom_Direction entities. +//! This processor merges directions with the same direction ratios and names. +//! The processor replaces all occurrences of the old directions with the new ones. +//! The processor does not remove old directions from the model. +//! See MergeSTEPEntities_EntityProcessor for the description of the processor workflow. +class MergeSTEPEntities_DirectionProcessor + : public MergeSTEPEntities_EntityProcessor +{ +public: + //! Constructor. Stores the work session and registers replacer functions. + //! @param theWS the work session. + Standard_EXPORT MergeSTEPEntities_DirectionProcessor(const Handle(XSControl_WorkSession)& theWS); + +private: + //! Replaces the old direction with the new one in the StepGeom_Axis1Placement entity. + //! @param theOldEntity the old direction. + //! @param theNewEntity the new direction. + //! @param theSharing the StepGeom_Axis1Placement entity to update. + //! @return true if the direction was replaced, false otherwise. + static bool replaceAxis1Placement(const Handle(StepGeom_Direction)& theOldEntity, + const Handle(StepGeom_Direction)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replaces the old direction with the new one in the StepGeom_Axis2Placement3d entity. + //! @param theOldEntity the old direction. + //! @param theNewEntity the new direction. + //! @param theSharing the StepGeom_Axis2Placement3d entity to update. + //! @return true if the direction was replaced, false otherwise. + static bool replaceAxis2Placement3d(const Handle(StepGeom_Direction)& theOldEntity, + const Handle(StepGeom_Direction)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replaces the old direction with the new one in the StepGeom_Vector entity. + //! @param theOldEntity the old direction. + //! @param theNewEntity the new direction. + //! @param theSharing the StepGeom_Vector entity to update. + //! @return true if the direction was replaced, false otherwise. + static bool replaceVector(const Handle(StepGeom_Direction)& theOldEntity, + const Handle(StepGeom_Direction)& theNewEntity, + Handle(Standard_Transient) theSharing); +}; + +#endif // _MergeSTEPEntities_DirectionProcessor_HeaderFile diff --git a/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_EntityProcessor.hxx b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_EntityProcessor.hxx new file mode 100644 index 0000000000..2d22742723 --- /dev/null +++ b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_EntityProcessor.hxx @@ -0,0 +1,241 @@ +// 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 _MergeSTEPEntities_EntityProcessor_HeaderFile +#define _MergeSTEPEntities_EntityProcessor_HeaderFile + +#include +#include +#include +#include +#include +#include + +#include + +//! Base class for removing duplicate entities. +//! Implements the logic of processing entities and removing duplicates. +//! Child classes should only implement and register replacer functions +//! for each specific type of sharing entity. +//! How to use: +//! 1. Create an instance of the child class. +//! 2. Add entities to the processor using ProcessEntity() method. Entities +//! that can be merged will be stored in the internal map, others will be ignored. +//! 3. Call Perform() method to replace duplicate entities. After this call +//! all duplicate entities will be replaced in a model with the first processed entity +//! that is evaluated as equal to them. +//! IMPORTANT: Duplicated entities will be replaced but not removed from the model! +//! 4. Call GetReplacedEntities() to get a list of replaced duplicates. This list can be used +//! to remove entities from the model. +//! @tparam ProcessedType Type of the processed entities. +//! @tparam ProcessedTypeHasher OCCT-Style hasher for the processed entities. +template +class MergeSTEPEntities_EntityProcessor +{ +protected: + // Map of duplicate entities. Key is the first processed entity, value is a list of duplicates. + using DuplicateMap = NCollection_DataMap, + ProcessedTypeHasher>; + // Function to replace an entity in sharings. First argument is the old entity, second is the new + // entity, third is the sharing in which the entity should be replaced. Returns true if the entity + // was replaced, false otherwise. + using ReplacerFunction = std::function; + // Map of replacer functions. Key is the type of the sharing entity, value is the replacer + // function for this type. + using ReplacerMap = NCollection_DataMap; + +protected: + //! Constructor. Accepts a work session containing the model to process. + //! Protected to prevent direct instantiation of the base class. Only child classes should be + //! allowed to instantiate. + //! @param theWS Work session. + MergeSTEPEntities_EntityProcessor(const Handle(XSControl_WorkSession)& theWS); + +public: + //! Function to process an entity. If the entity can be merged, it will be stored in the internal + //! map. If the entity cannot be merged, it will be ignored. + //! Entity can only be processed if: + //! 1. The type of entity is ProcessedType. + //! 2. All sharings of the entity have a registered replacer function. + //! @param theEntity Entity to process. + //! @return True if the entity was processed, false if it was ignored. + Standard_EXPORT bool ProcessEntity(const Handle(Standard_Transient)& theEntity); + + //! Function to replace duplicate entities. After this call, all duplicate entities will be + //! replaced with the first processed entity that is evaluated as equal to them. + //! IMPORTANT: Duplicated entities will be replaced but not removed from the model! + //! @param theReplacedEntities List where replaced entities will be stored. + //! This list can be used to remove entities from the model. + Standard_EXPORT void Perform(TColStd_MapOfTransient& theReplacedEntities); + +protected: + //! Register a replacer function for a specific type of sharing entity. + //! Should be used by child classes to register replacer functions for each specific type of + //! sharing entity. If a sharing entity of the specified type is encountered during processing, + //! the registered replacer function will be called to replace the old entity with the new one. + //! All replacers must be registered before calling ProcessEntity() method. + //! @param theType Type of the sharing entity. + //! @param theReplacer Replacer function. + void registerReplacer(const Handle(Standard_Type)& theType, const ReplacerFunction& theReplacer); + +public: + //! Checks if all sharings have registered replacers for their types. + //! @param theSharings List of sharings to check. + //! @return True if all sharings have registered replacers, false otherwise. + bool hasAllReplacers(const Handle(TColStd_HSequenceOfTransient)& theSharings) const; + + //! Replaces an old entity with a new entity in sharings. + //! Should only be called if all sharings have registered replacers. + //! @param theOldEntity Old entity to replace. + //! @param theNewEntity New entity to replace old entity with. + //! @param theSharings List of old entity sharings to replace the entity in. + //! @return True if all entities were replaced, false if at least one entity was not replaced. + bool replaceInSharings(const Handle(ProcessedType)& theOldEntity, + const Handle(ProcessedType)& theNewEntity, + const Handle(TColStd_HSequenceOfTransient)& theSharings) const; + +private: + Handle(XSControl_WorkSession) myWS; //!< Work session. + ReplacerMap myReplacerMap; //!< Map of replacer functions. + DuplicateMap myDuplicateMap; //!< Map of duplicate entities. +}; + +//================================================================================================== + +template +MergeSTEPEntities_EntityProcessor:: + MergeSTEPEntities_EntityProcessor(const Handle(XSControl_WorkSession)& theWS) + : myWS(theWS), + myReplacerMap(), + myDuplicateMap() +{ +} + +//================================================================================================== + +template +bool MergeSTEPEntities_EntityProcessor::ProcessEntity( + const Handle(Standard_Transient)& theEntity) +{ + const Handle(ProcessedType) anEntity = Handle(ProcessedType)::DownCast(theEntity); + if (anEntity.IsNull()) + { + return false; + } + const Interface_Graph& aGraph = myWS->Graph(); + const Handle(TColStd_HSequenceOfTransient) aSharings = aGraph.GetSharings(anEntity); + if (hasAllReplacers(aSharings)) + { + std::vector* anIter = myDuplicateMap.ChangeSeek(anEntity); + if (anIter == nullptr) + { + // Add as a new key. + myDuplicateMap.Bind(anEntity, std::vector{}); + } + else + { + // Add as a value. + anIter->push_back(anEntity); + } + } + + return true; +} + +//================================================================================================== + +template +void MergeSTEPEntities_EntityProcessor::Perform( + TColStd_MapOfTransient& theReplacedEntities) +{ + for (DuplicateMap::Iterator anIter(myDuplicateMap); anIter.More(); anIter.Next()) + { + const Handle(ProcessedType)& anEntity = anIter.Key(); + const std::vector& aDuplicates = anIter.Value(); + if (aDuplicates.empty()) + { + continue; + } + + const Interface_Graph& aGraph = myWS->Graph(); + for (const auto& aDuplicate : aDuplicates) + { + Handle(TColStd_HSequenceOfTransient) aSharings = aGraph.GetSharings(aDuplicate); + if (aSharings.IsNull()) + { + continue; + } + + if (replaceInSharings(aDuplicate, anEntity, aSharings)) + { + theReplacedEntities.Add(aDuplicate); + } + } + } +} + +//================================================================================================== + +template +void MergeSTEPEntities_EntityProcessor::registerReplacer( + const Handle(Standard_Type)& theType, + const ReplacerFunction& theReplacer) +{ + myReplacerMap.Bind(theType, theReplacer); +} + +//================================================================================================== + +template +bool MergeSTEPEntities_EntityProcessor::hasAllReplacers( + const Handle(TColStd_HSequenceOfTransient)& theSharings) const +{ + if (theSharings.IsNull()) + { + return false; + } + return std::all_of(theSharings->cbegin(), + theSharings->cend(), + [this](const Handle(Standard_Transient)& theSharing) { + return myReplacerMap.IsBound(theSharing->DynamicType()); + }); +} + +//================================================================================================== +template +bool MergeSTEPEntities_EntityProcessor::replaceInSharings( + const Handle(ProcessedType)& theOldEntity, + const Handle(ProcessedType)& theNewEntity, + const Handle(TColStd_HSequenceOfTransient)& theSharings) const +{ + bool isAllReplaced = true; + for (const auto& aSharing : *theSharings) + { + if (aSharing.IsNull()) + { + continue; + } + + const ReplacerFunction& aReplacer = myReplacerMap.Find(aSharing->DynamicType()); + if (!aReplacer(theOldEntity, theNewEntity, aSharing)) + { + isAllReplaced = false; + } + } + return isAllReplaced; +} + +#endif // _MergeSTEPEntities_EntityProcessor_HeaderFile diff --git a/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_LineHasher.hxx b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_LineHasher.hxx new file mode 100644 index 0000000000..3533d55af2 --- /dev/null +++ b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_LineHasher.hxx @@ -0,0 +1,77 @@ +// 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 _MergeSTEPEntities_LineHasher_HeaderFile +#define _MergeSTEPEntities_LineHasher_HeaderFile + +#include +#include + +#include +#include +#include + +//! OCCT-style hasher for StepGeom_Line entities. +struct MergeSTEPEntities_LineHasher +{ + // Hashes the Line by its name and Line ratios. + std::size_t operator()(const Handle(StepGeom_Line)& theLine) const noexcept + { + const size_t aHashes[2]{MergeSTEPEntities_CartesianPointHasher{}(theLine->Pnt()), + MergeSTEPEntities_VectorHasher{}(theLine->Dir())}; + + const size_t aCombinedHash = opencascade::hashBytes(aHashes, sizeof(aHashes)); + if (theLine->Name().IsNull()) + { + // If the name is not present, return the hash. + return aCombinedHash; + } + // Add the name to the hash if it is present. + const size_t aCombinedHashWithName[2]{ + aCombinedHash, + std::hash{}(theLine->Name()->String())}; + return opencascade::hashBytes(aCombinedHashWithName, sizeof(aCombinedHashWithName)); + } + + // Compares two Lines by their names and Line ratios. + bool operator()(const Handle(StepGeom_Line)& theLine1, + const Handle(StepGeom_Line)& theLine2) const noexcept + + { + // Compare names. + if (theLine1->Name().IsNull() != theLine2->Name().IsNull()) + { + return false; + } + if (!theLine1->Name()->IsSameString(theLine2->Name())) + { + return false; + } + + // Compare points. + if (!MergeSTEPEntities_CartesianPointHasher{}(theLine1->Pnt(), theLine2->Pnt())) + { + return false; + } + + // Compare directions. + if (!MergeSTEPEntities_VectorHasher{}(theLine1->Dir(), theLine2->Dir())) + { + return false; + } + + return true; + } +}; + +#endif // _MergeSTEPEntities_LineHasher_HeaderFile diff --git a/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_LineProcessor.cxx b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_LineProcessor.cxx new file mode 100644 index 0000000000..03f9d02ea9 --- /dev/null +++ b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_LineProcessor.cxx @@ -0,0 +1,131 @@ +// 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. + +// 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 + +#include +#include +#include +#include +#include +#include + +//================================================================================================== + +MergeSTEPEntities_LineProcessor::MergeSTEPEntities_LineProcessor( + const Handle(XSControl_WorkSession)& theWS) + : MergeSTEPEntities_EntityProcessor(theWS) +{ + registerReplacer(STANDARD_TYPE(StepShape_EdgeCurve), replaceEdgeCurve); + registerReplacer(STANDARD_TYPE(StepGeom_TrimmedCurve), replaceTrimmedCurve); + registerReplacer(STANDARD_TYPE(StepGeom_SurfaceCurve), replaceSurfaceCurve); + registerReplacer(STANDARD_TYPE(StepRepr_DefinitionalRepresentation), + replaceDefinitionalRepresentation); + registerReplacer(STANDARD_TYPE(StepGeom_SeamCurve), replaceSeamCurve); +} + +//================================================================================================== + +bool MergeSTEPEntities_LineProcessor::replaceEdgeCurve(const Handle(StepGeom_Line)& theOldEntity, + const Handle(StepGeom_Line)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepShape_EdgeCurve) aSharing = Handle(StepShape_EdgeCurve)::DownCast(theSharing); + if (aSharing->EdgeGeometry() == theOldEntity) + { + aSharing->SetEdgeGeometry(theNewEntity); + return true; + } + return false; +} + +//================================================================================================== + +bool MergeSTEPEntities_LineProcessor::replaceTrimmedCurve(const Handle(StepGeom_Line)& theOldEntity, + const Handle(StepGeom_Line)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepGeom_TrimmedCurve) aSharing = Handle(StepGeom_TrimmedCurve)::DownCast(theSharing); + if (aSharing->BasisCurve() == theOldEntity) + { + aSharing->SetBasisCurve(theNewEntity); + return true; + } + return false; +} + +//================================================================================================== + +bool MergeSTEPEntities_LineProcessor::replaceSurfaceCurve(const Handle(StepGeom_Line)& theOldEntity, + const Handle(StepGeom_Line)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepGeom_SurfaceCurve) aSharing = Handle(StepGeom_SurfaceCurve)::DownCast(theSharing); + if (aSharing->Curve3d() == theOldEntity) + { + aSharing->SetCurve3d(theNewEntity); + return true; + } + return false; +} + +//================================================================================================== + +bool MergeSTEPEntities_LineProcessor::replaceDefinitionalRepresentation( + const Handle(StepGeom_Line)& theOldEntity, + const Handle(StepGeom_Line)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepRepr_DefinitionalRepresentation) aSharing = + Handle(StepRepr_DefinitionalRepresentation)::DownCast(theSharing); + bool isReplaced = false; + Handle(StepRepr_HArray1OfRepresentationItem) anItems = aSharing->Items(); + for (Standard_Integer anIndex = 1; anIndex <= aSharing->NbItems(); ++anIndex) + { + const Handle(StepRepr_RepresentationItem) aRepItem = anItems->Value(anIndex); + if (aRepItem == theOldEntity) + { + anItems->SetValue(anIndex, theNewEntity); + isReplaced = true; + } + } + return isReplaced; +} + +//================================================================================================== + +bool MergeSTEPEntities_LineProcessor::replaceSeamCurve(const Handle(StepGeom_Line)& theOldEntity, + const Handle(StepGeom_Line)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepGeom_SeamCurve) aSharing = Handle(StepGeom_SeamCurve)::DownCast(theSharing); + if (aSharing->Curve3d() == theOldEntity) + { + aSharing->SetCurve3d(theNewEntity); + return true; + } + return false; +} diff --git a/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_LineProcessor.hxx b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_LineProcessor.hxx new file mode 100644 index 0000000000..791ef42189 --- /dev/null +++ b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_LineProcessor.hxx @@ -0,0 +1,84 @@ +// 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 _MergeSTEPEntities_LineProcessor_HeaderFile +#define _MergeSTEPEntities_LineProcessor_HeaderFile + +#include +#include + +#include + +//! Processor for merging StepGeom_Line entities. +//! This processor merges lines with the same point and direction and names. +class MergeSTEPEntities_LineProcessor + : public MergeSTEPEntities_EntityProcessor +{ +public: + //! Constructor. Stores the work session and registers replacer functions. + //! @param theWS the work session. + Standard_EXPORT MergeSTEPEntities_LineProcessor(const Handle(XSControl_WorkSession)& theWS); + +private: + //! Replacer function for StepShape_EdgeCurve entities. + //! Replaces the old entity with the new one in the sharing entity. + //! @param theOldEntity the old entity to replace. + //! @param theNewEntity the new entity to replace with. + //! @param theSharing the sharing entity in which to replace the old entity. + //! @return true if the entity was replaced, false otherwise. + static bool replaceEdgeCurve(const Handle(StepGeom_Line)& theOldEntity, + const Handle(StepGeom_Line)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replacer function for StepGeom_TrimmedCurve entities. + //! Replaces the old entity with the new one in the sharing entity. + //! @param theOldEntity the old entity to replace. + //! @param theNewEntity the new entity to replace with. + //! @param theSharing the sharing entity in which to replace the old entity. + //! @return true if the entity was replaced, false otherwise. + static bool replaceTrimmedCurve(const Handle(StepGeom_Line)& theOldEntity, + const Handle(StepGeom_Line)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replacer function for StepGeom_SurfaceCurve entities. + //! Replaces the old entity with the new one in the sharing entity. + //! @param theOldEntity the old entity to replace. + //! @param theNewEntity the new entity to replace with. + //! @param theSharing the sharing entity in which to replace the old entity. + //! @return true if the entity was replaced, false otherwise. + static bool replaceSurfaceCurve(const Handle(StepGeom_Line)& theOldEntity, + const Handle(StepGeom_Line)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replacer function for StepRepr_DefinitionalRepresentation entities. + //! Replaces the old entity with the new one in the sharing entity. + //! @param theOldEntity the old entity to replace. + //! @param theNewEntity the new entity to replace with. + //! @param theSharing the sharing entity in which to replace the old entity. + //! @return true if the entity was replaced, false otherwise. + static bool replaceDefinitionalRepresentation(const Handle(StepGeom_Line)& theOldEntity, + const Handle(StepGeom_Line)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replacer function for StepGeom_SeamCurve entities. + //! Replaces the old entity with the new one in the sharing entity. + //! @param theOldEntity the old entity to replace. + //! @param theNewEntity the new entity to replace with. + //! @param theSharing the sharing entity in which to replace the old entity. + //! @return true if the entity was replaced, false otherwise. + static bool replaceSeamCurve(const Handle(StepGeom_Line)& theOldEntity, + const Handle(StepGeom_Line)& theNewEntity, + Handle(Standard_Transient) theSharing); +}; + +#endif // _MergeSTEPEntities_LineProcessor_HeaderFile diff --git a/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_Merger.cxx b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_Merger.cxx new file mode 100644 index 0000000000..044c98548e --- /dev/null +++ b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_Merger.cxx @@ -0,0 +1,123 @@ +// 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//================================================================================================== + +MergeSTEPEntities_Merger::MergeSTEPEntities_Merger(Handle(XSControl_WorkSession) theWS) + : myWS(theWS) +{ +} + +//================================================================================================== + +void MergeSTEPEntities_Merger::Perform() +{ + Handle(StepData_StepModel) aModel = Handle(StepData_StepModel)::DownCast(myWS->Model()); + if (aModel.IsNull()) + { + return; + } + + //! Initialize processors. + MergeSTEPEntities_CartesianPointProcessor aCartesianPointProcessor(myWS); + MergeSTEPEntities_DirectionProcessor aDirectionProcessor(myWS); + MergeSTEPEntities_Axis2Placement3dProcessor aAxis2Placement3dProcessor(myWS); + MergeSTEPEntities_VectorProcessor aVectorProcessor(myWS); + MergeSTEPEntities_LineProcessor aLineProcessor(myWS); + MergeSTEPEntities_PlaneProcessor aPlaneProcessor(myWS); + MergeSTEPEntities_CircleProcessor aCircleProcessor(myWS); + + // Process all entities. + for (Standard_Integer anIndex = 1; anIndex <= aModel->NbEntities(); ++anIndex) + { + const Handle(Standard_Transient) anEntity = aModel->Value(anIndex); + aCartesianPointProcessor.ProcessEntity(anEntity); + aDirectionProcessor.ProcessEntity(anEntity); + aAxis2Placement3dProcessor.ProcessEntity(anEntity); + aVectorProcessor.ProcessEntity(anEntity); + aLineProcessor.ProcessEntity(anEntity); + aPlaneProcessor.ProcessEntity(anEntity); + aCircleProcessor.ProcessEntity(anEntity); + } + + // Perform replacement of duplicate entities. + TColStd_MapOfTransient aReplacedEntities; + aCartesianPointProcessor.Perform(aReplacedEntities); + aDirectionProcessor.Perform(aReplacedEntities); + aAxis2Placement3dProcessor.Perform(aReplacedEntities); + aVectorProcessor.Perform(aReplacedEntities); + aLineProcessor.Perform(aReplacedEntities); + aPlaneProcessor.Perform(aReplacedEntities); + aCircleProcessor.Perform(aReplacedEntities); + + // Remove duplicate entities. + removeEntities(aReplacedEntities); +} + +//================================================================================================== + +void MergeSTEPEntities_Merger::removeEntities(const TColStd_MapOfTransient& theToRemove) +{ + if (theToRemove.IsEmpty()) + { + return; + } + // Remove entities. + Handle(StepData_StepModel) anIntermediateModel = new StepData_StepModel(); + Handle(StepData_StepModel) aReadModel = Handle(StepData_StepModel)::DownCast(myWS->Model()); + anIntermediateModel->SetProtocol(aReadModel->Protocol()); + anIntermediateModel->SetGTool(aReadModel->GTool()); + + for (Standard_Integer i = 1; i <= aReadModel->NbEntities(); i++) + { + const Handle(Standard_Transient)& anEnt = aReadModel->Value(i); + if (!theToRemove.Contains(anEnt)) + { + anIntermediateModel->AddWithRefs(anEnt); + } + } + + myWS->SetModel(anIntermediateModel); + myWS->ComputeGraph(); + + // Clean hanged entities. + Handle(StepData_StepModel) aNewModel = new StepData_StepModel(); + aNewModel->SetProtocol(anIntermediateModel->Protocol()); + aNewModel->SetGTool(anIntermediateModel->GTool()); + const auto& aGraph = myWS->Graph(); + + for (Standard_Integer i = 1; i <= anIntermediateModel->NbEntities(); i++) + { + const Handle(Standard_Transient)& anEnt = anIntermediateModel->Value(i); + if (aGraph.Shareds(anEnt).NbEntities() > 0 || aGraph.Sharings(anEnt).NbEntities() > 0) + { + aNewModel->AddWithRefs(anEnt); + } + } + + myWS->SetModel(aNewModel); + myWS->ComputeGraph(); +} diff --git a/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_Merger.hxx b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_Merger.hxx new file mode 100644 index 0000000000..436ea414a6 --- /dev/null +++ b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_Merger.hxx @@ -0,0 +1,46 @@ +// 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 _MergeSTEPEntities_Merger_HeaderFile +#define _MergeSTEPEntities_Merger_HeaderFile + +#include + +class XSControl_WorkSession; + +//! A class to merge STEP entities. +//! This class is used to merge equal STEP entities in the work session and remove duplicates. +//! More detailed information about merging entities cam be found in +//! MergeSTEPEntities_EntityProcessor class and its descendants. +class MergeSTEPEntities_Merger +{ +public: + //! Constructor. + //! @param theWS the work session to merge entities in. + Standard_EXPORT MergeSTEPEntities_Merger(Handle(XSControl_WorkSession) theWS); + + //! Perform the merging of entities. + //! All entities in a model stored in the provided work session that are considered equal to + //! each other will be merged, and duplicates will be removed. + Standard_EXPORT void Perform(); + +private: + //! Remove entities from the work session. + //! @param theToRemove the entities to remove. + void removeEntities(const TColStd_MapOfTransient& theToRemove); + +private: + Handle(XSControl_WorkSession) myWS; //!< The work session containing the model with entities. +}; + +#endif // _MergeSTEPEntities_Merger_HeaderFile diff --git a/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_PlaneHasher.hxx b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_PlaneHasher.hxx new file mode 100644 index 0000000000..3e8ab9ab94 --- /dev/null +++ b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_PlaneHasher.hxx @@ -0,0 +1,60 @@ +// 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 _MergeSTEPEntities_PlaneHasher_HeaderFile +#define _MergeSTEPEntities_PlaneHasher_HeaderFile + +#include + +#include +#include + +//! OCCT-style hasher for StepGeom_Plane entities. +struct MergeSTEPEntities_PlaneHasher +{ + // Hashes the axis Planes. + std::size_t operator()(const Handle(StepGeom_Plane)& thePlane) const noexcept + { + const size_t aHash = MergeSTEPEntities_Axis2Placement3dHasher{}(thePlane->Position()); + if (thePlane->Name().IsNull()) + { + // If the name is not present, return the hash. + return aHash; + } + // Add the name to the hash if it is present. + const size_t aCombinedHashWithName[2]{ + aHash, + std::hash{}(thePlane->Name()->String())}; + return opencascade::hashBytes(aCombinedHashWithName, sizeof(aCombinedHashWithName)); + } + + // Compares two axis Planes. + bool operator()(const Handle(StepGeom_Plane)& thePlane1, + const Handle(StepGeom_Plane)& thePlane2) const noexcept + { + // Compare names. + if (thePlane1->Name().IsNull() != thePlane2->Name().IsNull()) + { + return false; + } + if (!thePlane1->Name()->IsSameString(thePlane2->Name())) + { + return false; + } + + // Compare axis Planes. + return MergeSTEPEntities_Axis2Placement3dHasher{}(thePlane1->Position(), thePlane2->Position()); + } +}; + +#endif // _MergeSTEPEntities_PlaneHasher_HeaderFile diff --git a/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_PlaneProcessor.cxx b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_PlaneProcessor.cxx new file mode 100644 index 0000000000..24ec850ea4 --- /dev/null +++ b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_PlaneProcessor.cxx @@ -0,0 +1,57 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include + +#include +#include + +//================================================================================================== + +MergeSTEPEntities_PlaneProcessor::MergeSTEPEntities_PlaneProcessor( + const Handle(XSControl_WorkSession)& theWS) + : MergeSTEPEntities_EntityProcessor(theWS) +{ + registerReplacer(STANDARD_TYPE(StepShape_AdvancedFace), replaceAdvancedFace); + registerReplacer(STANDARD_TYPE(StepGeom_Pcurve), replacePcurve); +} + +//================================================================================================== + +bool MergeSTEPEntities_PlaneProcessor::replaceAdvancedFace( + const Handle(StepGeom_Plane)& theOldEntity, + const Handle(StepGeom_Plane)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepShape_AdvancedFace) aSharing = Handle(StepShape_AdvancedFace)::DownCast(theSharing); + if (aSharing->FaceGeometry() == theOldEntity) + { + aSharing->SetFaceGeometry(theNewEntity); + return true; + } + return false; +} + +//================================================================================================== +bool MergeSTEPEntities_PlaneProcessor::replacePcurve(const Handle(StepGeom_Plane)& theOldEntity, + const Handle(StepGeom_Plane)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepGeom_Pcurve) aSharing = Handle(StepGeom_Pcurve)::DownCast(theSharing); + if (aSharing->BasisSurface() == theOldEntity) + { + aSharing->SetBasisSurface(theNewEntity); + return true; + } + return false; +} diff --git a/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_PlaneProcessor.hxx b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_PlaneProcessor.hxx new file mode 100644 index 0000000000..44dde07449 --- /dev/null +++ b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_PlaneProcessor.hxx @@ -0,0 +1,54 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#ifndef _MergeSTEPEntities_PlaneProcessor_HeaderFile +#define _MergeSTEPEntities_PlaneProcessor_HeaderFile + +#include +#include + +#include + +//! Processor for merging StepGeom_Plane entities. +//! This processor merges planes with the same names and placements. +class MergeSTEPEntities_PlaneProcessor + : public MergeSTEPEntities_EntityProcessor +{ +public: + //! Constructor. Stores the work session and registers replacer functions. + //! @param theWS the work session. + Standard_EXPORT MergeSTEPEntities_PlaneProcessor(const Handle(XSControl_WorkSession)& theWS); + +private: + //! Replacer function for StepShape_AdvancedFace entities. + //! Replaces the old entity with the new one in the sharing entity. + //! @param theOldEntity the old entity to replace. + //! @param theNewEntity the new entity to replace with. + //! @param theSharing the sharing StepShape_AdvancedFace in which to replace the old entity. + //! @return true if the entity was replaced, false otherwise. + static bool replaceAdvancedFace(const Handle(StepGeom_Plane)& theOldEntity, + const Handle(StepGeom_Plane)& theNewEntity, + Handle(Standard_Transient) theSharing); + + //! Replacer function for StepGeom_Pcurve entities. + //! Replaces the old entity with the new one in the sharing entity. + //! @param theOldEntity the old entity to replace. + //! @param theNewEntity the new entity to replace with. + //! @param theSharing the sharing StepGeom_Pcurve in which to replace the old entity. + //! @return true if the entity was replaced, false otherwise. + static bool replacePcurve(const Handle(StepGeom_Plane)& theOldEntity, + const Handle(StepGeom_Plane)& theNewEntity, + Handle(Standard_Transient) theSharing); +}; + +#endif // _MergeSTEPEntities_DirectionProcessor_HeaderFile diff --git a/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_VectorHasher.hxx b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_VectorHasher.hxx new file mode 100644 index 0000000000..2144b1ded2 --- /dev/null +++ b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_VectorHasher.hxx @@ -0,0 +1,71 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#ifndef _MergeSTEPEntities_VectorHasher_HeaderFile +#define _MergeSTEPEntities_VectorHasher_HeaderFile + +#include + +#include +#include +#include + +//! OCCT-style hasher for StepGeom_Vector entities. +struct MergeSTEPEntities_VectorHasher +{ + // Hashes the Vector by its name and Vector ratios. + std::size_t operator()(const Handle(StepGeom_Vector)& theVector) const noexcept + { + const size_t aHashes[2]{MergeSTEPEntities_DirectionHasher{}(theVector->Orientation()), + opencascade::hash(static_cast(theVector->Magnitude()))}; + const size_t aCombinedHash = opencascade::hashBytes(aHashes, sizeof(aHashes)); + if (theVector->Name().IsNull()) + { + // If the name is not present, return the hash. + return aCombinedHash; + } + // Add the name to the hash if it is present. + const size_t aCombinedHashWithName[2]{ + aCombinedHash, + std::hash{}(theVector->Name()->String())}; + return opencascade::hashBytes(aCombinedHashWithName, sizeof(aCombinedHashWithName)); + } + + // Compares two Vectors by their names and Vector ratios. + bool operator()(const Handle(StepGeom_Vector)& theVector1, + const Handle(StepGeom_Vector)& theVector2) const noexcept + { + // Compare names. + if (theVector1->Name().IsNull() != theVector2->Name().IsNull()) + { + return false; + } + if (!theVector1->Name()->IsSameString(theVector2->Name())) + { + return false; + } + + // Compare magnitudes. + constexpr double aTolerance = 1e-12; + if (fabs(theVector1->Magnitude() - theVector2->Magnitude()) > aTolerance) + { + return false; + } + + // Compare orientations. + return MergeSTEPEntities_DirectionHasher{}(theVector1->Orientation(), + theVector2->Orientation()); + } +}; + +#endif // _MergeSTEPEntities_VectorHasher_HeaderFile diff --git a/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_VectorProcessor.cxx b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_VectorProcessor.cxx new file mode 100644 index 0000000000..3eb340a91e --- /dev/null +++ b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_VectorProcessor.cxx @@ -0,0 +1,55 @@ +// 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. + +// 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 + +#include +#include +#include + +//================================================================================================== + +MergeSTEPEntities_VectorProcessor::MergeSTEPEntities_VectorProcessor( + const Handle(XSControl_WorkSession)& theWS) + : MergeSTEPEntities_EntityProcessor(theWS) +{ + registerReplacer(STANDARD_TYPE(StepGeom_Line), replaceLine); +} + +//================================================================================================== + +bool MergeSTEPEntities_VectorProcessor::replaceLine(const Handle(StepGeom_Vector)& theOldEntity, + const Handle(StepGeom_Vector)& theNewEntity, + Handle(Standard_Transient) theSharing) +{ + Handle(StepGeom_Line) aLine = Handle(StepGeom_Line)::DownCast(theSharing); + if (aLine->Dir() == theOldEntity) + { + aLine->SetDir(theNewEntity); + return true; + } + return false; +} diff --git a/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_VectorProcessor.hxx b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_VectorProcessor.hxx new file mode 100644 index 0000000000..56d198665e --- /dev/null +++ b/src/DataExchange/TKDESTEP/MergeSTEPEntities/MergeSTEPEntities_VectorProcessor.hxx @@ -0,0 +1,43 @@ +// 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 _MergeSTEPEntities_VectorProcessor_HeaderFile +#define _MergeSTEPEntities_VectorProcessor_HeaderFile + +#include +#include + +#include + +//! Processor for merging StepGeom_Vector entities. +//! This processor merges vectors with the same orientation and magnitude and names. +class MergeSTEPEntities_VectorProcessor + : public MergeSTEPEntities_EntityProcessor +{ +public: + //! Constructor. Stores the work session and registers replacer functions. + //! @param theWS the work session. + Standard_EXPORT MergeSTEPEntities_VectorProcessor(const Handle(XSControl_WorkSession)& theWS); + +private: + //! Replaces the old vector with the new one in the StepGeom_Line entity. + //! @param theOldEntity the old vector. + //! @param theNewEntity the new vector to replace the old one. + //! @param theSharing the StepGeom_Line entity to update. + //! @return true if the vector was replaced, false otherwise. + static bool replaceLine(const Handle(StepGeom_Vector)& theOldEntity, + const Handle(StepGeom_Vector)& theNewEntity, + Handle(Standard_Transient) theSharing); +}; + +#endif // _MergeSTEPEntities_VectorProcessor_HeaderFile diff --git a/src/DataExchange/TKDESTEP/PACKAGES.cmake b/src/DataExchange/TKDESTEP/PACKAGES.cmake index 1e4a82db2b..356c180fb5 100644 --- a/src/DataExchange/TKDESTEP/PACKAGES.cmake +++ b/src/DataExchange/TKDESTEP/PACKAGES.cmake @@ -41,4 +41,5 @@ set(OCCT_TKDESTEP_LIST_OF_PACKAGES APIHeaderSection HeaderSection DESTEP + MergeSTEPEntities ) diff --git a/src/DataExchange/TKDESTEP/StepGeom/StepGeom_CartesianPoint.cxx b/src/DataExchange/TKDESTEP/StepGeom/StepGeom_CartesianPoint.cxx index ffd14211a8..078ac8c7a3 100644 --- a/src/DataExchange/TKDESTEP/StepGeom/StepGeom_CartesianPoint.cxx +++ b/src/DataExchange/TKDESTEP/StepGeom/StepGeom_CartesianPoint.cxx @@ -39,6 +39,7 @@ void StepGeom_CartesianPoint::Init2D(const Handle(TCollection_HAsciiString)& aNa nbcoord = 2; coords[0] = X; coords[1] = Y; + coords[2] = 0; // --- classe inherited fields --- StepRepr_RepresentationItem::Init(aName); } @@ -65,13 +66,14 @@ void StepGeom_CartesianPoint::SetCoordinates(const Handle(TColStd_HArray1OfReal) // coordinates = aCoordinates; } -Handle(TColStd_HArray1OfReal) StepGeom_CartesianPoint::Coordinates() const +void StepGeom_CartesianPoint::SetCoordinates(const std::array& theCoordinates) { - Handle(TColStd_HArray1OfReal) coordinates = new TColStd_HArray1OfReal(1, nbcoord); - coordinates->SetValue(1, coords[0]); - coordinates->SetValue(2, coords[1]); - coordinates->SetValue(3, coords[2]); - return coordinates; + coords = theCoordinates; +} + +const std::array& StepGeom_CartesianPoint::Coordinates() const +{ + return coords; } Standard_Real StepGeom_CartesianPoint::CoordinatesValue(const Standard_Integer num) const diff --git a/src/DataExchange/TKDESTEP/StepGeom/StepGeom_CartesianPoint.hxx b/src/DataExchange/TKDESTEP/StepGeom/StepGeom_CartesianPoint.hxx index a1a82809e2..433e2679f9 100644 --- a/src/DataExchange/TKDESTEP/StepGeom/StepGeom_CartesianPoint.hxx +++ b/src/DataExchange/TKDESTEP/StepGeom/StepGeom_CartesianPoint.hxx @@ -23,6 +23,9 @@ #include #include #include + +#include + class TCollection_HAsciiString; class StepGeom_CartesianPoint; @@ -49,7 +52,9 @@ public: Standard_EXPORT void SetCoordinates(const Handle(TColStd_HArray1OfReal)& aCoordinates); - Standard_EXPORT Handle(TColStd_HArray1OfReal) Coordinates() const; + Standard_EXPORT void SetCoordinates(const std::array& theCoordinates); + + Standard_EXPORT const std::array& Coordinates() const; Standard_EXPORT Standard_Real CoordinatesValue(const Standard_Integer num) const; @@ -59,8 +64,8 @@ public: protected: private: - Standard_Integer nbcoord; - Standard_Real coords[3]; + Standard_Integer nbcoord; + std::array coords; }; #endif // _StepGeom_CartesianPoint_HeaderFile diff --git a/src/Draw/TKXSDRAWSTEP/XSDRAWSTEP/XSDRAWSTEP.cxx b/src/Draw/TKXSDRAWSTEP/XSDRAWSTEP/XSDRAWSTEP.cxx index 5889b3be95..a3bd3ac0da 100644 --- a/src/Draw/TKXSDRAWSTEP/XSDRAWSTEP/XSDRAWSTEP.cxx +++ b/src/Draw/TKXSDRAWSTEP/XSDRAWSTEP/XSDRAWSTEP.cxx @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -1093,6 +1094,39 @@ static Standard_Integer WriteStep(Draw_Interpretor& theDI, //================================================================================================= +static Standard_Integer MergeSTEPEntities(Draw_Interpretor& theDI, + Standard_Integer theNbArgs, + const char** theArgVec) +{ + if (theNbArgs < 2) + { + theDI << "Incorrect number of arguments\n"; + theDI << "Usage: MergeSTEPEntities input_file output_file\n"; + return 1; + } + + STEPControl_Reader aReader; + if (aReader.ReadFile(theArgVec[1]) != IFSelect_RetDone) + { + theDI << "Error: Cannot read file " << theArgVec[1] << "\n"; + return 1; + } + + MergeSTEPEntities_Merger aMerger(aReader.WS()); + aMerger.Perform(); + + STEPControl_Writer aWriter(aReader.WS(), Standard_False); + if (aWriter.Write(theArgVec[2]) != IFSelect_RetDone) + { + theDI << "Error: Cannot write file " << theArgVec[2] << "\n"; + return 1; + } + + return 0; +} + +//================================================================================================= + void XSDRAWSTEP::Factory(Draw_Interpretor& theDI) { static Standard_Boolean aIsActivated = Standard_False; @@ -1145,6 +1179,15 @@ void XSDRAWSTEP::Factory(Draw_Interpretor& theDI) WriteStep, aGroup); + theDI.Add("MergeSTEPEntities", + "MergeSTEPEntities input_file output_file" + "\n\t\t: Merge equal entities in STEP file" + "\n\t\t: input_file - Step file to read" + "\n\t\t: output_file - Step file to write output to", + __FILE__, + MergeSTEPEntities, + aGroup); + // Load XSDRAW session for pilot activation XSDRAW::LoadDraw(theDI); }