From: jfa Date: Fri, 17 Jun 2022 15:03:59 +0000 (+0300) Subject: [bos #29468] [EDF] (2022-T1) Advanced geometry features: distance Edge-Edge & Face... X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=8077ec9b2672bafef1d0b1bb197cdf1329ec609a;p=modules%2Fshaper.git [bos #29468] [EDF] (2022-T1) Advanced geometry features: distance Edge-Edge & Face-Face --- diff --git a/src/FeaturesAPI/FeaturesAPI_Measurement.cpp b/src/FeaturesAPI/FeaturesAPI_Measurement.cpp index 6954951cb..4230b69d4 100644 --- a/src/FeaturesAPI/FeaturesAPI_Measurement.cpp +++ b/src/FeaturesAPI/FeaturesAPI_Measurement.cpp @@ -139,3 +139,28 @@ double measureAngle(const std::shared_ptr& thePart, return aValue; } + +double shapeProximity(const std::shared_ptr& thePart, + const ModelHighAPI_Selection& theFrom, + const ModelHighAPI_Selection& theTo) +{ + FeaturePtr aMeasure = thePart->addFeature(FeaturesPlugin_Measurement::ID()); + fillAttribute(FeaturesPlugin_Measurement::MEASURE_PROXIMITY(), + aMeasure->string(FeaturesPlugin_Measurement::MEASURE_KIND())); + fillAttribute(theFrom, + aMeasure->selection(FeaturesPlugin_Measurement::DISTANCE_FROM_OBJECT_ID())); + fillAttribute(theTo, + aMeasure->selection(FeaturesPlugin_Measurement::DISTANCE_TO_OBJECT_ID())); + aMeasure->execute(); + + // obtain result + AttributeDoubleArrayPtr aResult = std::dynamic_pointer_cast( + aMeasure->attribute(FeaturesPlugin_Measurement::RESULT_VALUES_ID())); + double aValue = aResult->size() ? aResult->value(0) : -1.0; + + // perform removing macro feature Measurement + thePart->removeFeature(aMeasure); + apply(); + + return aValue; +} diff --git a/src/FeaturesAPI/FeaturesAPI_Measurement.h b/src/FeaturesAPI/FeaturesAPI_Measurement.h index 937bece21..715dc12b2 100644 --- a/src/FeaturesAPI/FeaturesAPI_Measurement.h +++ b/src/FeaturesAPI/FeaturesAPI_Measurement.h @@ -62,4 +62,11 @@ double measureAngle(const std::shared_ptr& thePart, const ModelHighAPI_Selection& thePoint2, const ModelHighAPI_Selection& thePoint3); +/// \ingroup CPPHighAPI +/// \brief Calculate maximum of all distances between objects. +FEATURESAPI_EXPORT +double shapeProximity(const std::shared_ptr& thePart, + const ModelHighAPI_Selection& theFrom, + const ModelHighAPI_Selection& theTo); + #endif // FeaturesAPI_Measurement_H_ diff --git a/src/FeaturesPlugin/FeaturesPlugin_Measurement.cpp b/src/FeaturesPlugin/FeaturesPlugin_Measurement.cpp index 9f3f9f003..47d61024a 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Measurement.cpp +++ b/src/FeaturesPlugin/FeaturesPlugin_Measurement.cpp @@ -90,12 +90,15 @@ void FeaturesPlugin_Measurement::attributeChanged(const std::string& theID) std::dynamic_pointer_cast( attribute(RESULT_VALUES_ID()))->setSize(0); } - if (theID != RESULT_ID()) { + else if (theID != RESULT_ID() && + theID != RESULT_VALUES_ID()) { std::string aKind = string(MEASURE_KIND())->value(); if (aKind == MEASURE_LENGTH()) computeLength(); else if (aKind == MEASURE_DISTANCE()) computeDistance(); + else if (aKind == MEASURE_PROXIMITY()) + computeProximity(); else if (aKind == MEASURE_RADIUS()) computeRadius(); else if (aKind == MEASURE_ANGLE()) @@ -181,6 +184,41 @@ void FeaturesPlugin_Measurement::computeDistance() aValues->setValue(0, aDistance); } +void FeaturesPlugin_Measurement::computeProximity() +{ + AttributeSelectionPtr aFirstFeature = selection(DISTANCE_FROM_OBJECT_ID()); + GeomShapePtr aShape1; + if (aFirstFeature && aFirstFeature->isInitialized()) { + aShape1 = aFirstFeature->value(); + if (!aShape1 && aFirstFeature->context()) + aShape1 = aFirstFeature->context()->shape(); + } + + AttributeSelectionPtr aSecondFeature = selection(DISTANCE_TO_OBJECT_ID()); + GeomShapePtr aShape2; + if (aSecondFeature && aSecondFeature->isInitialized()) { + aShape2 = aSecondFeature->value(); + if (!aShape2 && aSecondFeature->context()) + aShape2 = aSecondFeature->context()->shape(); + } + + if (!aShape1 || !aShape2) { + string(RESULT_ID())->setValue(""); + return; + } + + double aDistance = GeomAlgoAPI_ShapeTools::shapeProximity(aShape1, aShape2); + + std::ostringstream anOutput; + anOutput << "Proximity = " << std::setprecision(10) << aDistance; + string(RESULT_ID())->setValue(anOutput.str()); + + AttributeDoubleArrayPtr aValues = + std::dynamic_pointer_cast(attribute(RESULT_VALUES_ID())); + aValues->setSize(1); + aValues->setValue(0, aDistance); +} + void FeaturesPlugin_Measurement::computeRadius() { AttributeSelectionPtr aSelectedFeature = selection(CIRCULAR_OBJECT_ID()); diff --git a/src/FeaturesPlugin/FeaturesPlugin_Measurement.h b/src/FeaturesPlugin/FeaturesPlugin_Measurement.h index 5ef1a1c08..e4fdc1d6c 100644 --- a/src/FeaturesPlugin/FeaturesPlugin_Measurement.h +++ b/src/FeaturesPlugin/FeaturesPlugin_Measurement.h @@ -74,6 +74,13 @@ public: return MY_MEASURE_ID; } + /// Attribute name for distance measure. + inline static const std::string& MEASURE_PROXIMITY() + { + static const std::string MY_MEASURE_ID("Proximity"); + return MY_MEASURE_ID; + } + /// Attribute name for radius measure. inline static const std::string& MEASURE_RADIUS() { @@ -211,6 +218,8 @@ private: void computeLength(); /// Compute minimal distance between pair of shapes void computeDistance(); + /// Compute proximity (maximum of all minimal distances between pair of shapes) + void computeProximity(); /// Compute radius of circular edge, cylindrical surface or sphere. void computeRadius(); /// Compute angle(s) between pair of edges if they are intersected diff --git a/src/FeaturesPlugin/Test/TestMeasurementDistance.py b/src/FeaturesPlugin/Test/TestMeasurementDistance.py index 4afc8d830..c29bd6126 100644 --- a/src/FeaturesPlugin/Test/TestMeasurementDistance.py +++ b/src/FeaturesPlugin/Test/TestMeasurementDistance.py @@ -87,44 +87,59 @@ TOLERANCE = 1.e-6 # reference data REF_DATA = [(model.selection("VERTEX", "PartSet/Origin"), model.selection("VERTEX", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/To_Face]"), - 122.4744871), + 122.4744871, + "NA"), (model.selection("EDGE", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/To_Face]"), model.selection("VERTEX", "Sketch_2/SketchArc_1"), - 36.94403089), + 36.94403089, + "NA"), (model.selection("EDGE", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/From_Face]"), model.selection("EDGE", "([Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/From_Face])([Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/To_Face])([Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/From_Face])([Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/To_Face])"), - 0), + 0, + 60), (model.selection("EDGE", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/From_Face]"), model.selection("EDGE", "[Extrusion_2_1/Generated_Face&Sketch_2/SketchArc_1_2][Extrusion_2_1/To_Face]"), - 16.00781059), + 16.00781059, + 176.710217655), (model.selection("EDGE", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/From_Face]"), model.selection("FACE", "Extrusion_2_1/Generated_Face&Sketch_2/SketchArc_1_2"), - 8.412291828), + 8.412291828, + 165.748725311), (model.selection("VERTEX", "Sketch_1/SketchCircle_1_2__cc"), model.selection("FACE", "Plane_1"), - 35.35533906), + 35.35533906, + "NA"), (model.selection("FACE", "Extrusion_2_2/From_Face"), model.selection("FACE", "Extrusion_2_2/To_Face"), + 100, 100), (model.selection("FACE", "Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2"), model.selection("FACE", "Extrusion_2_1/Generated_Face&Sketch_2/SketchArc_1_2"), - 0), + 0, + 165.748725311), (model.selection("FACE", "Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2"), model.selection("FACE", "Extrusion_1_1/Generated_Face&Sketch_1/SketchLine_5"), - 27.63932023), + 27.63932023, + 27.61462299114), (model.selection("SOLID", "Extrusion_1_1"), model.selection("FACE", "Extrusion_2_1/To_Face"), - 12.5), + 12.5, + "NA"), (model.selection("SOLID", "Extrusion_1_1"), model.selection("SOLID", "Extrusion_2_1"), - 0), + 0, + "NA"), (model.selection("SOLID", "Extrusion_1_1"), model.selection("SOLID", "Extrusion_2_2"), - 87.5) + 87.5, + "NA") ] for ref in REF_DATA: dist = model.measureDistance(Part_1_doc, ref[0], ref[1]) assert(math.fabs(dist - ref[2]) < TOLERANCE), "Distance {} differs from expected value {}".format(dist, ref[2]) + if ref[3] != "NA": + prox = model.shapeProximity(Part_1_doc, ref[0], ref[1]) + assert(math.fabs(prox - ref[3]) < TOLERANCE), "Proximity {} differs from expected value {}".format(prox, ref[3]) model.end() diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.cpp b/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.cpp index f95671f3e..0b1a48f4a 100644 --- a/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.cpp +++ b/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.cpp @@ -48,7 +48,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -58,6 +60,10 @@ #include +#include +#include +#include + #include #include @@ -333,8 +339,131 @@ auto getExtemaDistShape = [](const GeomShapePtr& theShape1, aDist.Perform(); return aDist; }; + +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, + const gp_Pnt& thePoint1, + const gp_Pnt& thePoint2) +{ + BRepAdaptor_Curve aCurve1(theEdge1); + BRepAdaptor_Curve aCurve2(theEdge2); + + Standard_Real aU1 = paramOnCurve(aCurve1, thePoint1, BRep_Tool::Tolerance(theEdge1)); + Standard_Real aU2 = paramOnCurve(aCurve2, thePoint2, BRep_Tool::Tolerance(theEdge2)); + + Standard_Real aValue = -1.0; + Extrema_LocateExtCC anExtr(aCurve1, aCurve2, aU1, aU2); + if (anExtr.IsDone() && aValue > Precision::Confusion()) + aValue = Sqrt(anExtr.SquareDistance()); + return aValue; +} + +static Standard_Real extremaEF(const TopoDS_Edge& theEdge, + const TopoDS_Face& theFace, + const gp_Pnt& thePonE, + const gp_Pnt& thePonF) +{ + BRepAdaptor_Curve aCurve(theEdge); + BRepAdaptor_Surface aSurf(theFace); + + Standard_Real aP = paramOnCurve(aCurve, thePonE, BRep_Tool::Tolerance(theEdge)); + Standard_Real aU, aV; + paramsOnSurf(aSurf, thePonF, BRep_Tool::Tolerance(theFace), aU, aV); + + Standard_Real aValue = -1.0; + Extrema_GenLocateExtCS anExtr(aCurve, aSurf, aP, aU, aV, Precision::PConfusion(), Precision::PConfusion()); + if (anExtr.IsDone() && aValue > Precision::Confusion()) + aValue = Sqrt(anExtr.SquareDistance()); + return aValue; +} + +static Standard_Real extremaFF(const TopoDS_Face& theFace1, + const TopoDS_Face& theFace2, + const gp_Pnt& thePoint1, + const gp_Pnt& thePoint2) +{ + BRepAdaptor_Surface aSurf1(theFace1); + BRepAdaptor_Surface aSurf2(theFace2); + + Standard_Real aU1, aV1; + paramsOnSurf(aSurf1, thePoint1, BRep_Tool::Tolerance(theFace1), aU1, aV1); + Standard_Real aU2, aV2; + paramsOnSurf(aSurf2, thePoint2, BRep_Tool::Tolerance(theFace2), aU2, aV2); + + Standard_Real aValue = -1.0; + Extrema_GenLocateExtSS anExtr(aSurf1, aSurf2, aU1, aV1, aU2, aV2, Precision::PConfusion(), Precision::PConfusion()); + if (anExtr.IsDone() && aValue > Precision::Confusion()) + aValue = Sqrt(anExtr.SquareDistance()); + return aValue; +} + +} // namespace + double GeomAlgoAPI_ShapeTools::minimalDistance(const GeomShapePtr& theShape1, const GeomShapePtr& theShape2) { @@ -355,6 +484,54 @@ double GeomAlgoAPI_ShapeTools::minimalDistance(const GeomShapePtr& theShape1, return aDist.IsDone() ? aDist.Value() : Precision::Infinite(); } +//================================================================================================== +double GeomAlgoAPI_ShapeTools::shapeProximity(const GeomShapePtr& theShape1, + const GeomShapePtr& theShape2) +{ + double aResult = Precision::Infinite(); + if (!theShape1.get() || !theShape2.get()) + return aResult; + + const TopoDS_Shape& aShape1 = theShape1->impl(); + const TopoDS_Shape& aShape2 = theShape2->impl(); + + TopAbs_ShapeEnum aType1 = aShape1.ShapeType(); + TopAbs_ShapeEnum aType2 = aShape2.ShapeType(); + + // tessellate shapes if there is no mesh exists + tessellateShape(aShape1); + tessellateShape(aShape2); + + BRepExtrema_ShapeProximity aDist (aShape1, aShape2); + aDist.Perform(); + if (aDist.IsDone()) { + aResult = aDist.Proximity(); + + // refine the result + const gp_Pnt& aPonS1 = aDist.ProximityPoint1(); + const gp_Pnt& aPonS2 = aDist.ProximityPoint2(); + + double anExtrema = -1.0; + if (aType1 == TopAbs_EDGE) + { + if (aType2 == TopAbs_EDGE) + anExtrema = extremaEE(TopoDS::Edge(aShape1), TopoDS::Edge(aShape2), aPonS1, aPonS2); + else if (aType2 == TopAbs_FACE) + anExtrema = extremaEF(TopoDS::Edge(aShape1), TopoDS::Face(aShape2), aPonS1, aPonS2); + } + else if (aType1 == TopAbs_FACE) + { + if (aType2 == TopAbs_EDGE) + anExtrema = extremaEF(TopoDS::Edge(aShape2), TopoDS::Face(aShape1), aPonS2, aPonS1); + else if (aType2 == TopAbs_FACE) + anExtrema = extremaFF(TopoDS::Face(aShape1), TopoDS::Face(aShape2), aPonS1, aPonS2); + } + if (anExtrema > 0.0) + aResult = anExtrema; + } + return aResult; +} + //================================================================================================== std::shared_ptr GeomAlgoAPI_ShapeTools::combineShapes( const std::shared_ptr theCompound, diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.h b/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.h index 3346fbe54..0c058e147 100644 --- a/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.h +++ b/src/GeomAlgoAPI/GeomAlgoAPI_ShapeTools.h @@ -88,6 +88,11 @@ public: const GeomShapePtr& theShape2, std::array & fromShape1To2); + /// \brief Calculate maximal value of all possible distances between shapes. + /// The shapes can be two edges or two faces only. + GEOMALGOAPI_EXPORT static double shapeProximity(const GeomShapePtr& theShape1, + const GeomShapePtr& theShape2); + /// \brief Combines faces with common edges to shells, or solids to compsolids. /// \param[in] theCompound compound of shapes. /// \param[in] theType type of combine. diff --git a/src/PythonAPI/model/features/__init__.py b/src/PythonAPI/model/features/__init__.py index e31635882..bf5283b3d 100644 --- a/src/PythonAPI/model/features/__init__.py +++ b/src/PythonAPI/model/features/__init__.py @@ -30,6 +30,7 @@ from FeaturesAPI import addRecover from FeaturesAPI import addFillet, addChamfer from FeaturesAPI import addFusionFaces from FeaturesAPI import measureLength, measureDistance, measureRadius, measureAngle +from FeaturesAPI import shapeProximity from FeaturesAPI import getPointCoordinates, getGeometryCalculation, getBoundingBox from FeaturesAPI import getNormal from FeaturesAPI import addRemoveResults