--- /dev/null
+// Copyright (C) 2017-20xx CEA/DEN, EDF R&D
+
+// File: GeomAlgoAPI_Circ2dBuilder.cpp
+// Created: 3 April 2017
+// Author: Artem ZHIDKOV
+
+#include <GeomAlgoAPI_Circ2dBuilder.h>
+#include <GeomAPI_Ax3.h>
+#include <GeomAPI_Circ2d.h>
+#include <GeomAPI_Pnt2d.h>
+#include <GeomAPI_Dir2d.h>
+#include <GeomAPI_Shape.h>
+
+#include <BRep_Tool.hxx>
+#include <ElCLib.hxx>
+#include <GccAna_Circ2d2TanRad.hxx>
+#include <GccAna_Circ2d3Tan.hxx>
+#include <GccAna_Circ2dTanCen.hxx>
+#include <GccEnt.hxx>
+#include <GccEnt_QualifiedCirc.hxx>
+#include <GccEnt_QualifiedLin.hxx>
+#include <Geom2dAdaptor_Curve.hxx>
+#include <Geom_Plane.hxx>
+#include <TopoDS.hxx>
+#include <TopoDS_Edge.hxx>
+
+#include <cmath>
+
+typedef std::shared_ptr<gp_Circ2d> Circ2dPtr;
+typedef std::shared_ptr<Geom2dAdaptor_Curve> CurveAdaptorPtr;
+typedef std::vector< std::shared_ptr<GccEnt_QualifiedCirc> > VectorOfGccCirc;
+typedef std::vector< std::shared_ptr<GccEnt_QualifiedLin> > VectorOfGccLine;
+
+
+// Provide different mechanisms to create circle:
+// * by passing points
+// * by tangent edges
+// * with specified radius
+// * etc.
+class CircleBuilder
+{
+public:
+ CircleBuilder(const std::shared_ptr<GeomAPI_Ax3>& theBasePlane)
+ : myPlane(new Geom_Plane(theBasePlane->impl<gp_Ax3>())),
+ myRadius(0.0)
+ {}
+
+ void setRadius(const double theRadius)
+ { myRadius = theRadius; }
+
+ void setCenter(const std::shared_ptr<GeomAPI_Pnt2d>& theCenter)
+ { myCenter = theCenter; }
+
+ void setTangentCurves(const std::vector< std::shared_ptr<GeomAPI_Shape> >& theEdges)
+ {
+ std::vector< std::shared_ptr<GeomAPI_Shape> >::const_iterator anEdgeIt;
+ for (anEdgeIt = theEdges.begin(); anEdgeIt != theEdges.end(); ++anEdgeIt) {
+ const TopoDS_Edge& anEdge = TopoDS::Edge((*anEdgeIt)->impl<TopoDS_Shape>());
+
+ double aFirst, aLast;
+ TopLoc_Location aLoc;
+ Handle(Geom2d_Curve) aCurve = BRep_Tool::CurveOnSurface(anEdge, myPlane, aLoc, aFirst, aLast);
+ CurveAdaptorPtr aCurveAdaptor(new Geom2dAdaptor_Curve(aCurve, aFirst, aLast));
+
+ // sort curves (circles should become first)
+ if (aCurveAdaptor->GetType() == GeomAbs_Circle)
+ myTangentShapes.insert(myTangentShapes.begin(), aCurveAdaptor);
+ else
+ myTangentShapes.push_back(aCurveAdaptor);
+ }
+ }
+
+ void setPassingPoints(const std::vector< std::shared_ptr<GeomAPI_Pnt2d> >& thePoints)
+ {
+ std::vector< std::shared_ptr<GeomAPI_Pnt2d> >::const_iterator aPIt;
+ for (aPIt = thePoints.begin(); aPIt != thePoints.end(); ++aPIt)
+ myPassingPoints.push_back((*aPIt)->impl<gp_Pnt2d>());
+ }
+
+ void setClosestPoint(const std::shared_ptr<GeomAPI_Pnt2d>& thePoint)
+ { myClosestPoint = thePoint; }
+
+
+ Circ2dPtr circle()
+ {
+ Circ2dPtr aResult;
+ if (myCenter) {
+ if (myPassingPoints.size() == 1)
+ aResult = circleByCenterAndPassingPoint();
+ else if (myTangentShapes.size() == 1)
+ aResult = circleByCenterAndTangent();
+ else if (myRadius > 0.0)
+ aResult = circleByCenterAndRadius();
+ } else if (myRadius > 0.0) {
+ if (myTangentShapes.size() == 2)
+ aResult = circleByRadiusAndTwoTangentCurves();
+ } else {
+ switch (myPassingPoints.size()) {
+ case 0:
+ aResult = circleByThreeTangentCurves();
+ break;
+ case 1:
+ aResult = circleByPointAndTwoTangentCurves();
+ break;
+ case 2:
+ aResult = circleByTwoPointsAndTangentCurve();
+ break;
+ case 3:
+ aResult = circleByThreePassingPoints();
+ break;
+ default:
+ break;
+ }
+ }
+ return aResult;
+ }
+
+private:
+ Circ2dPtr circleByCenterAndRadius()
+ {
+ const gp_Pnt2d& aCenter = myCenter->impl<gp_Pnt2d>();
+ return Circ2dPtr(new gp_Circ2d(gp_Ax2d(aCenter, gp::DX2d()), myRadius));
+ }
+
+ Circ2dPtr circleByCenterAndPassingPoint()
+ {
+ const gp_Pnt2d& aCenter = myCenter->impl<gp_Pnt2d>();
+ GccAna_Circ2dTanCen aBuilder(myPassingPoints[0], aCenter);
+ if (aBuilder.NbSolutions() > 0)
+ return Circ2dPtr(new gp_Circ2d(aBuilder.ThisSolution(1)));
+ return Circ2dPtr();
+ }
+
+ Circ2dPtr circleByCenterAndTangent()
+ {
+ const gp_Pnt2d& aCenter = myCenter->impl<gp_Pnt2d>();
+ CurveAdaptorPtr aCurve = myTangentShapes[0];
+
+ std::shared_ptr<GccAna_Circ2dTanCen> aCircleBuilder;
+ if (aCurve->GetType() == GeomAbs_Line) {
+ aCircleBuilder = std::shared_ptr<GccAna_Circ2dTanCen>(
+ new GccAna_Circ2dTanCen(aCurve->Line(), aCenter));
+ } else if (aCurve->GetType() == GeomAbs_Circle) {
+ aCircleBuilder = std::shared_ptr<GccAna_Circ2dTanCen>(new GccAna_Circ2dTanCen(
+ GccEnt::Unqualified(aCurve->Circle()), aCenter, Precision::Confusion()));
+ }
+
+ return getProperCircle(aCircleBuilder);
+ }
+
+ Circ2dPtr getProperCircle(const std::shared_ptr<GccAna_Circ2dTanCen>& theBuilder) const
+ {
+ if (!theBuilder)
+ return Circ2dPtr();
+
+ CurveAdaptorPtr aCurve = myTangentShapes[0];
+
+ int aNbSol = theBuilder->NbSolutions();
+ if (aNbSol == 0)
+ return Circ2dPtr();
+
+ int anAppropriateSolution = 1;
+ double aMinDist = Precision::Infinite();
+
+ double aParSol, aPonTgCurve;
+ gp_Pnt2d aTgPnt;
+ for (int i = 1; i <= aNbSol && aCurve; ++i) {
+ theBuilder->Tangency1(i, aParSol, aPonTgCurve, aTgPnt);
+ if (isParamOnCurve(aPonTgCurve, aCurve)) {
+ double aDist = distanceToClosestPoint(theBuilder->ThisSolution(i));
+ if (aDist < aMinDist) {
+ anAppropriateSolution = i;
+ aMinDist = aDist;
+ }
+ }
+ }
+
+ return Circ2dPtr(new gp_Circ2d(theBuilder->ThisSolution(anAppropriateSolution)));
+ }
+
+ double distanceToClosestPoint(const gp_Circ2d& theCirc) const
+ {
+ if (myClosestPoint) {
+ double aDist = myClosestPoint->impl<gp_Pnt2d>().Distance(theCirc.Location());
+ return fabs(aDist - theCirc.Radius());
+ }
+ return 0.0;
+ }
+
+
+ Circ2dPtr circleByThreeTangentCurves()
+ {
+ VectorOfGccCirc aTgCirc;
+ VectorOfGccLine aTgLine;
+ convertTangentCurvesToGccEnt(aTgCirc, aTgLine);
+
+ if (aTgCirc.size() + aTgLine.size() != 3)
+ return 0;
+
+ std::shared_ptr<GccAna_Circ2d3Tan> aCircleBuilder;
+ switch (aTgLine.size()) {
+ case 0:
+ aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
+ *aTgCirc[0], *aTgCirc[1], *aTgCirc[2], Precision::Confusion()));
+ break;
+ case 1:
+ aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
+ *aTgCirc[0], *aTgCirc[1], *aTgLine[0], Precision::Confusion()));
+ break;
+ case 2:
+ aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
+ *aTgCirc[0], *aTgLine[0], *aTgLine[1], Precision::Confusion()));
+ break;
+ case 3:
+ aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
+ *aTgLine[0], *aTgLine[1], *aTgLine[0], Precision::Confusion()));
+ break;
+ default:
+ break;
+ }
+
+ return getProperCircle(aCircleBuilder);
+ }
+
+ Circ2dPtr circleByPointAndTwoTangentCurves()
+ {
+ const gp_Pnt2d& aPoint = myPassingPoints[0];
+ CurveAdaptorPtr aCurve1 = myTangentShapes[0];
+ CurveAdaptorPtr aCurve2 = myTangentShapes[1];
+ if (!aCurve1 || !aCurve2)
+ return 0;
+
+ std::shared_ptr<GccAna_Circ2d3Tan> aCircleBuilder;
+ if (aCurve1->GetType() == GeomAbs_Line) {
+ if (aCurve2->GetType() == GeomAbs_Line) {
+ aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(
+ new GccAna_Circ2d3Tan(GccEnt::Unqualified(aCurve1->Line()),
+ GccEnt::Unqualified(aCurve2->Line()),
+ aPoint, Precision::Confusion()));
+ } else if (aCurve2->GetType() == GeomAbs_Circle) {
+ aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(
+ new GccAna_Circ2d3Tan(GccEnt::Unqualified(aCurve2->Circle()),
+ GccEnt::Unqualified(aCurve1->Line()),
+ aPoint, Precision::Confusion()));
+ }
+ } else if (aCurve1->GetType() == GeomAbs_Circle) {
+ if (aCurve2->GetType() == GeomAbs_Line) {
+ aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(
+ new GccAna_Circ2d3Tan(GccEnt::Unqualified(aCurve1->Circle()),
+ GccEnt::Unqualified(aCurve2->Line()),
+ aPoint, Precision::Confusion()));
+ } else if (aCurve2->GetType() == GeomAbs_Circle) {
+ aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(
+ new GccAna_Circ2d3Tan(GccEnt::Unqualified(aCurve2->Circle()),
+ GccEnt::Unqualified(aCurve1->Circle()),
+ aPoint, Precision::Confusion()));
+ }
+ }
+
+ return getProperCircle(aCircleBuilder);
+ }
+
+ Circ2dPtr circleByTwoPointsAndTangentCurve()
+ {
+ const gp_Pnt2d& aPoint1 = myPassingPoints[0];
+ const gp_Pnt2d& aPoint2 = myPassingPoints[1];
+ CurveAdaptorPtr aCurve = myTangentShapes[0];
+ if (!aCurve)
+ return 0;
+
+ std::shared_ptr<GccAna_Circ2d3Tan> aCircleBuilder;
+ if (aCurve->GetType() == GeomAbs_Line) {
+ aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
+ GccEnt::Unqualified(aCurve->Line()), aPoint1, aPoint2, Precision::Confusion()));
+ } else if (aCurve->GetType() == GeomAbs_Circle) {
+ aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
+ GccEnt::Unqualified(aCurve->Circle()), aPoint1, aPoint2, Precision::Confusion()));
+ }
+
+ return getProperCircle(aCircleBuilder);
+ }
+
+ Circ2dPtr circleByThreePassingPoints()
+ {
+ GccAna_Circ2d3Tan aCircleBuilder(myPassingPoints[0],
+ myPassingPoints[1],
+ myPassingPoints[2],
+ Precision::Confusion());
+ if (aCircleBuilder.NbSolutions() > 0)
+ return Circ2dPtr(new gp_Circ2d(aCircleBuilder.ThisSolution(1)));
+ return Circ2dPtr();
+ }
+
+ Circ2dPtr getProperCircle(const std::shared_ptr<GccAna_Circ2d3Tan>& theBuilder) const
+ {
+ if (!theBuilder)
+ return Circ2dPtr();
+
+ int aNbSol = theBuilder->NbSolutions();
+ if (aNbSol == 0)
+ return Circ2dPtr();
+
+ int anAppropriateSolution = 1;
+ double aMinDist = Precision::Infinite();
+
+ double aParSol, aPonTgCurve;
+ gp_Pnt2d aTgPnt;
+ for (int i = 1; i <= aNbSol; ++i) {
+ bool isApplicable = false;
+ if (myTangentShapes.size() >= 1) {
+ theBuilder->Tangency1(i, aParSol, aPonTgCurve, aTgPnt);
+ isApplicable = isParamOnCurve(aPonTgCurve, myTangentShapes[0]);
+ }
+ if (myTangentShapes.size() >= 2 && isApplicable) {
+ theBuilder->Tangency2(i, aParSol, aPonTgCurve, aTgPnt);
+ isApplicable = isParamOnCurve(aPonTgCurve, myTangentShapes[1]);
+ }
+ if (myTangentShapes.size() >= 3 && isApplicable) {
+ theBuilder->Tangency3(i, aParSol, aPonTgCurve, aTgPnt);
+ isApplicable = isParamOnCurve(aPonTgCurve, myTangentShapes[2]);
+ }
+
+ if (isApplicable) {
+ double aDist = distanceToClosestPoint(theBuilder->ThisSolution(i));
+ if (aDist < aMinDist) {
+ anAppropriateSolution = i;
+ aMinDist = aDist;
+ }
+ }
+ }
+
+ return Circ2dPtr(new gp_Circ2d(theBuilder->ThisSolution(anAppropriateSolution)));
+ }
+
+
+ Circ2dPtr circleByRadiusAndTwoTangentCurves()
+ {
+ VectorOfGccCirc aTgCirc;
+ VectorOfGccLine aTgLine;
+ convertTangentCurvesToGccEnt(aTgCirc, aTgLine);
+
+ if (aTgCirc.size() + aTgLine.size() != 2)
+ return 0;
+
+ std::shared_ptr<GccAna_Circ2d2TanRad> aCircleBuilder;
+ switch (aTgLine.size()) {
+ case 0:
+ aCircleBuilder = std::shared_ptr<GccAna_Circ2d2TanRad>(new GccAna_Circ2d2TanRad(
+ *aTgCirc[0], *aTgCirc[1], myRadius, Precision::Confusion()));
+ break;
+ case 1:
+ aCircleBuilder = std::shared_ptr<GccAna_Circ2d2TanRad>(new GccAna_Circ2d2TanRad(
+ *aTgCirc[0], *aTgLine[0], myRadius, Precision::Confusion()));
+ break;
+ case 2:
+ aCircleBuilder = std::shared_ptr<GccAna_Circ2d2TanRad>(new GccAna_Circ2d2TanRad(
+ *aTgLine[0], *aTgLine[1], myRadius, Precision::Confusion()));
+ break;
+ default:
+ break;
+ }
+
+ return getProperCircle(aCircleBuilder);
+ }
+
+ Circ2dPtr getProperCircle(const std::shared_ptr<GccAna_Circ2d2TanRad>& theBuilder) const
+ {
+ if (!theBuilder)
+ return Circ2dPtr();
+
+ int aNbSol = theBuilder->NbSolutions();
+ if (aNbSol == 0)
+ return Circ2dPtr();
+
+ int anAppropriateSolution = 1;
+ double aMinDist = Precision::Infinite();
+
+ double aParSol, aPonTgCurve;
+ gp_Pnt2d aTgPnt;
+ for (int i = 1; i <= aNbSol; ++i) {
+ bool isApplicable = false;
+ if (myTangentShapes.size() >= 1) {
+ theBuilder->Tangency1(i, aParSol, aPonTgCurve, aTgPnt);
+ isApplicable = isParamInCurve(aPonTgCurve, myTangentShapes[0]);
+ }
+ if (myTangentShapes.size() >= 2 && isApplicable) {
+ theBuilder->Tangency2(i, aParSol, aPonTgCurve, aTgPnt);
+ isApplicable = isParamInCurve(aPonTgCurve, myTangentShapes[1]);
+ }
+
+ if (isApplicable) {
+ double aDist = distanceToClosestPoint(theBuilder->ThisSolution(i));
+ if (aDist < aMinDist) {
+ anAppropriateSolution = i;
+ aMinDist = aDist;
+ }
+ }
+ }
+
+ return Circ2dPtr(new gp_Circ2d(theBuilder->ThisSolution(anAppropriateSolution)));
+ }
+
+
+ void convertTangentCurvesToGccEnt(VectorOfGccCirc& theTangentCircles,
+ VectorOfGccLine& theTangentLines)
+ {
+ theTangentCircles.reserve(3);
+ theTangentLines.reserve(3);
+
+ std::vector<CurveAdaptorPtr>::iterator anIt = myTangentShapes.begin();
+ for (; anIt != myTangentShapes.end(); ++anIt) {
+ switch ((*anIt)->GetType()) {
+ case GeomAbs_Line:
+ theTangentLines.push_back(
+ std::shared_ptr<GccEnt_QualifiedLin>(
+ new GccEnt_QualifiedLin((*anIt)->Line(), GccEnt_unqualified))
+ );
+ break;
+ case GeomAbs_Circle:
+ theTangentCircles.push_back(
+ std::shared_ptr<GccEnt_QualifiedCirc>(
+ new GccEnt_QualifiedCirc((*anIt)->Circle(), GccEnt_unqualified))
+ );
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+
+ // boundary parameters of curve are NOT applied
+ static bool isParamInCurve(double& theParameter, const CurveAdaptorPtr& theCurve)
+ {
+ if (theCurve->Curve()->IsPeriodic()) {
+ theParameter = ElCLib::InPeriod(theParameter,
+ theCurve->FirstParameter(),
+ theCurve->FirstParameter() + theCurve->Period());
+ }
+ return theParameter > theCurve->FirstParameter() &&
+ theParameter < theCurve->LastParameter();
+ }
+
+ // boundary parameters of curve are applied too
+ static bool isParamOnCurve(double& theParameter, const CurveAdaptorPtr& theCurve)
+ {
+ if (theCurve->IsPeriodic()) {
+ theParameter = ElCLib::InPeriod(theParameter,
+ theCurve->FirstParameter(),
+ theCurve->FirstParameter() + theCurve->Period());
+ }
+ return theParameter >= theCurve->FirstParameter() &&
+ theParameter <= theCurve->LastParameter();
+ }
+
+private:
+ Handle(Geom_Plane) myPlane;
+ std::shared_ptr<GeomAPI_Pnt2d> myCenter;
+ std::vector<gp_Pnt2d> myPassingPoints;
+ std::vector<CurveAdaptorPtr> myTangentShapes;
+ double myRadius;
+ std::shared_ptr<GeomAPI_Pnt2d> myClosestPoint;
+};
+
+
+
+
+
+GeomAlgoAPI_Circ2dBuilder::GeomAlgoAPI_Circ2dBuilder(const std::shared_ptr<GeomAPI_Ax3>& thePlane)
+ : myPlane(thePlane),
+ myRadius(0.)
+{
+}
+
+void GeomAlgoAPI_Circ2dBuilder::setRadius(const double theRadius)
+{
+ myRadius = theRadius;
+}
+
+void GeomAlgoAPI_Circ2dBuilder::setCenter(const std::shared_ptr<GeomAPI_Pnt2d>& theCenter)
+{
+ myCenter = theCenter;
+}
+
+void GeomAlgoAPI_Circ2dBuilder::addTangentCurve(const std::shared_ptr<GeomAPI_Shape>& theEdge)
+{
+ if (theEdge->isEdge())
+ myTangentShapes.push_back(theEdge);
+}
+
+void GeomAlgoAPI_Circ2dBuilder::addPassingPoint(const std::shared_ptr<GeomAPI_Pnt2d>& thePoint)
+{
+ myPassingPoints.push_back(thePoint);
+}
+
+void GeomAlgoAPI_Circ2dBuilder::setClosestPoint(const std::shared_ptr<GeomAPI_Pnt2d>& thePoint)
+{
+ myClosestPoint = thePoint;
+}
+
+std::shared_ptr<GeomAPI_Circ2d> GeomAlgoAPI_Circ2dBuilder::circle(
+ const std::shared_ptr<GeomAPI_Pnt2d>& theFirstPoint,
+ const std::shared_ptr<GeomAPI_Pnt2d>& theSecondPoint,
+ const std::shared_ptr<GeomAPI_Pnt2d>& theThirdPoint)
+{
+ std::shared_ptr<GeomAPI_Ax3> aPlane(new GeomAPI_Ax3);
+
+ GeomAlgoAPI_Circ2dBuilder aBuilder(aPlane);
+ aBuilder.addPassingPoint(theFirstPoint);
+ aBuilder.addPassingPoint(theSecondPoint);
+ aBuilder.addPassingPoint(theThirdPoint);
+ return aBuilder.circle();
+}
+
+std::shared_ptr<GeomAPI_Circ2d> GeomAlgoAPI_Circ2dBuilder::circle()
+{
+ CircleBuilder aCircleBuilder(myPlane);
+ aCircleBuilder.setCenter(myCenter);
+ aCircleBuilder.setTangentCurves(myTangentShapes);
+ aCircleBuilder.setPassingPoints(myPassingPoints);
+ aCircleBuilder.setClosestPoint(myClosestPoint);
+ aCircleBuilder.setRadius(myRadius);
+ Circ2dPtr aCirc2d = aCircleBuilder.circle();
+
+ std::shared_ptr<GeomAPI_Circ2d> aCircle;
+ if (aCirc2d) {
+ const gp_Pnt2d& aCenter = aCirc2d->Location();
+ const gp_Dir2d& aXAxis = aCirc2d->XAxis().Direction();
+
+ std::shared_ptr<GeomAPI_Pnt2d> aCircleCenter(new GeomAPI_Pnt2d(aCenter.X(), aCenter.Y()));
+ std::shared_ptr<GeomAPI_Dir2d> aCircleDir(new GeomAPI_Dir2d(aXAxis.X(), aXAxis.Y()));
+
+ aCircle = std::shared_ptr<GeomAPI_Circ2d>(
+ new GeomAPI_Circ2d(aCircleCenter, aCircleDir, aCirc2d->Radius()));
+ }
+ return aCircle;
+}