Functionality to remove duplicate entities from Step graph is added.
Class MergeSTEPEntities_Merger is main entry point.
Class MergeSTEPEntities_EntityProcessor implements the basic replacement
logic.
Children of MergeSTEPEntities_EntityProcessor implement the logic for
the replacement of particular step entity.
(STEPControl_StepModelType)theResource->IntegerVal("write.model.type",
InternalParameters.WriteModelType,
aScope);
+ InternalParameters.CleanDuplicates =
+ theResource->BooleanVal("write.cleanduplicates", InternalParameters.CleanDuplicates, aScope);
return DE_ShapeFixConfigurationNode::Load(theResource);
}
aResult += aScope + "write.model.type :\t " + InternalParameters.WriteModelType + "\n";
aResult += "!\n";
+ aResult += "!\n";
+ aResult += "!Setting up a flag that indicates whether or not duplicate entities should be "
+ "removed from the model befor writing.\n";
+ aResult += "!Default value: -. Available values: \"-\", \"+\"\n";
+ aResult += aScope + "write.cleanduplicates :\t " + InternalParameters.CleanDuplicates + "\n";
+ aResult += "!\n";
+
aResult += DE_ShapeFixConfigurationNode::Save();
aResult += "!*****************************************************************************\n";
bool WriteLayer = true; //<! LayerMode is used to indicate write Layers or not
bool WriteProps = true; //<! PropsMode is used to indicate write Validation properties or not
STEPControl_StepModelType WriteModelType = STEPControl_AsIs; //<! Gives you the choice of translation mode for an Open CASCADE shape that is being translated to STEP
+ bool CleanDuplicates = false; //<! Indicates whether to remove duplicate entities from the STEP file
// clang-format on
};
aWriter.SetLayerMode(aNode->InternalParameters.WriteLayer);
aWriter.SetPropsMode(aNode->InternalParameters.WriteProps);
aWriter.SetShapeFixParameters(aNode->ShapeFixParameters);
+ aWriter.SetCleanDuplicates(aNode->InternalParameters.CleanDuplicates);
DESTEP_Parameters aParams = aNode->InternalParameters;
Standard_Real aScaleFactorMM = 1.;
if (XCAFDoc_DocumentTool::GetLengthUnit(theDocument,
<< "\t: abandon, no model loaded";
return false;
}
+ if (aNode->InternalParameters.CleanDuplicates)
+ {
+ aWriter.CleanDuplicateEntities();
+ }
+
if (aWriter.Write(thePath.ToCString()) != IFSelect_RetDone)
{
Message::SendFail() << "DESTEP_Provider: Error on writing file";
set(OCCT_TKDESTEP_GTests_FILES_LOCATION "${CMAKE_CURRENT_LIST_DIR}")
set(OCCT_TKDESTEP_GTests_FILES
+ StepTidy_BaseTestFixture.pxx
+ StepTidy_Axis2Placement3dReducer_Test.cxx
+ StepTidy_CartesianPointReducer_Test.cxx
+ StepTidy_CircleReducer_Test.cxx
+ StepTidy_DirectionReducer_Test.cxx
+ StepTidy_LineReducer_Test.cxx
+ StepTidy_PlaneReducer_Test.cxx
+ StepTidy_Merger_Test.cxx
+ StepTidy_VectorReducer_Test.cxx
)
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include "StepTidy_BaseTestFixture.pxx"
+
+#include <StepTidy_Axis2Placement3dReducer.pxx>
+
+#include <StepGeom_Plane.hxx>
+#include <StepRepr_ItemDefinedTransformation.hxx>
+#include <StepGeom_CylindricalSurface.hxx>
+#include <StepShape_ShapeRepresentation.hxx>
+#include <StepRepr_RepresentationContext.hxx>
+#include <StepRepr_ConstructiveGeometryRepresentation.hxx>
+#include <StepGeom_Circle.hxx>
+#include <StepVisual_PresentationLayerAssignment.hxx>
+#include <StepVisual_StyledItem.hxx>
+#include <StepGeom_Ellipse.hxx>
+#include <StepGeom_ConicalSurface.hxx>
+#include <StepGeom_ToroidalSurface.hxx>
+#include <StepShape_AdvancedBrepShapeRepresentation.hxx>
+#include <StepGeom_SphericalSurface.hxx>
+
+class StepTidy_Axis2Placement3dReducerTest : public StepTidy_BaseTestFixture
+{
+protected:
+ //! Perform removal of duplicate entities.
+ TColStd_MapOfTransient replaceDuplicateAxis2Placement3ds()
+ {
+ StepTidy_Axis2Placement3dReducer aReducer(myWS);
+ for (Standard_Integer anIndex = 1; anIndex <= myWS->Model()->NbEntities(); ++anIndex)
+ {
+ aReducer.ProcessEntity(myWS->Model()->Value(anIndex));
+ }
+
+ TColStd_MapOfTransient aRemovedEntities;
+ aReducer.Perform(aRemovedEntities);
+ return aRemovedEntities;
+ }
+};
+
+// Check that Axis2Placement3ds with the same coordinates and different names are not merged.
+TEST_F(StepTidy_Axis2Placement3dReducerTest, 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(StepTidy_Axis2Placement3dReducerTest, 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(StepTidy_Axis2Placement3dReducerTest, 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(StepTidy_Axis2Placement3dReducerTest, 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(StepTidy_Axis2Placement3dReducerTest, 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(StepTidy_Axis2Placement3dReducerTest, 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(StepTidy_Axis2Placement3dReducerTest, 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(StepTidy_Axis2Placement3dReducerTest, 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(StepTidy_Axis2Placement3dReducerTest, 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(StepTidy_Axis2Placement3dReducerTest, 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(StepTidy_Axis2Placement3dReducerTest, 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(StepTidy_Axis2Placement3dReducerTest, 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(StepTidy_Axis2Placement3dReducerTest, 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(StepTidy_Axis2Placement3dReducerTest, 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));
+}
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _StepTidy_BaseTestFixture_HeaderFile
+#define _StepTidy_BaseTestFixture_HeaderFile
+
+#include <STEPControl_Controller.hxx>
+#include <StepGeom_Axis2Placement3d.hxx>
+#include <StepGeom_CartesianPoint.hxx>
+#include <StepGeom_Circle.hxx>
+#include <StepGeom_Direction.hxx>
+#include <StepGeom_Line.hxx>
+#include <StepGeom_Plane.hxx>
+#include <StepGeom_Vector.hxx>
+#include <XSControl_WorkSession.hxx>
+
+#include <gtest/gtest.h>
+
+class StepTidy_BaseTestFixture : public testing::Test
+{
+protected:
+ // Initialize the work session and model.
+ StepTidy_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 // _StepTidy_BaseTestFixture_HeaderFile
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include "StepTidy_BaseTestFixture.pxx"
+
+#include <StepTidy_CartesianPointReducer.pxx>
+
+#include <StepGeom_Axis1Placement.hxx>
+#include <StepGeom_Axis2Placement3d.hxx>
+#include <StepGeom_BSplineCurveWithKnots.hxx>
+#include <StepGeom_BSplineCurveWithKnotsAndRationalBSplineCurve.hxx>
+#include <StepGeom_BSplineSurfaceWithKnots.hxx>
+#include <StepGeom_BSplineSurfaceWithKnotsAndRationalBSplineSurface.hxx>
+#include <StepGeom_Line.hxx>
+#include <StepGeom_RationalBSplineSurface.hxx>
+#include <StepRepr_Representation.hxx>
+#include <StepRepr_RepresentationContext.hxx>
+#include <StepShape_GeometricCurveSet.hxx>
+#include <StepShape_VertexPoint.hxx>
+#include <StepVisual_PresentationLayerAssignment.hxx>
+#include <StepVisual_StyledItem.hxx>
+
+class StepTidy_CartesianPointReducerTest : public StepTidy_BaseTestFixture
+{
+protected:
+ // Perform removal of duplicate Cartesian points.
+ TColStd_MapOfTransient replaceDuplicateCartesianPoints()
+ {
+ StepTidy_CartesianPointReducer aReducer(myWS);
+ for (Standard_Integer anIndex = 1; anIndex <= myWS->Model()->NbEntities(); ++anIndex)
+ {
+ aReducer.ProcessEntity(myWS->Model()->Value(anIndex));
+ }
+
+ TColStd_MapOfTransient aRemovedEntities;
+ aReducer.Perform(aRemovedEntities);
+ return aRemovedEntities;
+ }
+};
+
+// Check that points with the same coordinates and different names are not merged.
+TEST_F(StepTidy_CartesianPointReducerTest, 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(StepTidy_CartesianPointReducerTest, 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(StepTidy_CartesianPointReducerTest, 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(StepTidy_CartesianPointReducerTest, 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(StepTidy_CartesianPointReducerTest, 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(StepTidy_CartesianPointReducerTest, 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(StepTidy_CartesianPointReducerTest, 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);
+ 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);
+ 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(StepTidy_CartesianPointReducerTest, 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(StepTidy_CartesianPointReducerTest, 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);
+ 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);
+ 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(StepTidy_CartesianPointReducerTest, 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(StepTidy_CartesianPointReducerTest, 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(StepTidy_CartesianPointReducerTest, 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);
+ 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);
+ 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(StepTidy_CartesianPointReducerTest,
+ StepGeom_BSplineSurfaceWithKnotsAndRationalBSplineSurface)
+{
+ // Creating Cartesian points.
+ Handle(StepGeom_CartesianPoint) aPt1 = addCartesianPoint();
+ Handle(StepGeom_CartesianPoint) aPt2 = addCartesianPoint();
+
+ // Creating rational BSpline surface to use.
+ Handle(StepGeom_RationalBSplineSurface) aRationalBSplineSurface =
+ new StepGeom_RationalBSplineSurface;
+ aRationalBSplineSurface->Init(new TCollection_HAsciiString,
+ 1,
+ 1,
+ new StepGeom_HArray2OfCartesianPoint(1, 1, 1, 1),
+ StepGeom_bssfUnspecified,
+ StepData_LUnknown,
+ StepData_LUnknown,
+ StepData_LUnknown,
+ new TColStd_HArray2OfReal(1, 1, 1, 1));
+
+ // Creating surface containing the first Cartesian point.
+ Handle(StepGeom_HArray2OfCartesianPoint) aFirstControlPoints =
+ new StepGeom_HArray2OfCartesianPoint(1, 1, 1, 1);
+ aFirstControlPoints->SetValue(1, 1, aPt1);
+ 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,
+ aRationalBSplineSurface);
+ 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);
+ 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,
+ aSecondBSSWN,
+ aRationalBSplineSurface);
+
+ // 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));
+}
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include "StepTidy_BaseTestFixture.pxx"
+
+#include <StepTidy_CircleReducer.pxx>
+
+#include <StepShape_EdgeCurve.hxx>
+#include <StepGeom_SurfaceCurve.hxx>
+#include <StepGeom_SeamCurve.hxx>
+
+class StepTidy_CircleReducerTest : public StepTidy_BaseTestFixture
+{
+protected:
+ //! Perform removal of duplicate entities.
+ TColStd_MapOfTransient replaceDuplicateCircles()
+ {
+ StepTidy_CircleReducer aReducer(myWS);
+ for (Standard_Integer anIndex = 1; anIndex <= myWS->Model()->NbEntities(); ++anIndex)
+ {
+ aReducer.ProcessEntity(myWS->Model()->Value(anIndex));
+ }
+
+ TColStd_MapOfTransient aRemovedEntities;
+ aReducer.Perform(aRemovedEntities);
+ return aRemovedEntities;
+ }
+};
+
+// Check that Circles with the same coordinates and different names are not merged.
+TEST_F(StepTidy_CircleReducerTest, 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(StepTidy_CircleReducerTest, 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(StepTidy_CircleReducerTest, 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(StepTidy_CircleReducerTest, 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));
+}
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include "StepTidy_BaseTestFixture.pxx"
+
+#include <StepTidy_DirectionReducer.pxx>
+
+#include <StepGeom_Axis1Placement.hxx>
+#include <StepGeom_Axis2Placement3d.hxx>
+
+class StepTidy_DirectionReducerTest : public StepTidy_BaseTestFixture
+{
+protected:
+ //! Perform removal of duplicate entities.
+ TColStd_MapOfTransient replaceDuplicateDirections()
+ {
+ StepTidy_DirectionReducer aReducer(myWS);
+ for (Standard_Integer anIndex = 1; anIndex <= myWS->Model()->NbEntities(); ++anIndex)
+ {
+ aReducer.ProcessEntity(myWS->Model()->Value(anIndex));
+ }
+
+ TColStd_MapOfTransient aRemovedEntities;
+ aReducer.Perform(aRemovedEntities);
+ return aRemovedEntities;
+ }
+};
+
+// Check that directions with the same coordinates and different names are not merged.
+TEST_F(StepTidy_DirectionReducerTest, 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(StepTidy_DirectionReducerTest, 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(StepTidy_DirectionReducerTest, 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(StepTidy_DirectionReducerTest, 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));
+}
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include "StepTidy_BaseTestFixture.pxx"
+
+#include <StepTidy_LineReducer.pxx>
+
+#include <StepShape_EdgeCurve.hxx>
+#include <StepGeom_TrimmedCurve.hxx>
+#include <StepGeom_SurfaceCurve.hxx>
+#include <StepRepr_DefinitionalRepresentation.hxx>
+#include <StepGeom_SeamCurve.hxx>
+#include <StepRepr_RepresentationContext.hxx>
+
+class StepTidy_LineReducerTest : public StepTidy_BaseTestFixture
+{
+protected:
+ //! Perform removal of duplicate entities.
+ TColStd_MapOfTransient replaceDuplicateLines()
+ {
+ StepTidy_LineReducer aReducer(myWS);
+ for (Standard_Integer anIndex = 1; anIndex <= myWS->Model()->NbEntities(); ++anIndex)
+ {
+ aReducer.ProcessEntity(myWS->Model()->Value(anIndex));
+ }
+
+ TColStd_MapOfTransient aRemovedEntities;
+ aReducer.Perform(aRemovedEntities);
+ return aRemovedEntities;
+ }
+};
+
+// Check that Lines with different names are not merged.
+TEST_F(StepTidy_LineReducerTest, 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(StepTidy_LineReducerTest, 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(StepTidy_LineReducerTest, 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(StepTidy_LineReducerTest, 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(StepTidy_LineReducerTest, 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(StepTidy_LineReducerTest, 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));
+}
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include "StepTidy_BaseTestFixture.pxx"
+
+#include <StepTidy_DuplicateCleaner.hxx>
+
+#include <StepGeom_Axis1Placement.hxx>
+
+class StepTidy_DuplicateCleanerTest : public StepTidy_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()
+ {
+ StepTidy_DuplicateCleaner aMerger(myWS);
+ aMerger.Perform();
+ }
+};
+
+// Check that entities with the same coordinates and different names are not merged.
+TEST_F(StepTidy_DuplicateCleanerTest, 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(StepTidy_DuplicateCleanerTest, 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);
+}
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include "StepTidy_BaseTestFixture.pxx"
+
+#include <StepTidy_PlaneReducer.pxx>
+
+#include <StepShape_AdvancedFace.hxx>
+#include <StepGeom_Pcurve.hxx>
+#include <StepRepr_DefinitionalRepresentation.hxx>
+
+class StepTidy_PlaneReducerTest : public StepTidy_BaseTestFixture
+{
+protected:
+ //! Perform removal of duplicate entities.
+ TColStd_MapOfTransient replaceDuplicatePlanes()
+ {
+ StepTidy_PlaneReducer aReducer(myWS);
+ for (Standard_Integer anIndex = 1; anIndex <= myWS->Model()->NbEntities(); ++anIndex)
+ {
+ aReducer.ProcessEntity(myWS->Model()->Value(anIndex));
+ }
+
+ TColStd_MapOfTransient aRemovedEntities;
+ aReducer.Perform(aRemovedEntities);
+ return aRemovedEntities;
+ }
+};
+
+// Check that Planes with different names are not merged.
+TEST_F(StepTidy_PlaneReducerTest, 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(StepTidy_PlaneReducerTest, 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(StepTidy_PlaneReducerTest, 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));
+}
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include "StepTidy_BaseTestFixture.pxx"
+
+#include <StepTidy_VectorReducer.pxx>
+
+#include <StepGeom_Line.hxx>
+
+class StepTidy_VectorReducerTest : public StepTidy_BaseTestFixture
+{
+protected:
+ //! Perform removal of duplicate entities.
+ TColStd_MapOfTransient replaceDuplicateVectors()
+ {
+ StepTidy_VectorReducer aReducer(myWS);
+ for (Standard_Integer anIndex = 1; anIndex <= myWS->Model()->NbEntities(); ++anIndex)
+ {
+ aReducer.ProcessEntity(myWS->Model()->Value(anIndex));
+ }
+
+ TColStd_MapOfTransient aRemovedEntities;
+ aReducer.Perform(aRemovedEntities);
+ return aRemovedEntities;
+ }
+};
+
+// Check that Vectors with the same coordinates and different names are not merged.
+TEST_F(StepTidy_VectorReducerTest, 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(StepTidy_VectorReducerTest, 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));
+}
APIHeaderSection
HeaderSection
DESTEP
+ StepTidy
)
{
ach->AddFail("ERROR: No.of KnotMultiplicities not equal No.of Knots");
}
+ if (nbMult == 0)
+ {
+ ach->AddFail("ERROR: No.of KnotMultiplicities is equal to 0");
+ return;
+ }
+
Standard_Integer i; // svv Jan 10 2000: porting on DEC
for (i = 1; i <= nbMult - 1; i++)
{
{
ach->AddFail("ERROR: No.of KnotMultiplicities not equal No.of Knots in V");
}
+ if (nbMulU == 0)
+ {
+ ach->AddWarning("WARNING: No.of KnotMultiplicities in U is zero");
+ return;
+ }
+ if (nbMulV == 0)
+ {
+ ach->AddWarning("WARNING: No.of KnotMultiplicities in V is zero");
+ return;
+ }
// check in U direction
myPropsMode(Standard_True),
mySHUOMode(Standard_True),
myGDTMode(Standard_True),
- myMatMode(Standard_True)
+ myMatMode(Standard_True),
+ myIsCleanDuplicates(Standard_False)
{
STEPCAFControl_Controller::Init();
Handle(XSControl_WorkSession) aWS = new XSControl_WorkSession;
myPropsMode(Standard_True),
mySHUOMode(Standard_True),
myGDTMode(Standard_True),
- myMatMode(Standard_True)
+ myMatMode(Standard_True),
+ myIsCleanDuplicates(Standard_False)
{
STEPCAFControl_Controller::Init();
Init(theWS, theScratch);
IFSelect_ReturnStatus STEPCAFControl_Writer::Write(const Standard_CString theFileName)
{
+ if (myIsCleanDuplicates)
+ {
+ // remove duplicates
+ myWriter.CleanDuplicateEntities();
+ }
+
IFSelect_ReturnStatus aStatus = myWriter.Write(theFileName);
if (aStatus != IFSelect_RetDone)
{
Standard_Boolean GetMaterialMode() const { return myMatMode; }
+ //! Set clean duplicates flag.
+ //! If set to True, duplicates will be removed from the model.
+ //! @param theCleanDuplicates the flag to set.
+ void SetCleanDuplicates(const Standard_Boolean theCleanDuplicates)
+ {
+ myIsCleanDuplicates = theCleanDuplicates;
+ }
+
+ //! Returns the flag indicating whether duplicates should be removed from the model.
+ //! @return the flag indicating whether duplicates should be removed from the model.
+ Standard_Boolean GetCleanDuplicates() const { return myIsCleanDuplicates; }
+
//! Sets parameters for shape processing.
//! @param theParameters the parameters for shape processing.
Standard_EXPORT void SetShapeFixParameters(
MoniTool_DataMapOfShapeTransient myMapCompMDGPR;
Standard_Boolean myGDTMode;
Standard_Boolean myMatMode;
- NCollection_Vector<Handle(StepRepr_RepresentationItem)> myGDTAnnotations;
+ Standard_Boolean myIsCleanDuplicates;
+ NCollection_Vector<Handle(StepRepr_RepresentationItem)> myGDTAnnotations;
Handle(StepVisual_DraughtingModel) myGDTPresentationDM;
Handle(StepVisual_HArray1OfPresentationStyleAssignment) myGDTPrsCurveStyle;
Handle(StepRepr_ProductDefinitionShape) myGDTCommonPDS;
#include <StepData_StepModel.hxx>
#include <StepData_Protocol.hxx>
#include <StepData_StepWriter.hxx>
+#include <StepTidy_DuplicateCleaner.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS_Shape.hxx>
#include <XSAlgo.hxx>
thesession->TransferWriter()->PrintStats(what, mode);
}
+//=================================================================================================
+
+Standard_EXPORT void STEPControl_Writer::CleanDuplicateEntities()
+{
+ StepTidy_DuplicateCleaner aCleaner(thesession);
+ aCleaner.Perform();
+}
+
//=============================================================================
void STEPControl_Writer::SetShapeFixParameters(
Standard_EXPORT void PrintStatsTransfer(const Standard_Integer what,
const Standard_Integer mode = 0) const;
+ Standard_EXPORT void CleanDuplicateEntities();
+
//! Sets parameters for shape processing.
//! @param theParameters the parameters for shape processing.
Standard_EXPORT void SetShapeFixParameters(
nbcoord = 2;
coords[0] = X;
coords[1] = Y;
+ coords[2] = 0;
// --- classe inherited fields ---
StepRepr_RepresentationItem::Init(aName);
}
// coordinates = aCoordinates;
}
-Handle(TColStd_HArray1OfReal) StepGeom_CartesianPoint::Coordinates() const
+void StepGeom_CartesianPoint::SetCoordinates(const std::array<Standard_Real, 3>& 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<Standard_Real, 3>& StepGeom_CartesianPoint::Coordinates() const
+{
+ return coords;
}
Standard_Real StepGeom_CartesianPoint::CoordinatesValue(const Standard_Integer num) const
#include <Standard_Integer.hxx>
#include <StepGeom_Point.hxx>
#include <TColStd_HArray1OfReal.hxx>
+
+#include <array>
+
class TCollection_HAsciiString;
class StepGeom_CartesianPoint;
Standard_EXPORT void SetCoordinates(const Handle(TColStd_HArray1OfReal)& aCoordinates);
- Standard_EXPORT Handle(TColStd_HArray1OfReal) Coordinates() const;
+ Standard_EXPORT void SetCoordinates(const std::array<Standard_Real, 3>& theCoordinates);
+
+ Standard_EXPORT const std::array<Standard_Real, 3>& Coordinates() const;
Standard_EXPORT Standard_Real CoordinatesValue(const Standard_Integer num) const;
protected:
private:
- Standard_Integer nbcoord;
- Standard_Real coords[3];
+ Standard_Integer nbcoord;
+ std::array<Standard_Real, 3> coords;
};
#endif // _StepGeom_CartesianPoint_HeaderFile
--- /dev/null
+# Source files for StepTidy package
+set(OCCT_StepTidy_FILES_LOCATION "${CMAKE_CURRENT_LIST_DIR}")
+
+set(OCCT_StepTidy_FILES
+ StepTidy_Axis2Placement2dHasher.pxx
+ StepTidy_Axis2Placement3dHasher.pxx
+ StepTidy_Axis2Placement3dReducer.cxx
+ StepTidy_Axis2Placement3dReducer.pxx
+ StepTidy_CartesianPointHasher.pxx
+ StepTidy_CartesianPointReducer.cxx
+ StepTidy_CartesianPointReducer.pxx
+ StepTidy_CircleHasher.pxx
+ StepTidy_CircleReducer.cxx
+ StepTidy_CircleReducer.pxx
+ StepTidy_DirectionHasher.pxx
+ StepTidy_DirectionReducer.cxx
+ StepTidy_DirectionReducer.pxx
+ StepTidy_EntityReducer.pxx
+ StepTidy_LineHasher.pxx
+ StepTidy_LineReducer.cxx
+ StepTidy_LineReducer.pxx
+ StepTidy_DuplicateCleaner.cxx
+ StepTidy_DuplicateCleaner.hxx
+ StepTidy_PlaneHasher.pxx
+ StepTidy_PlaneReducer.cxx
+ StepTidy_PlaneReducer.pxx
+ StepTidy_VectorHasher.pxx
+ StepTidy_VectorReducer.cxx
+ StepTidy_VectorReducer.pxx
+)
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _StepTidy_Axis2Placement2dHasher_HeaderFile
+#define _StepTidy_Axis2Placement2dHasher_HeaderFile
+
+#include <StepTidy_CartesianPointHasher.pxx>
+#include <StepTidy_DirectionHasher.pxx>
+
+#include <Standard_HashUtils.hxx>
+#include <StepGeom_Axis2Placement2d.hxx>
+
+//! OCCT-style hasher for StepGeom_Axis2Placement2d entities.
+//! Currently only used for implementation of hasher for StepGeom_Circle.
+struct StepTidy_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]{StepTidy_CartesianPointHasher{}(thePlacement->Location()),
+ thePlacement->HasRefDirection()
+ ? StepTidy_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<TCollection_AsciiString>{}(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 =
+ StepTidy_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()
+ || StepTidy_DirectionHasher{}(thePlacement1->RefDirection(),
+ thePlacement2->RefDirection()));
+
+ return isSameLocation && isSameRefDirection;
+ }
+};
+
+#endif // _StepTidy_Axis2Placement2dHasher_HeaderFile
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _StepTidy_Axis2Placement3dHasher_HeaderFile
+#define _StepTidy_Axis2Placement3dHasher_HeaderFile
+
+#include <StepTidy_CartesianPointHasher.pxx>
+#include <StepTidy_DirectionHasher.pxx>
+
+#include <Standard_HashUtils.hxx>
+#include <StepGeom_Axis2Placement3d.hxx>
+
+//! OCCT-style hasher for StepGeom_Axis2Placement3d entities.
+struct StepTidy_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]{
+ StepTidy_CartesianPointHasher{}(thePlacement->Location()),
+ thePlacement->HasAxis() ? StepTidy_DirectionHasher{}(thePlacement->Axis())
+ : opencascade::MurmurHash::optimalSeed(),
+ thePlacement->HasRefDirection() ? StepTidy_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<TCollection_AsciiString>{}(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 =
+ StepTidy_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()
+ || StepTidy_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()
+ || StepTidy_DirectionHasher{}(thePlacement1->RefDirection(),
+ thePlacement2->RefDirection()));
+
+ return isSameLocation && isSameAxis && isSameRefDirection;
+ }
+};
+
+#endif // _StepTidy_Axis2Placement3dHasher_HeaderFile
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <StepTidy_Axis2Placement3dReducer.pxx>
+
+#include <StepGeom_Plane.hxx>
+#include <StepRepr_ItemDefinedTransformation.hxx>
+#include <StepGeom_CylindricalSurface.hxx>
+#include <StepShape_ShapeRepresentation.hxx>
+#include <StepRepr_ConstructiveGeometryRepresentation.hxx>
+#include <StepGeom_Circle.hxx>
+#include <StepVisual_PresentationLayerAssignment.hxx>
+#include <StepVisual_StyledItem.hxx>
+#include <StepGeom_Ellipse.hxx>
+#include <StepGeom_ConicalSurface.hxx>
+#include <StepGeom_ToroidalSurface.hxx>
+#include <StepShape_AdvancedBrepShapeRepresentation.hxx>
+#include <StepGeom_SphericalSurface.hxx>
+
+//==================================================================================================
+
+StepTidy_Axis2Placement3dReducer::StepTidy_Axis2Placement3dReducer(
+ const Handle(XSControl_WorkSession)& theWS)
+ : StepTidy_EntityReducer<StepGeom_Axis2Placement3d, StepTidy_Axis2Placement3dHasher>(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 StepTidy_Axis2Placement3dReducer::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 StepTidy_Axis2Placement3dReducer::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 StepTidy_Axis2Placement3dReducer::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 StepTidy_Axis2Placement3dReducer::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 StepTidy_Axis2Placement3dReducer::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 StepTidy_Axis2Placement3dReducer::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 StepTidy_Axis2Placement3dReducer::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 StepTidy_Axis2Placement3dReducer::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 StepTidy_Axis2Placement3dReducer::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 StepTidy_Axis2Placement3dReducer::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 StepTidy_Axis2Placement3dReducer::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 StepTidy_Axis2Placement3dReducer::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 StepTidy_Axis2Placement3dReducer::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;
+}
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _StepTidy_Axis2Placement3dReducer_HeaderFile
+#define _StepTidy_Axis2Placement3dReducer_HeaderFile
+
+#include <StepTidy_EntityReducer.pxx>
+#include <StepTidy_Axis2Placement3dHasher.pxx>
+
+#include <StepGeom_Axis2Placement3d.hxx>
+
+//! Processor for merging StepGeom_Axis2Placement3d entities.
+//! This processor merges axis placements with the same location, axis, and ref direction.
+class StepTidy_Axis2Placement3dReducer
+ : public StepTidy_EntityReducer<StepGeom_Axis2Placement3d, StepTidy_Axis2Placement3dHasher>
+{
+public:
+ //! Constructor. Stores the work session and registers replacer functions.
+ //! @param theWS the work session.
+ Standard_EXPORT StepTidy_Axis2Placement3dReducer(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 // _StepTidy_DirectionReducer_HeaderFile
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _StepTidy_CartesianPointHasher_HeaderFile
+#define _StepTidy_CartesianPointHasher_HeaderFile
+
+#include <Standard_HashUtils.hxx>
+#include <StepGeom_CartesianPoint.hxx>
+#include <TCollection_HAsciiString.hxx>
+
+//! OCCT-style hasher for StepGeom_CartesianPoint entities.
+struct StepTidy_CartesianPointHasher
+{
+ // Hashes the Cartesian point by its name and coordinates.
+ std::size_t operator()(const Handle(StepGeom_CartesianPoint)& theCartesianPoint) const noexcept
+ {
+ const std::array<Standard_Real, 3>& aCoords = theCartesianPoint->Coordinates();
+ // If Cartesian point has no name, hash only coordinates.
+ if (theCartesianPoint->Name().IsNull())
+ {
+ return opencascade::hashBytes(aCoords.data(), static_cast<int>(aCoords.size()));
+ }
+ // Otherwise, hash both coordinates and name.
+ const size_t aHashes[2]{
+ opencascade::hashBytes(aCoords.data(), static_cast<int>(aCoords.size())),
+ std::hash<TCollection_AsciiString>{}(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<Standard_Real, 3>& aCoords1 = theCartesianPoint1->Coordinates();
+ const std::array<Standard_Real, 3>& aCoords2 = theCartesianPoint2->Coordinates();
+ if (theCartesianPoint1->NbCoordinates() != theCartesianPoint2->NbCoordinates())
+ {
+ return false;
+ }
+ for (int anIndex = 0; anIndex < theCartesianPoint1->NbCoordinates(); ++anIndex)
+ {
+ if (std::abs(aCoords1[anIndex] - aCoords2[anIndex]) > aTolerance)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+};
+
+#endif // _StepTidy_CartesianPointHasher_HeaderFile
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <StepTidy_CartesianPointReducer.pxx>
+
+#include <Interface_Graph.hxx>
+#include <StepGeom_Axis1Placement.hxx>
+#include <StepGeom_Axis2Placement3d.hxx>
+#include <StepGeom_BSplineCurveWithKnots.hxx>
+#include <StepGeom_BSplineCurveWithKnotsAndRationalBSplineCurve.hxx>
+#include <StepGeom_BSplineSurfaceWithKnots.hxx>
+#include <StepGeom_BSplineSurfaceWithKnotsAndRationalBSplineSurface.hxx>
+#include <StepGeom_Line.hxx>
+#include <StepRepr_Representation.hxx>
+#include <StepShape_GeometricCurveSet.hxx>
+#include <StepShape_VertexPoint.hxx>
+#include <StepVisual_PresentationLayerAssignment.hxx>
+#include <StepVisual_StyledItem.hxx>
+
+//==================================================================================================
+
+StepTidy_CartesianPointReducer::StepTidy_CartesianPointReducer(
+ const Handle(XSControl_WorkSession)& theWS)
+ : StepTidy_EntityReducer<StepGeom_CartesianPoint, StepTidy_CartesianPointHasher>(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 StepTidy_CartesianPointReducer::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 StepTidy_CartesianPointReducer::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 StepTidy_CartesianPointReducer::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 StepTidy_CartesianPointReducer::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 StepTidy_CartesianPointReducer::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 StepTidy_CartesianPointReducer::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 StepTidy_CartesianPointReducer::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 StepTidy_CartesianPointReducer::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 StepTidy_CartesianPointReducer::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 StepTidy_CartesianPointReducer::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 StepTidy_CartesianPointReducer::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 StepTidy_CartesianPointReducer::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;
+}
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _StepTidy_CartesianPointReducer_HeaderFile
+#define _StepTidy_CartesianPointReducer_HeaderFile
+
+#include <StepTidy_EntityReducer.pxx>
+#include <StepTidy_CartesianPointHasher.pxx>
+
+#include <StepGeom_CartesianPoint.hxx>
+
+//! 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 StepTidy_EntityReducer for the description of the Reducer workflow.
+class StepTidy_CartesianPointReducer
+ : public StepTidy_EntityReducer<StepGeom_CartesianPoint, StepTidy_CartesianPointHasher>
+{
+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 StepTidy_CartesianPointReducer(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 // _StepTidy_CartesianPointReducer_HeaderFile
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _StepTidy_CircleHasher_HeaderFile
+#define _StepTidy_CircleHasher_HeaderFile
+
+#include <StepTidy_Axis2Placement2dHasher.pxx>
+#include <StepTidy_Axis2Placement3dHasher.pxx>
+
+#include <Standard_HashUtils.hxx>
+#include <StepGeom_Circle.hxx>
+#include <TCollection_HAsciiString.hxx>
+
+//! OCCT-style hasher for StepGeom_Circle entities.
+struct StepTidy_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()
+ ? StepTidy_Axis2Placement2dHasher{}(theCircle->Position().Axis2Placement2d())
+ : !theCircle->Position().Axis2Placement3d().IsNull()
+ ? StepTidy_Axis2Placement3dHasher{}(theCircle->Position().Axis2Placement3d())
+ : opencascade::MurmurHash::optimalSeed();
+
+ const size_t aRadiusHash = opencascade::hash(static_cast<int>(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<TCollection_AsciiString>{}(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 (!StepTidy_Axis2Placement2dHasher{}(theCircle1->Position().Axis2Placement2d(),
+ theCircle2->Position().Axis2Placement2d()))
+ {
+ return false;
+ }
+ }
+ else if (theCircle1->Position().CaseNumber() == 2)
+ {
+ if (!StepTidy_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 // _StepTidy_CircleHasher_HeaderFile
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <StepTidy_CircleReducer.pxx>
+
+#include <Interface_Graph.hxx>
+#include <StepShape_EdgeCurve.hxx>
+#include <StepGeom_SurfaceCurve.hxx>
+#include <StepGeom_SeamCurve.hxx>
+
+//==================================================================================================
+
+StepTidy_CircleReducer::StepTidy_CircleReducer(const Handle(XSControl_WorkSession)& theWS)
+ : StepTidy_EntityReducer<StepGeom_Circle, StepTidy_CircleHasher>(theWS)
+{
+ registerReplacer(STANDARD_TYPE(StepShape_EdgeCurve), replaceEdgeCurve);
+ registerReplacer(STANDARD_TYPE(StepGeom_SurfaceCurve), replaceSurfaceCurve);
+ registerReplacer(STANDARD_TYPE(StepGeom_SeamCurve), replaceSeamCurve);
+}
+
+//==================================================================================================
+
+bool StepTidy_CircleReducer::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 StepTidy_CircleReducer::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 StepTidy_CircleReducer::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;
+}
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _StepTidy_CircleReducer_HeaderFile
+#define _StepTidy_CircleReducer_HeaderFile
+
+#include <StepTidy_EntityReducer.pxx>
+#include <StepTidy_CircleHasher.pxx>
+
+#include <StepGeom_Circle.hxx>
+
+//! Processor for merging StepGeom_Circle entities.
+//! This processor merges circles with the same position and radius and names.
+class StepTidy_CircleReducer : public StepTidy_EntityReducer<StepGeom_Circle, StepTidy_CircleHasher>
+{
+public:
+ //! Constructor. Stores the work session and registers replacer functions.
+ //! @param theWS the work session.
+ Standard_EXPORT StepTidy_CircleReducer(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 // _StepTidy_CircleReducer_HeaderFile
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _StepTidy_DirectionHasher_HeaderFile
+#define _StepTidy_DirectionHasher_HeaderFile
+
+#include <Standard_HashUtils.hxx>
+#include <StepGeom_Direction.hxx>
+#include <TCollection_HAsciiString.hxx>
+
+//! OCCT-style hasher for StepGeom_Direction entities.
+struct StepTidy_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<int>(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<TCollection_AsciiString>{}(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 // _StepTidy_DirectionHasher_HeaderFile
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <StepTidy_DirectionReducer.pxx>
+
+#include <Interface_Graph.hxx>
+#include <StepGeom_Axis1Placement.hxx>
+#include <StepGeom_Axis2Placement3d.hxx>
+#include <StepGeom_Vector.hxx>
+
+//==================================================================================================
+
+StepTidy_DirectionReducer::StepTidy_DirectionReducer(const Handle(XSControl_WorkSession)& theWS)
+ : StepTidy_EntityReducer<StepGeom_Direction, StepTidy_DirectionHasher>(theWS)
+{
+ registerReplacer(STANDARD_TYPE(StepGeom_Axis1Placement), replaceAxis1Placement);
+ registerReplacer(STANDARD_TYPE(StepGeom_Axis2Placement3d), replaceAxis2Placement3d);
+ registerReplacer(STANDARD_TYPE(StepGeom_Vector), replaceVector);
+}
+
+//==================================================================================================
+
+bool StepTidy_DirectionReducer::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 StepTidy_DirectionReducer::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 StepTidy_DirectionReducer::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;
+}
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _StepTidy_DirectionReducer_HeaderFile
+#define _StepTidy_DirectionReducer_HeaderFile
+
+#include <StepTidy_EntityReducer.pxx>
+#include <StepTidy_DirectionHasher.pxx>
+
+#include <StepGeom_Direction.hxx>
+
+//! 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 StepTidy_EntityReducer for the description of the Reducer workflow.
+class StepTidy_DirectionReducer
+ : public StepTidy_EntityReducer<StepGeom_Direction, StepTidy_DirectionHasher>
+{
+public:
+ //! Constructor. Stores the work session and registers replacer functions.
+ //! @param theWS the work session.
+ Standard_EXPORT StepTidy_DirectionReducer(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 // _StepTidy_DirectionReducer_HeaderFile
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <StepTidy_DuplicateCleaner.hxx>
+
+#include <Interface_EntityIterator.hxx>
+#include <Interface_Graph.hxx>
+#include <StepTidy_Axis2Placement3dReducer.pxx>
+#include <StepTidy_CartesianPointReducer.pxx>
+#include <StepTidy_DirectionReducer.pxx>
+#include <StepTidy_LineReducer.pxx>
+#include <StepTidy_VectorReducer.pxx>
+#include <StepTidy_PlaneReducer.pxx>
+#include <StepTidy_CircleReducer.pxx>
+#include <StepData_StepModel.hxx>
+
+//==================================================================================================
+
+StepTidy_DuplicateCleaner::StepTidy_DuplicateCleaner(Handle(XSControl_WorkSession) theWS)
+ : myWS(theWS)
+{
+}
+
+//==================================================================================================
+
+void StepTidy_DuplicateCleaner::Perform()
+{
+ Handle(StepData_StepModel) aModel = Handle(StepData_StepModel)::DownCast(myWS->Model());
+ if (aModel.IsNull())
+ {
+ return;
+ }
+
+ //! Initialize Reducers.
+ StepTidy_CartesianPointReducer aCartesianPointReducer(myWS);
+ StepTidy_DirectionReducer aDirectionReducer(myWS);
+ StepTidy_Axis2Placement3dReducer aAxis2Placement3dReducer(myWS);
+ StepTidy_VectorReducer aVectorReducer(myWS);
+ StepTidy_LineReducer aLineReducer(myWS);
+ StepTidy_PlaneReducer aPlaneReducer(myWS);
+ StepTidy_CircleReducer aCircleReducer(myWS);
+
+ // Process all entities.
+ for (Standard_Integer anIndex = 1; anIndex <= aModel->NbEntities(); ++anIndex)
+ {
+ const Handle(Standard_Transient) anEntity = aModel->Value(anIndex);
+ aCartesianPointReducer.ProcessEntity(anEntity);
+ aDirectionReducer.ProcessEntity(anEntity);
+ aAxis2Placement3dReducer.ProcessEntity(anEntity);
+ aVectorReducer.ProcessEntity(anEntity);
+ aLineReducer.ProcessEntity(anEntity);
+ aPlaneReducer.ProcessEntity(anEntity);
+ aCircleReducer.ProcessEntity(anEntity);
+ }
+
+ // Perform replacement of duplicate entities.
+ TColStd_MapOfTransient aReplacedEntities;
+ aCartesianPointReducer.Perform(aReplacedEntities);
+ aDirectionReducer.Perform(aReplacedEntities);
+ aAxis2Placement3dReducer.Perform(aReplacedEntities);
+ aVectorReducer.Perform(aReplacedEntities);
+ aLineReducer.Perform(aReplacedEntities);
+ aPlaneReducer.Perform(aReplacedEntities);
+ aCircleReducer.Perform(aReplacedEntities);
+
+ // Remove duplicate entities.
+ removeEntities(aReplacedEntities);
+}
+
+//==================================================================================================
+
+void StepTidy_DuplicateCleaner::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();
+}
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _StepTidy_DuplicateCleaner_HeaderFile
+#define _StepTidy_DuplicateCleaner_HeaderFile
+
+#include <TColStd_MapOfTransient.hxx>
+
+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.
+//! It uses the child classes of StepTidy_EntityReducer class to perform the merging.
+//! The child classes of StepTidy_EntityReducer are specialized for different types of entities.
+//! StepTidy_EntityReducer implements the basic logic for searching and merging entities
+//! while child classes implement the logic for replacing specific type of entities in the specific
+//! type of sharing entities.
+//! Classes StepTidy_*Hasher are used to hash the entities and compare them. They define which
+//! entities are considered equal to each other. The hashers are used in the StepTidy_EntityReducer
+//! class to store the entities in a map. The map is used to find the duplicates and replace them.
+//! From this perspective of this module, 'equal' or 'duplicate' entities are those that
+//! has equal names and very close numerical values, like for example Cartesian points with
+//! coordinates that are equal up to 1e-12 or Vectors with equal orientations and magnitudes
+//! up to 1e-12.
+//! After the merging this class calls its own method to remove the duplicates.
+//! How to use:
+//! 1. Create an instance of the class by providing a pointer to the work session where the
+//! entities to process are stored.
+//! 2. Call Perform() method to perform the merging of entities. After this call all entities
+//! that are considered equal to each other will be merged, and duplicates will be removed.
+class StepTidy_DuplicateCleaner
+{
+public:
+ //! Constructor.
+ //! @param theWS the work session to merge entities in.
+ Standard_EXPORT StepTidy_DuplicateCleaner(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 // _StepTidy_DuplicateCleaner_HeaderFile
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _StepTidy_EntityReducer_HeaderFile
+#define _StepTidy_EntityReducer_HeaderFile
+
+#include <Interface_Graph.hxx>
+#include <NCollection_Allocator.hxx>
+#include <NCollection_DataMap.hxx>
+#include <Standard_HashUtils.hxx>
+#include <XSControl_WorkSession.hxx>
+#include <TColStd_MapOfTransient.hxx>
+
+#include <functional>
+
+//! 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 <typename ProcessedType, typename ProcessedTypeHasher>
+class StepTidy_EntityReducer
+{
+protected:
+ // Map of duplicate entities. Key is the first processed entity, value is a list of duplicates.
+ using DuplicateMap = NCollection_DataMap<Handle(ProcessedType),
+ std::vector<Handle(ProcessedType)>,
+ 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<bool(const Handle(ProcessedType)&,
+ const Handle(ProcessedType)&,
+ const Handle(Standard_Transient))>;
+ // Map of replacer functions. Key is the type of the sharing entity, value is the replacer
+ // function for this type.
+ using ReplacerMap = NCollection_DataMap<Handle(Standard_Type), ReplacerFunction>;
+
+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.
+ StepTidy_EntityReducer(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 <typename ProcessedType, typename ProcessedTypeHasher>
+StepTidy_EntityReducer<ProcessedType, ProcessedTypeHasher>::StepTidy_EntityReducer(
+ const Handle(XSControl_WorkSession)& theWS)
+ : myWS(theWS),
+ myReplacerMap(),
+ myDuplicateMap()
+{
+}
+
+//==================================================================================================
+
+template <typename ProcessedType, typename ProcessedTypeHasher>
+bool StepTidy_EntityReducer<ProcessedType, ProcessedTypeHasher>::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<Handle(ProcessedType)>* anIter = myDuplicateMap.ChangeSeek(anEntity);
+ if (anIter == nullptr)
+ {
+ // Add as a new key.
+ myDuplicateMap.Bind(anEntity, std::vector<Handle(ProcessedType)>{});
+ }
+ else
+ {
+ // Add as a value.
+ anIter->push_back(anEntity);
+ }
+ }
+
+ return true;
+}
+
+//==================================================================================================
+
+template <typename ProcessedType, typename ProcessedTypeHasher>
+void StepTidy_EntityReducer<ProcessedType, ProcessedTypeHasher>::Perform(
+ TColStd_MapOfTransient& theReplacedEntities)
+{
+ for (typename DuplicateMap::Iterator anIter(myDuplicateMap); anIter.More(); anIter.Next())
+ {
+ const Handle(ProcessedType)& anEntity = anIter.Key();
+ const std::vector<Handle(ProcessedType)>& 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 <typename ProcessedType, typename ProcessedTypeHasher>
+void StepTidy_EntityReducer<ProcessedType, ProcessedTypeHasher>::registerReplacer(
+ const Handle(Standard_Type)& theType,
+ const ReplacerFunction& theReplacer)
+{
+ myReplacerMap.Bind(theType, theReplacer);
+}
+
+//==================================================================================================
+
+template <typename ProcessedType, typename ProcessedTypeHasher>
+bool StepTidy_EntityReducer<ProcessedType, ProcessedTypeHasher>::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 <typename ProcessedType, typename ProcessedTypeHasher>
+bool StepTidy_EntityReducer<ProcessedType, ProcessedTypeHasher>::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 // _StepTidy_EntityReducer_HeaderFile
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _StepTidy_LineHasher_HeaderFile
+#define _StepTidy_LineHasher_HeaderFile
+
+#include <StepTidy_CartesianPointHasher.pxx>
+#include <StepTidy_VectorHasher.pxx>
+
+#include <Standard_HashUtils.hxx>
+#include <StepGeom_Line.hxx>
+#include <TCollection_HAsciiString.hxx>
+
+//! OCCT-style hasher for StepGeom_Line entities.
+struct StepTidy_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]{StepTidy_CartesianPointHasher{}(theLine->Pnt()),
+ StepTidy_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<TCollection_AsciiString>{}(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 (!StepTidy_CartesianPointHasher{}(theLine1->Pnt(), theLine2->Pnt()))
+ {
+ return false;
+ }
+
+ // Compare directions.
+ if (!StepTidy_VectorHasher{}(theLine1->Dir(), theLine2->Dir()))
+ {
+ return false;
+ }
+
+ return true;
+ }
+};
+
+#endif // _StepTidy_LineHasher_HeaderFile
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <StepTidy_LineReducer.pxx>
+
+#include <Interface_Graph.hxx>
+#include <StepShape_EdgeCurve.hxx>
+#include <StepGeom_TrimmedCurve.hxx>
+#include <StepGeom_SurfaceCurve.hxx>
+#include <StepRepr_DefinitionalRepresentation.hxx>
+#include <StepGeom_SeamCurve.hxx>
+
+//==================================================================================================
+
+StepTidy_LineReducer::StepTidy_LineReducer(const Handle(XSControl_WorkSession)& theWS)
+ : StepTidy_EntityReducer<StepGeom_Line, StepTidy_LineHasher>(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 StepTidy_LineReducer::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 StepTidy_LineReducer::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 StepTidy_LineReducer::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 StepTidy_LineReducer::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 StepTidy_LineReducer::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;
+}
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _StepTidy_LineReducer_HeaderFile
+#define _StepTidy_LineReducer_HeaderFile
+
+#include <StepTidy_EntityReducer.pxx>
+#include <StepTidy_LineHasher.pxx>
+
+#include <StepGeom_Line.hxx>
+
+//! Processor for merging StepGeom_Line entities.
+//! This processor merges lines with the same point and direction and names.
+class StepTidy_LineReducer : public StepTidy_EntityReducer<StepGeom_Line, StepTidy_LineHasher>
+{
+public:
+ //! Constructor. Stores the work session and registers replacer functions.
+ //! @param theWS the work session.
+ Standard_EXPORT StepTidy_LineReducer(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 // _StepTidy_LineReducer_HeaderFile
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _StepTidy_PlaneHasher_HeaderFile
+#define _StepTidy_PlaneHasher_HeaderFile
+
+#include <StepTidy_Axis2Placement3dHasher.pxx>
+
+#include <Standard_HashUtils.hxx>
+#include <StepGeom_Plane.hxx>
+
+//! OCCT-style hasher for StepGeom_Plane entities.
+struct StepTidy_PlaneHasher
+{
+ // Hashes the axis Planes.
+ std::size_t operator()(const Handle(StepGeom_Plane)& thePlane) const noexcept
+ {
+ const size_t aHash = StepTidy_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<TCollection_AsciiString>{}(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 StepTidy_Axis2Placement3dHasher{}(thePlane1->Position(), thePlane2->Position());
+ }
+};
+
+#endif // _StepTidy_PlaneHasher_HeaderFile
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <StepTidy_PlaneReducer.pxx>
+
+#include <StepShape_AdvancedFace.hxx>
+#include <StepGeom_Pcurve.hxx>
+
+//==================================================================================================
+
+StepTidy_PlaneReducer::StepTidy_PlaneReducer(const Handle(XSControl_WorkSession)& theWS)
+ : StepTidy_EntityReducer<StepGeom_Plane, StepTidy_PlaneHasher>(theWS)
+{
+ registerReplacer(STANDARD_TYPE(StepShape_AdvancedFace), replaceAdvancedFace);
+ registerReplacer(STANDARD_TYPE(StepGeom_Pcurve), replacePcurve);
+}
+
+//==================================================================================================
+
+bool StepTidy_PlaneReducer::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 StepTidy_PlaneReducer::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;
+}
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _StepTidy_PlaneReducer_HeaderFile
+#define _StepTidy_PlaneReducer_HeaderFile
+
+#include <StepTidy_EntityReducer.pxx>
+#include <StepTidy_PlaneHasher.pxx>
+
+#include <StepGeom_Plane.hxx>
+
+//! Processor for merging StepGeom_Plane entities.
+//! This processor merges planes with the same names and placements.
+class StepTidy_PlaneReducer : public StepTidy_EntityReducer<StepGeom_Plane, StepTidy_PlaneHasher>
+{
+public:
+ //! Constructor. Stores the work session and registers replacer functions.
+ //! @param theWS the work session.
+ Standard_EXPORT StepTidy_PlaneReducer(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 // _StepTidy_DirectionReducer_HeaderFile
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _StepTidy_VectorHasher_HeaderFile
+#define _StepTidy_VectorHasher_HeaderFile
+
+#include <StepTidy_DirectionHasher.pxx>
+
+#include <Standard_HashUtils.hxx>
+#include <StepGeom_Vector.hxx>
+#include <TCollection_HAsciiString.hxx>
+
+//! OCCT-style hasher for StepGeom_Vector entities.
+struct StepTidy_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]{StepTidy_DirectionHasher{}(theVector->Orientation()),
+ opencascade::hash(static_cast<int>(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<TCollection_AsciiString>{}(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 StepTidy_DirectionHasher{}(theVector1->Orientation(), theVector2->Orientation());
+ }
+};
+
+#endif // _StepTidy_VectorHasher_HeaderFile
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <StepTidy_VectorReducer.pxx>
+
+#include <Interface_Graph.hxx>
+#include <StepGeom_Line.hxx>
+#include <StepGeom_Vector.hxx>
+
+//==================================================================================================
+
+StepTidy_VectorReducer::StepTidy_VectorReducer(const Handle(XSControl_WorkSession)& theWS)
+ : StepTidy_EntityReducer<StepGeom_Vector, StepTidy_VectorHasher>(theWS)
+{
+ registerReplacer(STANDARD_TYPE(StepGeom_Line), replaceLine);
+}
+
+//==================================================================================================
+
+bool StepTidy_VectorReducer::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;
+}
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _StepTidy_VectorReducer_HeaderFile
+#define _StepTidy_VectorReducer_HeaderFile
+
+#include <StepTidy_EntityReducer.pxx>
+#include <StepTidy_VectorHasher.pxx>
+
+#include <StepGeom_Vector.hxx>
+
+//! Processor for merging StepGeom_Vector entities.
+//! This processor merges vectors with the same orientation and magnitude and names.
+class StepTidy_VectorReducer : public StepTidy_EntityReducer<StepGeom_Vector, StepTidy_VectorHasher>
+{
+public:
+ //! Constructor. Stores the work session and registers replacer functions.
+ //! @param theWS the work session.
+ Standard_EXPORT StepTidy_VectorReducer(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 // _StepTidy_VectorReducer_HeaderFile
provider.STEP.OCC.write.layer : 1
provider.STEP.OCC.write.props : 1
provider.STEP.OCC.write.model.type : 0
+provider.STEP.OCC.write.cleanduplicates : 0
provider.STEP.OCC.healing.tolerance3d : 1e-06
provider.STEP.OCC.healing.max.tolerance3d : 1
provider.STEP.OCC.healing.min.tolerance3d : 1e-07
provider.STEP.OCC.write.layer : 1
provider.STEP.OCC.write.props : 1
provider.STEP.OCC.write.model.type : 0
+provider.STEP.OCC.write.cleanduplicates : 0
provider.STEP.OCC.healing.tolerance3d : 1e-06
provider.STEP.OCC.healing.max.tolerance3d : 1
provider.STEP.OCC.healing.min.tolerance3d : 1e-07