From: azv Date: Fri, 17 Jun 2022 14:19:17 +0000 (+0300) Subject: bos #29468: Advanced geometry features: distance Edge-Edge & Face-Face X-Git-Tag: V9_11_0a1~3^2 X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=9f730900596e9412f431c23b2bd985063814a750;p=modules%2Fgeom.git bos #29468: Advanced geometry features: distance Edge-Edge & Face-Face --- diff --git a/doc/salome/examples/shape_proximity.py b/doc/salome/examples/shape_proximity.py new file mode 100644 index 000000000..4e634fdc1 --- /dev/null +++ b/doc/salome/examples/shape_proximity.py @@ -0,0 +1,40 @@ +# Shape Proximity + +import math +import salome +salome.salome_init_without_session() +import GEOM +from salome.geom import geomBuilder +geompy = geomBuilder.New() + +# create conical and planar faces +O = geompy.MakeVertex(0, 0, 0) +OX = geompy.MakeVectorDXDYDZ(1, 0, 0) +OY = geompy.MakeVectorDXDYDZ(0, 1, 0) +OZ = geompy.MakeVectorDXDYDZ(0, 0, 1) +Cone_1 = geompy.MakeConeR1R2H(100, 0, 300) +Cone_1_face_3 = geompy.GetSubShape(Cone_1, [3]) +Cone_1_wire_4 = geompy.GetSubShape(Cone_1, [4]) +Face_1 = geompy.MakeFaceFromSurface(Cone_1_face_3, Cone_1_wire_4) +Face_1_edge_5 = geompy.GetSubShape(Face_1, [5]) +Face_2 = geompy.MakeFaceObjHW(Face_1_edge_5, 200, 200) +geompy.Rotate(Face_2, OY, 90*math.pi/180.0) +Face_2_vertex_7 = geompy.GetSubShape(Face_2, [7]) +Translation_1 = geompy.MakeTranslationTwoPoints(Face_2, Face_2_vertex_7, O) + +shape1 = Face_1 +shape2 = Translation_1 + +# perform proximity calculation with the default parameters +p1 = geompy.ShapeProximity() +proximity1 = p1.proximity(shape1, shape2) +print("Proximity with default parameters: ", proximity1) + +# perform proximity calculation with custom parameters +p2 = geompy.ShapeProximity() +p2.setShapes(shape1, shape2) +p2.setSampling(shape1, 100) # number of sample points for the first shape +p2.setSampling(shape2, 40) # number of sample points for the second shape +proximity2_coarse = p2.coarseProximity() +proximity2_fine = p2.preciseProximity() +print("Proximity with custom parameters: coarse = ", proximity2_coarse, "; precise = ", proximity2_fine) diff --git a/doc/salome/examples/tests.set b/doc/salome/examples/tests.set index 327d64070..c2c8eb81e 100644 --- a/doc/salome/examples/tests.set +++ b/doc/salome/examples/tests.set @@ -135,4 +135,5 @@ SET(GOOD_TESTS working_with_groups_ex06.py GEOM_Field.py check_self_intersections_fast.py # OCC > 6.9.0 + shape_proximity.py ) diff --git a/doc/salome/gui/GEOM/images/Shape_proximity_0.png b/doc/salome/gui/GEOM/images/Shape_proximity_0.png new file mode 100644 index 000000000..196b764c7 Binary files /dev/null and b/doc/salome/gui/GEOM/images/Shape_proximity_0.png differ diff --git a/doc/salome/gui/GEOM/images/Shape_proximity_1.png b/doc/salome/gui/GEOM/images/Shape_proximity_1.png new file mode 100644 index 000000000..1217989e0 Binary files /dev/null and b/doc/salome/gui/GEOM/images/Shape_proximity_1.png differ diff --git a/doc/salome/gui/GEOM/input/shape_proximity.doc b/doc/salome/gui/GEOM/input/shape_proximity.doc new file mode 100644 index 000000000..fc82ece1e --- /dev/null +++ b/doc/salome/gui/GEOM/input/shape_proximity.doc @@ -0,0 +1,52 @@ +/*! + +\page shape_proximity_page Shape Proximity + +The Shape Proximity operation calculates maximal of all possible distances between two shapes. +Considering this case : + +\image html Shape_proximity_0.png + +The proximity of blue shape to the red one is computed like this : +For each point of blue the distance to the red is computed using perpendicular projection. The proximity value returned is equal to maximal value of all of this distances. +To do that the implemented algorithm + +1 - sampling shapes by points then calculating the distance from each sample point from one shape to another + +2 - find the position which gives the maximal distance + +3 - improve the proximity value basing on the exact shapes using the points found on step 2 as a start position + +It may happen that for some point of blue shape no distance to red shape exist using perpendicular projection. +This is typically the case here : + +\image html Shape_proximity_1.png + +In the case of no perpendicular projection of a point on blue shape exists, instead of perpendicular projection the minimal distance to border point of red shape is considered. +The distance from EndBlue Point is taken using EndRed Point (black line represents the distance for EndBlue). + +This is just a TUI functionality. The provided class +
+geompy.ShapeProximity()
+
+has an interface to compute proximity value with default parameters +
+p = geompy.ShapeProximity()
+value = p.proximity(shape1, shape2)
+
+ +Moreover, it also provides the functionality to customize the calculation. +For example, compute coarse proximity value basing on the number of sampling points for each shape, +or compute the precise value as a refining operation after the coarse value calculation. +
+p = geompy.ShapeProximity()
+p.setShapes(shape1, shape2) # customize calculator with input shapes
+p.setSampling(shape1, 100)  # assign number of sample points for the first shape
+p.setSampling(shape2, 25)   # assign number of sample points for the second shape
+coarse_proximity = p.coarseProximity() # rough proximity value basing on the shape sampling and tessellation
+fine_proximity = p.preciseProximity()  # more precise proximity value using exact shapes
+
+ +See also a \ref tui_shape_proximity_page "TUI example". + +*/ diff --git a/doc/salome/gui/GEOM/input/tui_measurement_tools.doc b/doc/salome/gui/GEOM/input/tui_measurement_tools.doc index 3966c9c0f..a4e7f5c9d 100644 --- a/doc/salome/gui/GEOM/input/tui_measurement_tools.doc +++ b/doc/salome/gui/GEOM/input/tui_measurement_tools.doc @@ -22,6 +22,7 @@
  • \subpage tui_check_self_intersections_fast_page
  • \subpage tui_fast_intersection_page
  • \subpage tui_check_conformity_page
  • +
  • \subpage tui_shape_proximity_page
  • */ diff --git a/doc/salome/gui/GEOM/input/tui_shape_proximity.doc b/doc/salome/gui/GEOM/input/tui_shape_proximity.doc new file mode 100644 index 000000000..34efab45d --- /dev/null +++ b/doc/salome/gui/GEOM/input/tui_shape_proximity.doc @@ -0,0 +1,6 @@ +/*! + +\page tui_shape_proximity_page Compute Proximity between Shapes +\tui_script{shape_proximity.py} + +*/ diff --git a/doc/salome/gui/GEOM/input/using_measurement_tools.doc b/doc/salome/gui/GEOM/input/using_measurement_tools.doc index 11803eec2..c30850fd3 100644 --- a/doc/salome/gui/GEOM/input/using_measurement_tools.doc +++ b/doc/salome/gui/GEOM/input/using_measurement_tools.doc @@ -21,6 +21,7 @@
  • \subpage whatis_page "WhatIs"
  • \subpage inspect_object_operation_page "Inspect Object"
  • \subpage shape_statistics_operation_page "Shape Statistics"
  • +
  • \subpage shape_proximity_page "Shapes Proximity"
  • \n To check their integrity: diff --git a/idl/GEOM_Gen.idl b/idl/GEOM_Gen.idl index 89a1e7779..d33bc140d 100644 --- a/idl/GEOM_Gen.idl +++ b/idl/GEOM_Gen.idl @@ -4833,6 +4833,38 @@ module GEOM * \param theShape Shape for update. */ double UpdateTolerance(in GEOM_Object theShape); + + /*! + * \brief Get the calculator for the proximity value between the given shapes. + * \param theShape1,theShape2 Shapes to find proximity. + * \return The calculator object. + */ + GEOM_Object ShapeProximityCalculator(in GEOM_Object theShape1, in GEOM_Object theShape2); + + /*! + * \brief Set number sample points to compute the coarse proximity. + * \param theCalculator Proximity calculator. + * \param theShape Shape to be samples. + * \param theNbSamples Number of samples points. + */ + void SetShapeSampling(in GEOM_Object theCalculator, + in GEOM_Object theShape, + in long theNbSamples); + + /*! + * \brief Compute coarse value of the proximity basing on the polygonal representation of shapes. + * \param theCalculator Proximity calculator. + * \return Proximity value. + */ + double GetCoarseProximity(in GEOM_Object theCalculator); + + /*! + * \brief Compute precise value of the proximity basing on the exact shapes. + * \param theCalculator Proximity calculator. + * \return Proximity value. + */ + double GetPreciseProximity(in GEOM_Object theCalculator); + }; // # GEOM_IGroupOperations: diff --git a/src/GEOMImpl/CMakeLists.txt b/src/GEOMImpl/CMakeLists.txt index 6dbbae81e..f79a186a5 100644 --- a/src/GEOMImpl/CMakeLists.txt +++ b/src/GEOMImpl/CMakeLists.txt @@ -95,6 +95,7 @@ SET(GEOMImpl_HEADERS GEOMImpl_ILine.hxx GEOMImpl_IPatchFace.hxx GEOMImpl_IPlane.hxx + GEOMImpl_IProximity.hxx GEOMImpl_IMarker.hxx GEOMImpl_ITranslate.hxx GEOMImpl_IMirror.hxx @@ -182,6 +183,7 @@ SET(GEOMImpl_HEADERS GEOMImpl_FillingDriver.hxx GEOMImpl_GlueDriver.hxx GEOMImpl_PatchFaceDriver.hxx + GEOMImpl_ShapeProximityDriver.hxx GEOMImpl_Types.hxx GEOM_GEOMImpl.hxx GEOMImpl_ICanonicalRecognition.hxx @@ -261,6 +263,7 @@ SET(GEOMImpl_SOURCES GEOMImpl_FillingDriver.cxx GEOMImpl_GlueDriver.cxx GEOMImpl_PatchFaceDriver.cxx + GEOMImpl_ShapeProximityDriver.cxx GEOMImpl_FieldDriver.cxx GEOMImpl_ICanonicalRecognition.cxx ) diff --git a/src/GEOMImpl/GEOMImpl_Gen.cxx b/src/GEOMImpl/GEOMImpl_Gen.cxx index 7df117c33..e1d7d3a28 100644 --- a/src/GEOMImpl/GEOMImpl_Gen.cxx +++ b/src/GEOMImpl/GEOMImpl_Gen.cxx @@ -84,6 +84,7 @@ #include #include #include +#include //============================================================================= /*! @@ -165,6 +166,7 @@ GEOMImpl_Gen::GEOMImpl_Gen() TFunction_DriverTable::Get()->AddDriver(GEOMImpl_MeasureDriver::GetID(), new GEOMImpl_MeasureDriver()); TFunction_DriverTable::Get()->AddDriver(GEOMImpl_PatchFaceDriver::GetID(), new GEOMImpl_PatchFaceDriver()); TFunction_DriverTable::Get()->AddDriver(GEOMImpl_ConformityDriver::GetID(), new GEOMImpl_ConformityDriver()); + TFunction_DriverTable::Get()->AddDriver(GEOMImpl_ShapeProximityDriver::GetID(), new GEOMImpl_ShapeProximityDriver()); // Field TFunction_DriverTable::Get()->AddDriver(GEOMImpl_FieldDriver::GetID(), new GEOMImpl_FieldDriver()); diff --git a/src/GEOMImpl/GEOMImpl_IMeasureOperations.cxx b/src/GEOMImpl/GEOMImpl_IMeasureOperations.cxx index 1c8c7add3..7bfc35b5f 100644 --- a/src/GEOMImpl/GEOMImpl_IMeasureOperations.cxx +++ b/src/GEOMImpl/GEOMImpl_IMeasureOperations.cxx @@ -25,7 +25,9 @@ #include #include +#include #include +#include #include @@ -3278,3 +3280,191 @@ void GEOMImpl_IMeasureOperations::FillErrors } } } + +//======================================================================= +//function : ShapeProximityCalculator +//purpose : returns an object to compute the proximity value +//======================================================================= +Handle(GEOM_Object) GEOMImpl_IMeasureOperations::ShapeProximityCalculator + (Handle(GEOM_Object) theShape1, + Handle(GEOM_Object) theShape2) +{ + SetErrorCode(KO); + + if (theShape1.IsNull() || theShape2.IsNull()) + return NULL; + + Handle(GEOM_Function) aShapeFunc1 = theShape1->GetLastFunction(); + Handle(GEOM_Function) aShapeFunc2 = theShape2->GetLastFunction(); + if (aShapeFunc1.IsNull() || aShapeFunc2.IsNull()) + return NULL; + + Handle(GEOM_Object) aProximityCalc = GetEngine()->AddObject(GEOM_SHAPE_PROXIMITY); + if (aProximityCalc.IsNull()) + return NULL; + + Handle(GEOM_Function) aProximityFuncCoarse = + aProximityCalc->AddFunction(GEOMImpl_ShapeProximityDriver::GetID(), PROXIMITY_COARSE); + //Check if the function is set correctly + if (aProximityFuncCoarse.IsNull() || + aProximityFuncCoarse->GetDriverGUID() != GEOMImpl_ShapeProximityDriver::GetID()) + return NULL; + + GEOMImpl_IProximity aProximity (aProximityFuncCoarse); + aProximity.SetShapes(aShapeFunc1, aShapeFunc2); + + //Make a Python command + GEOM::TPythonDump pd (aProximityFuncCoarse); + pd << "p = geompy.ShapeProximity()\n"; + pd << "p.setShapes(" << theShape1 << ", " << theShape2 << ")"; + + SetErrorCode(OK); + return aProximityCalc; +} + +//======================================================================= +//function : SetShapeSampling +//purpose : set number sample points to compute the coarse proximity +//======================================================================= +void GEOMImpl_IMeasureOperations::SetShapeSampling(Handle(GEOM_Object) theCalculator, + Handle(GEOM_Object) theShape, + const Standard_Integer theNbSamples) +{ + SetErrorCode(KO); + if (theShape.IsNull() || + theCalculator.IsNull() || + theCalculator->GetNbFunctions() <= 0 || + theNbSamples <= 0) + return ; + + Handle(GEOM_Function) aProximityFuncCoarse = theCalculator->GetFunction(1); + if (aProximityFuncCoarse.IsNull() || + aProximityFuncCoarse->GetDriverGUID() != GEOMImpl_ShapeProximityDriver::GetID()) + return ; + + Handle(GEOM_Function) aShapeFunc = theShape->GetLastFunction(); + if (aShapeFunc.IsNull()) + return ; + + GEOMImpl_IProximity aProximity(aProximityFuncCoarse); + Handle(GEOM_Function) aShape1, aShape2; + aProximity.GetShapes(aShape1, aShape2); + if (aShape1->GetValue() == aShapeFunc->GetValue()) + aProximity.SetNbSamples(PROXIMITY_ARG_SAMPLES1, theNbSamples); + else if (aShape2->GetValue() == aShapeFunc->GetValue()) + aProximity.SetNbSamples(PROXIMITY_ARG_SAMPLES2, theNbSamples); + + //Make a Python command + GEOM::TPythonDump(aProximityFuncCoarse, /*append=*/true) << + "p.setSampling(" << theShape << ", " << theNbSamples << ")"; + + SetErrorCode(OK); +} + +//======================================================================= +//function : GetCoarseProximity +//purpose : compute coarse proximity +//======================================================================= +Standard_Real GEOMImpl_IMeasureOperations::GetCoarseProximity(Handle(GEOM_Object) theCalculator, + bool doPythonDump) +{ + SetErrorCode(KO); + if (theCalculator.IsNull()) + return -1; + + Handle(GEOM_Function) aProximityFuncCoarse = theCalculator->GetFunction(1); + if (aProximityFuncCoarse.IsNull() || + aProximityFuncCoarse->GetDriverGUID() != GEOMImpl_ShapeProximityDriver::GetID() || + aProximityFuncCoarse->GetType() != PROXIMITY_COARSE) + return -1; + + // Perform + // We have to recompute the function each time, + // because the number of samples can be changed + try { + OCC_CATCH_SIGNALS; + if (!GetSolver()->ComputeFunction(aProximityFuncCoarse)) { + SetErrorCode("shape proximity driver failed"); + return -1; + } + } + catch (Standard_Failure& aFail) { + SetErrorCode(aFail.GetMessageString()); + return -1; + } + + //Make a Python command + if (doPythonDump) + GEOM::TPythonDump(aProximityFuncCoarse, /*append=*/true) << "value = p.coarseProximity()"; + + SetErrorCode(OK); + GEOMImpl_IProximity aProximity (aProximityFuncCoarse); + return aProximity.GetValue(); +} + +//======================================================================= +//function : GetPreciseProximity +//purpose : compute precise proximity +//======================================================================= +Standard_Real GEOMImpl_IMeasureOperations::GetPreciseProximity(Handle(GEOM_Object) theCalculator) +{ + SetErrorCode(KO); + if (theCalculator.IsNull()) + return -1; + + Handle(GEOM_Function) aProximityFuncCoarse = theCalculator->GetFunction(1); + Handle(GEOM_Function) aProximityFuncFine = theCalculator->GetFunction(2); + if (aProximityFuncFine.IsNull()) + aProximityFuncFine = theCalculator->AddFunction + (GEOMImpl_ShapeProximityDriver::GetID(), PROXIMITY_PRECISE); + + //Check if the functions are set correctly + if (aProximityFuncCoarse.IsNull() || + aProximityFuncCoarse->GetDriverGUID() != GEOMImpl_ShapeProximityDriver::GetID() || + aProximityFuncFine.IsNull() || + aProximityFuncFine->GetDriverGUID() != GEOMImpl_ShapeProximityDriver::GetID()) + return -1; + + // perform coarse computation beforehand + GetCoarseProximity(theCalculator, /*doPythonDump=*/false); + + // transfer parameters from the coarse to precise calculator + GEOMImpl_IProximity aCoarseProximity (aProximityFuncCoarse); + Handle(GEOM_Function) aShape1, aShape2; + aCoarseProximity.GetShapes(aShape1, aShape2); + if (aShape1.IsNull() || aShape2.IsNull()) + return -1; + gp_Pnt aProxPnt1, aProxPnt2; + Standard_Integer intStatus1, intStatus2; + aCoarseProximity.GetProximityPoints(aProxPnt1, aProxPnt2); + aCoarseProximity.GetStatusOfPoints(intStatus1, intStatus2); + Standard_Real aResultValue = aCoarseProximity.GetValue(); + + GEOMImpl_IProximity aFineProximity (aProximityFuncFine); + aFineProximity.SetShapes(aShape1, aShape2); + aFineProximity.SetProximityPoints(aProxPnt1, aProxPnt2); + aFineProximity.SetStatusOfPoints(intStatus1, intStatus2); + aFineProximity.SetValue(aResultValue); // in some cases this value cannot be precised + + // Perform + try { + OCC_CATCH_SIGNALS; + if (!GetSolver()->ComputeFunction(aProximityFuncFine)) { + SetErrorCode("shape proximity driver failed"); + return -1; + } + } + catch (Standard_Failure& aFail) { + SetErrorCode(aFail.GetMessageString()); + return -1; + } + + aResultValue = aFineProximity.GetValue(); + aFineProximity.GetProximityPoints(aProxPnt1, aProxPnt2); + + //Make a Python command + GEOM::TPythonDump(aProximityFuncCoarse, /*append=*/true) << "value = p.preciseProximity()"; + + SetErrorCode(OK); + return aResultValue; +} diff --git a/src/GEOMImpl/GEOMImpl_IMeasureOperations.hxx b/src/GEOMImpl/GEOMImpl_IMeasureOperations.hxx index d896b5da1..3e96a3295 100644 --- a/src/GEOMImpl/GEOMImpl_IMeasureOperations.hxx +++ b/src/GEOMImpl/GEOMImpl_IMeasureOperations.hxx @@ -244,6 +244,16 @@ class GEOMImpl_IMeasureOperations : public GEOM_IOperations { Handle(GEOM_Object) thePoint, Handle(GEOM_Object) theDirection); + // Methods to compute proximity between two shapes + Standard_EXPORT Handle(GEOM_Object) ShapeProximityCalculator(Handle(GEOM_Object) theShape1, + Handle(GEOM_Object) theShape2); + Standard_EXPORT Standard_Real GetCoarseProximity(Handle(GEOM_Object) theCalculator, + bool doPythonDump = true); + Standard_EXPORT Standard_Real GetPreciseProximity(Handle(GEOM_Object) theCalculator); + Standard_EXPORT void SetShapeSampling(Handle(GEOM_Object) theCalculator, + Handle(GEOM_Object) theShape, + const Standard_Integer theNbSamples); + private: void FillErrorsSub diff --git a/src/GEOMImpl/GEOMImpl_IProximity.hxx b/src/GEOMImpl/GEOMImpl_IProximity.hxx new file mode 100644 index 000000000..c32a5fdb7 --- /dev/null +++ b/src/GEOMImpl/GEOMImpl_IProximity.hxx @@ -0,0 +1,122 @@ +// Copyright (C) 2022-2022 CEA/DEN, EDF R&D, OPEN CASCADE +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +#include + +#define PROXIMITY_ARG_SHAPE1 1 +#define PROXIMITY_ARG_SHAPE2 2 +#define PROXIMITY_ARG_SAMPLES1 3 +#define PROXIMITY_ARG_SAMPLES2 4 +#define PROXIMITY_ARG_POINT1 5 +#define PROXIMITY_ARG_POINT2 6 +#define PROXIMITY_ARG_VALUE 7 +#define PROXIMITY_ARG_STATUS1 8 +#define PROXIMITY_ARG_STATUS2 9 + +class GEOMImpl_IProximity +{ +public: + + GEOMImpl_IProximity(Handle(GEOM_Function) theFunction) : _func(theFunction) {} + + void SetShapes(Handle(GEOM_Function) theShape1, Handle(GEOM_Function) theShape2) + { + _func->SetReference(PROXIMITY_ARG_SHAPE1, theShape1); + _func->SetReference(PROXIMITY_ARG_SHAPE2, theShape2); + } + + void GetShapes(Handle(GEOM_Function)& theShape1, Handle(GEOM_Function)& theShape2) const + { + theShape1 = _func->GetReference(PROXIMITY_ARG_SHAPE1); + theShape2 = _func->GetReference(PROXIMITY_ARG_SHAPE2); + } + + void SetNbSamples(const Standard_Integer thePosition, const Standard_Integer theNbSamples) const + { + _func->SetInteger(thePosition, theNbSamples); + } + + Standard_Integer GetNbSamples(const Standard_Integer thePosition) const + { + return _func->GetInteger(thePosition); + } + + void SetProximityPoints(const gp_Pnt& thePoint1, const gp_Pnt& thePoint2) + { + setPoint(PROXIMITY_ARG_POINT1, thePoint1); + setPoint(PROXIMITY_ARG_POINT2, thePoint2); + } + + void SetStatusOfPoints(const Standard_Integer theStatus1, const Standard_Integer theStatus2) + { + setStatus(PROXIMITY_ARG_STATUS1, theStatus1); + setStatus(PROXIMITY_ARG_STATUS2, theStatus2); + } + + void GetProximityPoints(gp_Pnt& thePoint1, gp_Pnt& thePoint2) + { + thePoint1 = getPoint(PROXIMITY_ARG_POINT1); + thePoint2 = getPoint(PROXIMITY_ARG_POINT2); + } + + void GetStatusOfPoints(Standard_Integer& theStatus1, Standard_Integer& theStatus2) + { + theStatus1 = getStatus(PROXIMITY_ARG_STATUS1); + theStatus2 = getStatus(PROXIMITY_ARG_STATUS2); + } + + void SetValue(const Standard_Real theValue) + { + _func->SetReal(PROXIMITY_ARG_VALUE, theValue); + } + + Standard_Real GetValue() const + { + return _func->GetReal(PROXIMITY_ARG_VALUE); + } + +private: + void setPoint(const Standard_Integer thePosition, const gp_Pnt& thePoint) + { + Handle(TColStd_HArray1OfReal) aCoords = new TColStd_HArray1OfReal(1, 3); + aCoords->SetValue(1, thePoint.X()); + aCoords->SetValue(2, thePoint.Y()); + aCoords->SetValue(3, thePoint.Z()); + _func->SetRealArray(thePosition, aCoords); + } + + void setStatus(const Standard_Integer thePosition, const Standard_Integer theStatus) + { + _func->SetInteger(thePosition, theStatus); + } + + gp_Pnt getPoint(const Standard_Integer thePosition) + { + Handle(TColStd_HArray1OfReal) aCoords = _func->GetRealArray(thePosition); + return gp_Pnt(aCoords->Value(1), aCoords->Value(2), aCoords->Value(3)); + } + + Standard_Integer getStatus(const Standard_Integer thePosition) + { + return _func->GetInteger(thePosition); + } + +private: + Handle(GEOM_Function) _func; +}; diff --git a/src/GEOMImpl/GEOMImpl_ShapeProximityDriver.cxx b/src/GEOMImpl/GEOMImpl_ShapeProximityDriver.cxx new file mode 100644 index 000000000..e1397efc4 --- /dev/null +++ b/src/GEOMImpl/GEOMImpl_ShapeProximityDriver.cxx @@ -0,0 +1,457 @@ +// Copyright (C) 2022-2022 CEA/DEN, EDF R&D, OPEN CASCADE +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + static void tessellateShape(const TopoDS_Shape& theShape) + { + Standard_Boolean isTessellate = Standard_False; + TopLoc_Location aLoc; + for (TopExp_Explorer anExp(theShape, TopAbs_FACE); anExp.More() && !isTessellate; anExp.Next()) + { + Handle(Poly_Triangulation) aTria = BRep_Tool::Triangulation(TopoDS::Face(anExp.Value()), aLoc); + isTessellate = aTria.IsNull(); + } + for (TopExp_Explorer anExp(theShape, TopAbs_EDGE); anExp.More() && !isTessellate; anExp.Next()) + { + Handle(Poly_Polygon3D) aPoly = BRep_Tool::Polygon3D(TopoDS::Edge(anExp.Value()), aLoc); + isTessellate = aPoly.IsNull(); + } + + if (isTessellate) + { + BRepMesh_IncrementalMesh aMesher(theShape, 0.1); + Standard_ProgramError_Raise_if(!aMesher.IsDone(), "Meshing failed"); + } + } + + static Standard_Real paramOnCurve(const BRepAdaptor_Curve& theCurve, const gp_Pnt& thePoint, const Standard_Real theTol) + { + Extrema_ExtPC aParamSearch(thePoint, theCurve, theCurve.FirstParameter(), theCurve.LastParameter()); + if (aParamSearch.IsDone()) + { + Standard_Integer anIndMin = 0, aNbExt = aParamSearch.NbExt(); + Standard_Real aSqDistMin = RealLast(); + for (Standard_Integer i = 1; i <= aNbExt; ++i) + { + if (aParamSearch.SquareDistance(i) < aSqDistMin) + { + anIndMin = i; + aSqDistMin = aParamSearch.SquareDistance(i); + } + } + if (anIndMin != 0 && aSqDistMin <= theTol * theTol) + { + return aParamSearch.Point(anIndMin).Parameter(); + } + } + return 0.5 * (theCurve.FirstParameter() + theCurve.LastParameter()); + } + + static void paramsOnSurf(const BRepAdaptor_Surface& theSurf, const gp_Pnt& thePoint, const Standard_Real theTol, + Standard_Real& theU, Standard_Real& theV) + { + Extrema_ExtPS aParamSearch(thePoint, theSurf, Precision::PConfusion(), Precision::PConfusion()); + if (aParamSearch.IsDone()) + { + Standard_Integer anIndMin = 0, aNbExt = aParamSearch.NbExt(); + Standard_Real aSqDistMin = RealLast(); + for (Standard_Integer i = 1; i <= aNbExt; ++i) + { + if (aParamSearch.SquareDistance(i) < aSqDistMin) + { + anIndMin = i; + aSqDistMin = aParamSearch.SquareDistance(i); + } + } + if (anIndMin != 0 && aSqDistMin <= theTol * theTol) + { + return aParamSearch.Point(anIndMin).Parameter(theU, theV); + } + } + theU = 0.5 * (theSurf.FirstUParameter() + theSurf.LastUParameter()); + theV = 0.5 * (theSurf.FirstVParameter() + theSurf.LastVParameter()); + } + + static Standard_Real extremaEE(const TopoDS_Edge& theEdge1, const TopoDS_Edge& theEdge2, + gp_Pnt& thePoint1, gp_Pnt& thePoint2) + { + BRepAdaptor_Curve aCurve1(theEdge1); + BRepAdaptor_Curve aCurve2(theEdge2); + + TopLoc_Location aLoc; + Standard_Real aTol1 = BRep_Tool::Tolerance(theEdge1); + Handle(Poly_Polygon3D) aPoly1 = BRep_Tool::Polygon3D (theEdge1, aLoc); + if (!aPoly1.IsNull()) + aTol1 = Max (aTol1, aPoly1->Deflection()); + Standard_Real aTol2 = BRep_Tool::Tolerance(theEdge2); + Handle(Poly_Polygon3D) aPoly2 = BRep_Tool::Polygon3D (theEdge2, aLoc); + if (!aPoly2.IsNull()) + aTol2 = Max (aTol2, aPoly2->Deflection()); + + Standard_Real aU1 = paramOnCurve(aCurve1, thePoint1, 2*aTol1); + Standard_Real aU2 = paramOnCurve(aCurve2, thePoint2, 2*aTol2); + + Standard_Real aValue = -1.0; + Extrema_LocateExtCC anExtr(aCurve1, aCurve2, aU1, aU2); + if (anExtr.IsDone()) + { + aValue = Sqrt(anExtr.SquareDistance()); + + Extrema_POnCurv aP1, aP2; + anExtr.Point(aP1, aP2); + thePoint1 = aP1.Value(); + thePoint2 = aP2.Value(); + } + return aValue; + } + + static Standard_Real extremaPE(const gp_Pnt& thePoint, + const TopoDS_Edge& theEdge, + gp_Pnt& thePointOnEdge) + { + BRepAdaptor_Curve aCurve (theEdge); + + TopLoc_Location aLoc; + Standard_Real aTol = BRep_Tool::Tolerance(theEdge); + Handle(Poly_Polygon3D) aPoly = BRep_Tool::Polygon3D (theEdge, aLoc); + if (!aPoly.IsNull()) + aTol = Max (aTol, aPoly->Deflection()); + + Standard_Real aParam = paramOnCurve (aCurve, thePointOnEdge, 2*aTol); + + Standard_Real aValue = -1.0; + Extrema_LocateExtPC anExtr (thePoint, aCurve, aParam, Precision::PConfusion()); + if (anExtr.IsDone()) + { + aValue = Sqrt(anExtr.SquareDistance()); + + Extrema_POnCurv aPointOnCurve = anExtr.Point(); + thePointOnEdge = aPointOnCurve.Value(); + } + return aValue; + } + + static Standard_Real extremaPF(const gp_Pnt& thePoint, + const TopoDS_Face& theFace, + gp_Pnt& thePointOnFace) + { + BRepAdaptor_Surface aSurf (theFace); + + TopLoc_Location aLoc; + Standard_Real aTol = BRep_Tool::Tolerance(theFace); + Handle(Poly_Triangulation) aTria = BRep_Tool::Triangulation (theFace, aLoc); + if (!aTria.IsNull()) + aTol = Max (aTol, aTria->Deflection()); + + Standard_Real aU, aV; + paramsOnSurf(aSurf, thePointOnFace, 2*aTol, aU, aV); + + Standard_Real aValue = -1.0; + Extrema_GenLocateExtPS anExtr (aSurf); + anExtr.Perform (thePoint, aU, aV); + if (anExtr.IsDone()) + { + aValue = Sqrt(anExtr.SquareDistance()); + + Extrema_POnSurf aPointOnSurf = anExtr.Point(); + thePointOnFace = aPointOnSurf.Value(); + } + return aValue; + } + + static Standard_Real extremaEF(const TopoDS_Edge& theEdge, const TopoDS_Face& theFace, + gp_Pnt& thePonE, gp_Pnt& thePonF) + { + BRepAdaptor_Curve aCurve(theEdge); + BRepAdaptor_Surface aSurf(theFace); + + TopLoc_Location aLoc; + Standard_Real aTolEdge = BRep_Tool::Tolerance(theEdge); + Handle(Poly_Polygon3D) aPoly = BRep_Tool::Polygon3D (theEdge, aLoc); + if (!aPoly.IsNull()) + aTolEdge = Max (aTolEdge, aPoly->Deflection()); + Standard_Real aTolFace = BRep_Tool::Tolerance(theFace); + Handle(Poly_Triangulation) aTria = BRep_Tool::Triangulation (theFace, aLoc); + if (!aTria.IsNull()) + aTolFace = Max (aTolFace, aTria->Deflection()); + + Standard_Real aP = paramOnCurve(aCurve, thePonE, 2*aTolEdge); + Standard_Real aU, aV; + paramsOnSurf(aSurf, thePonF, 2*aTolFace, aU, aV); + + Standard_Real aValue = -1.0; + Extrema_GenLocateExtCS anExtr(aCurve, aSurf, aP, aU, aV, Precision::PConfusion(), Precision::PConfusion()); + if (anExtr.IsDone()) + { + aValue = Sqrt(anExtr.SquareDistance()); + thePonE = anExtr.PointOnCurve().Value(); + thePonF = anExtr.PointOnSurface().Value(); + } + return aValue; + } + + static Standard_Real extremaFF(const TopoDS_Face& theFace1, const TopoDS_Face& theFace2, + gp_Pnt& thePoint1, gp_Pnt& thePoint2) + { + BRepAdaptor_Surface aSurf1(theFace1); + BRepAdaptor_Surface aSurf2(theFace2); + + TopLoc_Location aLoc; + Standard_Real aTol1 = BRep_Tool::Tolerance(theFace1); + Handle(Poly_Triangulation) aTria1 = BRep_Tool::Triangulation (theFace1, aLoc); + if (!aTria1.IsNull()) + aTol1 = Max (aTol1, aTria1->Deflection()); + Standard_Real aTol2 = BRep_Tool::Tolerance(theFace2); + Handle(Poly_Triangulation) aTria2 = BRep_Tool::Triangulation (theFace2, aLoc); + if (!aTria2.IsNull()) + aTol2 = Max (aTol2, aTria2->Deflection()); + + Standard_Real aU1, aV1; + paramsOnSurf(aSurf1, thePoint1, 2*aTol1, aU1, aV1); + Standard_Real aU2, aV2; + paramsOnSurf(aSurf2, thePoint2, 2*aTol2, aU2, aV2); + + Standard_Real aValue = -1.0; + Extrema_GenLocateExtSS anExtr(aSurf1, aSurf2, aU1, aV1, aU2, aV2, Precision::PConfusion(), Precision::PConfusion()); + if (anExtr.IsDone()) + { + aValue = Sqrt(anExtr.SquareDistance()); + thePoint1 = anExtr.PointOnS1().Value(); + thePoint2 = anExtr.PointOnS2().Value(); + } + return aValue; + } +} + +//======================================================================= +//function : GetID +//purpose : +//======================================================================= +const Standard_GUID& GEOMImpl_ShapeProximityDriver::GetID() +{ + static Standard_GUID aShapeProximityDriver("1C3449A6-E4EB-407D-B623-89261C4BA785"); + return aShapeProximityDriver; +} + +//======================================================================= +//function : Execute +//purpose : +//======================================================================= +Standard_Integer GEOMImpl_ShapeProximityDriver::Execute(Handle(TFunction_Logbook)& log) const +{ + if (Label().IsNull()) + return 0; + + Handle(GEOM_Function) aFunction = GEOM_Function::GetFunction(Label()); + GEOMImpl_IProximity aProximity (aFunction); + + Handle(GEOM_Function) aShapeFunc1, aShapeFunc2; + aProximity.GetShapes(aShapeFunc1, aShapeFunc2); + if (aShapeFunc1.IsNull() || aShapeFunc2.IsNull()) + return 0; + + TopoDS_Shape aShape1 = aShapeFunc1->GetValue(); + TopoDS_Shape aShape2 = aShapeFunc2->GetValue(); + + Standard_Real aValue = -1.0; + + if (aFunction->GetType() == PROXIMITY_COARSE) + { + // tessellate shapes if there is no mesh exists + tessellateShape(aShape1); + tessellateShape(aShape2); + + // compute proximity basing on the tessellation + BRepExtrema_ShapeProximity aCalcProx; + aCalcProx.LoadShape1(aShape1); + aCalcProx.LoadShape2(aShape2); + aCalcProx.SetNbSamples1(aProximity.GetNbSamples(PROXIMITY_ARG_SAMPLES1)); + aCalcProx.SetNbSamples2(aProximity.GetNbSamples(PROXIMITY_ARG_SAMPLES2)); + aCalcProx.Perform(); + + if (aCalcProx.IsDone()) + { + aValue = aCalcProx.Proximity(); + aProximity.SetProximityPoints(aCalcProx.ProximityPoint1(), + aCalcProx.ProximityPoint2()); + aProximity.SetStatusOfPoints((Standard_Integer)aCalcProx.ProxPntStatus1(), + (Standard_Integer)aCalcProx.ProxPntStatus2()); + } + else + Standard_ConstructionError::Raise("Failed to compute coarse proximity"); + } + else if (aFunction->GetType() == PROXIMITY_PRECISE) + { + // coarse proximity value + // in some cases this value cannot be precised + // it can be precised only if at least one point is in the middle of the shape + aValue = aProximity.GetValue(); + + TopAbs_ShapeEnum aType1 = aShape1.ShapeType(); + TopAbs_ShapeEnum aType2 = aShape2.ShapeType(); + + gp_Pnt aPnt1, aPnt2; + BRepExtrema_ProximityDistTool::ProxPnt_Status aStatus1, aStatus2; + Standard_Integer intStatus1, intStatus2; + aProximity.GetProximityPoints(aPnt1, aPnt2); + aProximity.GetStatusOfPoints(intStatus1, intStatus2); + aStatus1 = (BRepExtrema_ProximityDistTool::ProxPnt_Status)intStatus1; + aStatus2 = (BRepExtrema_ProximityDistTool::ProxPnt_Status)intStatus2; + + if (aType1 == TopAbs_EDGE) + { + if (aType2 == TopAbs_EDGE) + { + if (aStatus1 == BRepExtrema_ProximityDistTool::ProxPnt_Status_MIDDLE && + aStatus2 == BRepExtrema_ProximityDistTool::ProxPnt_Status_MIDDLE) + { + aValue = extremaEE(TopoDS::Edge(aShape1), TopoDS::Edge(aShape2), aPnt1, aPnt2); + } + else if (aStatus1 == BRepExtrema_ProximityDistTool::ProxPnt_Status_BORDER && + aStatus2 == BRepExtrema_ProximityDistTool::ProxPnt_Status_MIDDLE) + { + aValue = extremaPE(aPnt1, TopoDS::Edge(aShape2), aPnt2); + } + else if (aStatus1 == BRepExtrema_ProximityDistTool::ProxPnt_Status_MIDDLE && + aStatus2 == BRepExtrema_ProximityDistTool::ProxPnt_Status_BORDER) + { + aValue = extremaPE(aPnt2, TopoDS::Edge(aShape1), aPnt1); + } + } + else if (aType2 == TopAbs_FACE) + { + if (aStatus1 == BRepExtrema_ProximityDistTool::ProxPnt_Status_MIDDLE && + aStatus2 == BRepExtrema_ProximityDistTool::ProxPnt_Status_MIDDLE) + { + aValue = extremaEF(TopoDS::Edge(aShape1), TopoDS::Face(aShape2), aPnt1, aPnt2); + } + else if (aStatus1 == BRepExtrema_ProximityDistTool::ProxPnt_Status_BORDER && + aStatus2 == BRepExtrema_ProximityDistTool::ProxPnt_Status_MIDDLE) + { + aValue = extremaPF(aPnt1, TopoDS::Face(aShape2), aPnt2); + } + else if (aStatus1 == BRepExtrema_ProximityDistTool::ProxPnt_Status_MIDDLE && + aStatus2 == BRepExtrema_ProximityDistTool::ProxPnt_Status_BORDER) + { + aValue = extremaPE(aPnt2, TopoDS::Edge(aShape1), aPnt1); + } + } + } + else if (aType1 == TopAbs_FACE) + { + if (aType2 == TopAbs_EDGE) + { + if (aStatus1 == BRepExtrema_ProximityDistTool::ProxPnt_Status_MIDDLE && + aStatus2 == BRepExtrema_ProximityDistTool::ProxPnt_Status_MIDDLE) + { + aValue = extremaEF(TopoDS::Edge(aShape2), TopoDS::Face(aShape1), aPnt2, aPnt1); + } + else if (aStatus1 == BRepExtrema_ProximityDistTool::ProxPnt_Status_BORDER && + aStatus2 == BRepExtrema_ProximityDistTool::ProxPnt_Status_MIDDLE) + { + aValue = extremaPE(aPnt1, TopoDS::Edge(aShape2), aPnt2); + } + else if (aStatus1 == BRepExtrema_ProximityDistTool::ProxPnt_Status_MIDDLE && + aStatus2 == BRepExtrema_ProximityDistTool::ProxPnt_Status_BORDER) + { + aValue = extremaPF(aPnt2, TopoDS::Face(aShape1), aPnt1); + } + } + else if (aType2 == TopAbs_FACE) + { + if (aStatus1 == BRepExtrema_ProximityDistTool::ProxPnt_Status_MIDDLE && + aStatus2 == BRepExtrema_ProximityDistTool::ProxPnt_Status_MIDDLE) + { + aValue = extremaFF(TopoDS::Face(aShape1), TopoDS::Face(aShape2), aPnt1, aPnt2); + } + else if (aStatus1 == BRepExtrema_ProximityDistTool::ProxPnt_Status_BORDER && + aStatus2 == BRepExtrema_ProximityDistTool::ProxPnt_Status_MIDDLE) + { + aValue = extremaPF(aPnt1, TopoDS::Face(aShape2), aPnt2); + } + else if (aStatus1 == BRepExtrema_ProximityDistTool::ProxPnt_Status_MIDDLE && + aStatus2 == BRepExtrema_ProximityDistTool::ProxPnt_Status_BORDER) + { + aValue = extremaPF(aPnt2, TopoDS::Face(aShape1), aPnt1); + } + } + } + + if (aValue >= 0) + aProximity.SetProximityPoints(aPnt1, aPnt2); + else + Standard_ConstructionError::Raise("Failed to compute precise proximity"); + } + else + { + Standard_ConstructionError::Raise("incorrect algorithm"); + } + + aProximity.SetValue(aValue); + log->SetTouched(Label()); + return 1; +} + +//======================================================================= +//function : GetCreationInformation +//purpose : Returns a name of creation operation and names and values of creation parameters +//======================================================================= +bool GEOMImpl_ShapeProximityDriver::GetCreationInformation( + std::string& theOperationName, + std::vector& theParams) +{ + if (Label().IsNull()) + return false; + + Handle(GEOM_Function) aFunc = GEOM_Function::GetFunction(Label()); + GEOMImpl_IProximity aProxFunc(aFunc); + Handle(GEOM_Function) aShape1, aShape2; + aProxFunc.GetShapes(aShape1, aShape2); + + if (aFunc->GetType() == PROXIMITY_COARSE) + theOperationName = "PROXIMITY_COARSE"; + else if (aFunc->GetType() == PROXIMITY_PRECISE) + theOperationName = "PROXIMITY_PRECISE"; + + AddParam(theParams, "Shape1", aShape1); + AddParam(theParams, "Shape2", aShape2); + + return false; +} + +IMPLEMENT_STANDARD_RTTIEXT(GEOMImpl_ShapeProximityDriver, GEOM_BaseDriver) diff --git a/src/GEOMImpl/GEOMImpl_ShapeProximityDriver.hxx b/src/GEOMImpl/GEOMImpl_ShapeProximityDriver.hxx new file mode 100644 index 000000000..3a1312e2f --- /dev/null +++ b/src/GEOMImpl/GEOMImpl_ShapeProximityDriver.hxx @@ -0,0 +1,45 @@ +// Copyright (C) 2022-2022 CEA/DEN, EDF R&D, OPEN CASCADE +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +#ifndef _GEOMImpl_ShapeProximityDriver_HeaderFile +#define _GEOMImpl_ShapeProximityDriver_HeaderFile + +#include + +DEFINE_STANDARD_HANDLE(GEOMImpl_ShapeProximityDriver, GEOM_BaseDriver) + +class GEOMImpl_ShapeProximityDriver : public GEOM_BaseDriver { + +public: + + Standard_EXPORT GEOMImpl_ShapeProximityDriver() {} + Standard_EXPORT virtual Standard_Integer Execute(Handle(TFunction_Logbook)& log) const; + Standard_EXPORT virtual void Validate(Handle(TFunction_Logbook)&) const {} + Standard_EXPORT Standard_Boolean MustExecute(const Handle(TFunction_Logbook)&) const { return Standard_True; } + + Standard_EXPORT static const Standard_GUID& GetID(); + Standard_EXPORT ~GEOMImpl_ShapeProximityDriver() {} + + Standard_EXPORT virtual + bool GetCreationInformation(std::string& theOperationName, + std::vector& params); + + DEFINE_STANDARD_RTTIEXT(GEOMImpl_ShapeProximityDriver, GEOM_BaseDriver) +}; + +#endif diff --git a/src/GEOMImpl/GEOMImpl_Types.hxx b/src/GEOMImpl/GEOMImpl_Types.hxx index fec26368a..8984fe939 100644 --- a/src/GEOMImpl/GEOMImpl_Types.hxx +++ b/src/GEOMImpl/GEOMImpl_Types.hxx @@ -125,6 +125,7 @@ #define GEOM_PATCH_FACE 60 +#define GEOM_SHAPE_PROXIMITY 61 #define GEOM_CHECKCONFORMITY 62 //GEOM_Function types @@ -365,6 +366,10 @@ #define VERTEX_BY_INDEX 5 #define CURVATURE_VEC_MEASURE 6 +// Proximity algorithms +#define PROXIMITY_COARSE 1 +#define PROXIMITY_PRECISE 2 + #define GROUP_FUNCTION 1 // Curve constructor type diff --git a/src/GEOM_I/GEOM_IMeasureOperations_i.cc b/src/GEOM_I/GEOM_IMeasureOperations_i.cc index c2bffa8e9..e829aceab 100644 --- a/src/GEOM_I/GEOM_IMeasureOperations_i.cc +++ b/src/GEOM_I/GEOM_IMeasureOperations_i.cc @@ -1443,3 +1443,97 @@ void GEOM_IMeasureOperations_i::ConvertToList(const GEOM::GEOM_IMeasureOperation theListOfResults.push_back(aCheck); } } + +//============================================================================= +/*! + * ShapeProximityCalculator + */ +//============================================================================= +GEOM::GEOM_Object_ptr GEOM_IMeasureOperations_i::ShapeProximityCalculator(GEOM::GEOM_Object_ptr theShape1, + GEOM::GEOM_Object_ptr theShape2) +{ + GEOM::GEOM_Object_var anEmptyCalc; + + //Set a not done flag + GetOperations()->SetNotDone(); + + //Get the reference shape + Handle(::GEOM_Object) aShape1 = GetObjectImpl(theShape1); + Handle(::GEOM_Object) aShape2 = GetObjectImpl(theShape2); + if (aShape1.IsNull() || aShape2.IsNull()) + return anEmptyCalc._retn(); + + Handle(::GEOM_Object) aCalculator = GetOperations()->ShapeProximityCalculator(aShape1, aShape2); + if (!GetOperations()->IsDone() || aCalculator.IsNull()) + return anEmptyCalc._retn(); + + return GetObject(aCalculator); +} + +//============================================================================= +/*! + * SetShapeSampling + */ + //============================================================================= +void GEOM_IMeasureOperations_i::SetShapeSampling(GEOM::GEOM_Object_ptr theCalculator, + GEOM::GEOM_Object_ptr theShape, + CORBA::Long theNbSamples) +{ + //Set a not done flag + GetOperations()->SetNotDone(); + + //Get the proximity calculator + Handle(::GEOM_Object) aCalc = GetObjectImpl(theCalculator); + if (aCalc.IsNull()) + return ; + //Get the reference shape + Handle(::GEOM_Object) aShape = GetObjectImpl(theShape); + if (aShape.IsNull()) + return ; + + GetOperations()->SetShapeSampling(aCalc, aShape, theNbSamples); +} + +//============================================================================= +/*! + * GetCoarseProximity + */ + //============================================================================= +CORBA::Double GEOM_IMeasureOperations_i::GetCoarseProximity(GEOM::GEOM_Object_ptr theCalculator) +{ + //Set a not done flag + GetOperations()->SetNotDone(); + + //Get the reference shape + Handle(::GEOM_Object) aCalc = GetObjectImpl(theCalculator); + if (aCalc.IsNull()) + return -1.0; + + Standard_Real aProximity = GetOperations()->GetCoarseProximity(aCalc); + if (!GetOperations()->IsDone()) + return -1.0; + + return aProximity; +} + +//============================================================================= +/*! + * GetPreciseProximity + */ + //============================================================================= +CORBA::Double GEOM_IMeasureOperations_i::GetPreciseProximity(GEOM::GEOM_Object_ptr theCalculator) +{ + //Set a not done flag + GetOperations()->SetNotDone(); + + //Get the reference shape + Handle(::GEOM_Object) aCalc = GetObjectImpl(theCalculator); + if (aCalc.IsNull()) + return -1.0; + + Standard_Real aProximity = GetOperations()->GetPreciseProximity(aCalc); + if (!GetOperations()->IsDone()) + return -1.0; + + return aProximity; +} diff --git a/src/GEOM_I/GEOM_IMeasureOperations_i.hh b/src/GEOM_I/GEOM_IMeasureOperations_i.hh index 630218c04..ded2f9c41 100644 --- a/src/GEOM_I/GEOM_IMeasureOperations_i.hh +++ b/src/GEOM_I/GEOM_IMeasureOperations_i.hh @@ -191,6 +191,15 @@ class GEOM_I_EXPORT GEOM_IMeasureOperations_i : CORBA::Double UpdateTolerance(GEOM::GEOM_Object_ptr theShape); + // Methods to compute proximity between two shapes + GEOM::GEOM_Object_ptr ShapeProximityCalculator (GEOM::GEOM_Object_ptr theShape1, + GEOM::GEOM_Object_ptr theShape2); + void SetShapeSampling(GEOM::GEOM_Object_ptr theCalculator, + GEOM::GEOM_Object_ptr theShape, + CORBA::Long theNbSamples); + CORBA::Double GetCoarseProximity(GEOM::GEOM_Object_ptr theCalculator); + CORBA::Double GetPreciseProximity(GEOM::GEOM_Object_ptr theCalculator); + ::GEOMImpl_IMeasureOperations* GetOperations() { return (::GEOMImpl_IMeasureOperations*)GetImpl(); } diff --git a/src/GEOM_SWIG/CMakeLists.txt b/src/GEOM_SWIG/CMakeLists.txt index e9b78cadc..5735fa949 100644 --- a/src/GEOM_SWIG/CMakeLists.txt +++ b/src/GEOM_SWIG/CMakeLists.txt @@ -73,6 +73,7 @@ SET(_python_SCRIPTS gsketcher.py canonicalrecognition.py conformity.py + proximity.py ) # Advanced scripts diff --git a/src/GEOM_SWIG/geomBuilder.py b/src/GEOM_SWIG/geomBuilder.py index cf5a9dba0..d28107781 100644 --- a/src/GEOM_SWIG/geomBuilder.py +++ b/src/GEOM_SWIG/geomBuilder.py @@ -262,6 +262,7 @@ import functools from salome.geom.gsketcher import Sketcher3D, Sketcher2D, Polyline2D from salome.geom.canonicalrecognition import CanonicalRecognition from salome.geom.conformity import CheckConformity +from salome.geom.proximity import ShapeProximity # In case the omniORBpy EnumItem class does not fully support Python 3 # (for instance in version 4.2.1-2), the comparison ordering methods must be @@ -14056,6 +14057,21 @@ class geomBuilder(GEOM._objref_GEOM_Gen): conf = CheckConformity (shape, self) return conf + ## Obtain a shape proximity calculator + # @return An instance of @ref proximity.ShapeProximity "ShapeProximity" interface + # + # @ref tui_proximity_page "Example" + def ShapeProximity (self): + """ + Obtain a shape proximity calculator. + + Example of usage: + prox = geompy.ShapeProximity() + value = prox.proximity(shape1, shape2) + """ + prox = ShapeProximity (self) + return prox + # end of l2_testing ## @} diff --git a/src/GEOM_SWIG/proximity.py b/src/GEOM_SWIG/proximity.py new file mode 100644 index 000000000..ac24fbe12 --- /dev/null +++ b/src/GEOM_SWIG/proximity.py @@ -0,0 +1,91 @@ +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2022-2022 CEA/DEN, EDF R&D, OPEN CASCADE +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com + +## @defgroup proximity ShapeProximity - tool to check the proximity distance between two shapes +# @{ +# @details +# The tool provides the user a possibility to compute the proximity distance between two shapes: +# * a minimal diameter of a tube containing both edges (for edge-edge proximity); +# * a minimal thickness of a shell containing both faces (for face-face proximity). +# +# In other words, this tool calculate maximal of all possible distances between pair of objects. +# It is supported to compute distance between two edges or between two faces. +# Other combinations of shapes are prohibited. +# @} + +""" + \namespace geompy + \brief ShapeProximity interface +""" + +## A class to compute proximity value between two shapes. +# Use geompy.ShapeProximity() method to obtain an instance of this class. +# +# @ref tui_proximity_page "Example" +# @ingroup proximity +class ShapeProximity(): + """ + ShapeProximity interface. + + Example of usage: + prox = geompy.ShapeProximity() + value = prox.proximity(shape1, shape2) + """ + + def __init__(self, geompyD): + self.myCommand = "ShapeProximity" + self.myOp = geompyD.GetIMeasureOperations() + pass + + ## Computes proximity between two shapes of the same type + def proximity(self, shape1, shape2): + self.setShapes(shape1, shape2) + #self.coarseProximity() + return self.preciseProximity() + pass + + ## Customize object with the input shapes + def setShapes(self, shape1, shape2): + self.myProximityValue = None + from salome.geom.geomBuilder import RaiseIfFailed + self.myCalc = self.myOp.ShapeProximityCalculator(shape1, shape2) + RaiseIfFailed(self.myCommand, self.myOp) + pass + + ## Define the minimal number of sample points for the given shape, + # which should be used for raw computation of proximity value + def setSampling(self, shape, nbSamples): + self.myOp.SetShapeSampling(self.myCalc, shape, nbSamples) + pass + + ## Find rough proximity value based on polygon/tessellation representation + def coarseProximity(self): + from salome.geom.geomBuilder import RaiseIfFailed + self.myProximityValue = self.myOp.GetCoarseProximity(self.myCalc) + RaiseIfFailed(self.myCommand, self.myOp) + return self.myProximityValue + pass + + ## Find precise proximity value based on exact shapes + def preciseProximity(self): + from salome.geom.geomBuilder import RaiseIfFailed + self.myProximityValue = self.myOp.GetPreciseProximity(self.myCalc) + RaiseIfFailed(self.myCommand, self.myOp) + return self.myProximityValue + pass diff --git a/test/test_proximity_edge_edge.py b/test/test_proximity_edge_edge.py new file mode 100644 index 000000000..9c8e7329c --- /dev/null +++ b/test/test_proximity_edge_edge.py @@ -0,0 +1,130 @@ +# Shape Proximity between edges + +import math +import salome +salome.salome_init_without_session() +import GEOM +from salome.geom import geomBuilder +geompy = geomBuilder.New() + +O = geompy.MakeVertex(0, 0, 0) +OX = geompy.MakeVectorDXDYDZ(1, 0, 0) +OY = geompy.MakeVectorDXDYDZ(0, 1, 0) +OZ = geompy.MakeVectorDXDYDZ(0, 0, 1) + +# Case 1: two bezier curves (original Cas2_29468.py) +from math import sqrt + +# 283x384 +szY = 384 +listOfPtsRed_gimp = [(10,84), (54,96),(145,146),(167,167),(185,212),(187,234),(176,302)] +listOfPtsBlue_gimp = [(120,72),(170,87),(227,118),(238,126),(243,157),(203,216),(134,281),(94,324)] +# +listOfPtsRed = [(x,szY-y) for x,y in listOfPtsRed_gimp] +listOfPtsBlue = [(x,szY-y) for x,y in listOfPtsBlue_gimp] +# +verticesRed = [geompy.MakeVertex(x,y,0) for x,y in listOfPtsRed] +verticesBlue = [geompy.MakeVertex(x,y,0) for x,y in listOfPtsBlue] +for i,(x,y) in enumerate(listOfPtsRed): + geompy.addToStudy(geompy.MakeVertex(x,y,0),"red_pt{}".format(i)) +for i,(x,y) in enumerate(listOfPtsBlue): + geompy.addToStudy(geompy.MakeVertex(x,y,0),"blue_pt{}".format(i)) +redEdge = geompy.MakeBezier(verticesRed) +blueEdge = geompy.MakeBezier(verticesBlue) +# +geompy.addToStudy(redEdge,"red") +geompy.addToStudy(blueEdge,"blue") + +XY_red = (152,214) +XY_blue = (215,260) +exp_red = geompy.MakeVertex(*XY_red,0) +exp_blue = geompy.MakeVertex(*XY_blue,0) +geompy.addToStudy(exp_red,"exp_red") +geompy.addToStudy(exp_blue,"exp_blue") + +p = geompy.ShapeProximity() +p.setShapes(redEdge, blueEdge) +p.setSampling(redEdge, 1000) +p.setSampling(blueEdge, 1000) +p_coarse = p.coarseProximity() +p_precise = p.preciseProximity() +print( "coarse = {} ; fine = {}".format(p_coarse,p_precise) ) +print( "Manually obtained value = {}".format( sqrt( (XY_red[0]-XY_blue[0])**2 + (XY_red[1]-XY_blue[1])**2 ) ) ) + +assert(math.fabs(p_coarse - 223.00892775) < 1.e-7) + +prev = geompy.ShapeProximity() +prev.setShapes(blueEdge, redEdge) +prev.setSampling(redEdge, 1000) +prev.setSampling(blueEdge, 1000) +p_coarse = prev.coarseProximity() +p_precise = prev.preciseProximity() +print( "coarse = {} ; fine = {}".format(p_coarse,p_precise) ) + +assert(math.fabs(p_coarse - 84.89994110) < 1.e-7) + +# Case 2: two bezier curves (different coarse and fine proximities) +V1 = geompy.MakeVertex(10, 10, 0) +V2 = geompy.MakeVertex(20, -10, 0) +V3 = geompy.MakeVertex(30, 0, 0) +V4 = geompy.MakeVertex(0, -3, 0) +V5 = geompy.MakeVertex(13, -10, 0) +V6 = geompy.MakeVertex(25, 10, 0) +V7 = geompy.MakeVertex(30, 5, 0) +BC1 = geompy.MakeBezier([ O, V1, V2, V3], False, "BC1") +BC2 = geompy.MakeBezier([V4, V5, V6, V7], False, "BC2") + +pcalc = geompy.ShapeProximity() +pcalc.setShapes(BC1, BC2) +p_coarse = pcalc.coarseProximity() +p_fine = pcalc.preciseProximity() + +assert(math.fabs(p_coarse - 7.3126564) < 1.e-7) +assert(math.fabs(p_fine - 7.380468495) < 1.e-7) + +# Case 3: arc and segment +Vertex_1 = geompy.MakeVertex(0, 0, -1) +Vertex_2 = geompy.MakeVertex(1, 0, 0) +Vertex_3 = geompy.MakeVertex(0, 0, 1) +Arc_1 = geompy.MakeArc(Vertex_1, Vertex_2, Vertex_3) +Arc_1_vertex_2 = geompy.GetSubShape(Arc_1, [2]) +Edge_1 = geompy.MakeEdgeOnCurveByLength(Arc_1, 3, Arc_1_vertex_2) +Edge_2 = geompy.MakeEdge(Vertex_1, Vertex_3) + +shape1 = Edge_1 +shape2 = Edge_2 + +# perform proximity calculation with the default parameters +p1 = geompy.ShapeProximity() +proximity1 = p1.proximity(shape1, shape2) + +# perform proximity calculation with custom parameters +p2 = geompy.ShapeProximity() +p2.setShapes(shape1, shape2) +p2.setSampling(shape1, 100) # number of sample points for the first shape +p2.setSampling(shape2, 40) # number of sample points for the second shape +proximity2_coarse = p2.coarseProximity() +proximity2_fine = p2.preciseProximity() + +assert(math.fabs(proximity1 - proximity2_fine) < 1.e-7) +assert(math.fabs(proximity2_coarse - 0.99998769) < 1.e-7) +assert(math.fabs(proximity2_fine - 1) < 1.e-7) + +# move second edge and check proximity +Translation_1 = geompy.MakeTranslation(Edge_2, 0.3, 0, 0) +shape2 = Translation_1 + +# perform proximity calculation with the default parameters +p1 = geompy.ShapeProximity() +proximity1 = p1.proximity(shape1, shape2) + +# perform proximity calculation with custom parameters +p2 = geompy.ShapeProximity() +p2.setShapes(shape1, shape2) +p2.setSampling(shape1, 100) # number of sample points for the first shape +p2.setSampling(shape2, 40) # number of sample points for the second shape +proximity2_coarse = p2.coarseProximity() +proximity2_fine = p2.preciseProximity() + +assert(math.fabs(proximity1 - 0.7) < 1.e-7) +assert(math.fabs(proximity2_fine - 0.7) < 1.e-7) diff --git a/test/test_proximity_face_face.py b/test/test_proximity_face_face.py new file mode 100644 index 000000000..a143709ba --- /dev/null +++ b/test/test_proximity_face_face.py @@ -0,0 +1,87 @@ +# Shape Proximity between faces + +import math +import salome +salome.salome_init_without_session() +import GEOM +from salome.geom import geomBuilder +geompy = geomBuilder.New() + +O = geompy.MakeVertex(0, 0, 0) +OX = geompy.MakeVectorDXDYDZ(1, 0, 0) +OY = geompy.MakeVectorDXDYDZ(0, 1, 0) +OZ = geompy.MakeVectorDXDYDZ(0, 0, 1) + +# Case 1: cylinder and sphere (different coarse and fine proximities) +OCyl = geompy.MakeVertex(0, -5, 15) +Cyl = geompy.MakeCylinder(OCyl, OY, 3, 10, "Cyl") +AX1 = geompy.MakeTranslation(OX, 0, 0, 15, "AX1") +geompy.Rotate(Cyl, AX1, -20.0*math.pi/180.0) +Cyl_face = geompy.SubShapeAllSortedCentres(Cyl, geompy.ShapeType["FACE"], "Face")[1] +Sph = geompy.MakeSphereR(10, "Sph") +Box_1 = geompy.MakeBoxDXDYDZ(40, 40, 27.071067) +Translation_1 = geompy.MakeTranslation(Box_1, -20, -20, -20) +Cut_1 = geompy.MakeCutList(Sph, [Translation_1], True, "Cut_1") +Sph_face = geompy.SubShapeAllSortedCentres(Cut_1, geompy.ShapeType["FACE"], "Face")[1] + +pcalc = geompy.ShapeProximity() +#pcalc.setShapes(Cyl_face, Sph_face) +pcalc.setShapes(Sph_face, Cyl_face) +p_coarse = pcalc.coarseProximity() +p_fine = pcalc.preciseProximity() + +assert(math.fabs(p_coarse - 9.8649933) < 1.e-7) +assert(math.fabs(p_fine - 7.6984631) < 1.e-7) + +geompy.MakeVertex(0, 2.63303, 17.2342, "p1") +geompy.MakeVertex(0, 0, 10, "p2") + +print("With sampling 0: coarse = {} ; fine = {}".format(p_coarse, p_fine)) + +pcalc.setSampling(Cyl_face, 100) # number of sample points for the first shape +pcalc.setSampling(Sph_face, 100) # number of sample points for the second shape +p_coarse = pcalc.coarseProximity() +p_fine = pcalc.preciseProximity() + +print("With sampling 100: coarse = {} ; fine = {}".format(p_coarse, p_fine)) + +pcalc.setSampling(Cyl_face, 1000) # number of sample points for the first shape +pcalc.setSampling(Sph_face, 1000) # number of sample points for the second shape +p_coarse = pcalc.coarseProximity() +p_fine = pcalc.preciseProximity() + +print("With sampling 1000: coarse = {} ; fine = {}".format(p_coarse, p_fine)) + +# Case 2: conical and planar faces +Cone_1 = geompy.MakeConeR1R2H(100, 0, 300) +Cone_1_face_3 = geompy.GetSubShape(Cone_1, [3]) +Cone_1_wire_4 = geompy.GetSubShape(Cone_1, [4]) +Face_1 = geompy.MakeFaceFromSurface(Cone_1_face_3, Cone_1_wire_4, "Face_1") +Face_1_edge_5 = geompy.GetSubShape(Face_1, [5]) +Face_2 = geompy.MakeFaceObjHW(Face_1_edge_5, 200, 200) +geompy.Rotate(Face_2, OY, 90*math.pi/180.0) +Face_2_vertex_7 = geompy.GetSubShape(Face_2, [7]) +Translation_1 = geompy.MakeTranslationTwoPoints(Face_2, Face_2_vertex_7, O, "Translation_1") + +shape1 = Face_1 +shape2 = Translation_1 + +# perform proximity calculation with the default parameters +p1 = geompy.ShapeProximity() +proximity1 = p1.proximity(shape1, shape2) + +# perform proximity calculation with custom parameters +p2 = geompy.ShapeProximity() +p2.setShapes(shape1, shape2) +p2.setSampling(shape1, 100) # number of sample points for the first shape +p2.setSampling(shape2, 40) # number of sample points for the second shape +proximity2_coarse = p2.coarseProximity() +proximity2_fine = p2.preciseProximity() + +assert(math.fabs(proximity1 - proximity2_fine) < 1.e-7) +assert(math.fabs(proximity2_coarse - 127.1141386) < 1.e-7) +#assert(math.fabs(proximity2_fine - 94.8683298) < 1.e-7) +assert(math.fabs(proximity2_fine - 127.1141386) < 1.e-7) + +geompy.MakeVertex(0, 0, 300, "p3") +geompy.MakeVertex(-63.2456, 0, 189.737, "p4") diff --git a/test/tests.set b/test/tests.set index 1688ac2c7..7a002556f 100644 --- a/test/tests.set +++ b/test/tests.set @@ -27,5 +27,7 @@ IF(${OpenCASCADE_VERSION}.${OpenCASCADE_SP_VERSION} VERSION_GREATER "7.5.3.3") test_point_cloud_on_face.py test_CR.py test_conformity.py + test_proximity_edge_edge.py + test_proximity_face_face.py ) ENDIF()