From 0a88f0478c0d467fd412f5337775ee7353665537 Mon Sep 17 00:00:00 2001 From: azv Date: Thu, 23 Mar 2017 10:25:11 +0300 Subject: [PATCH] Issue #2024: Redesign of circle and arc of circle 1. Add possibility to select features while constructing circle by three points 2. Redesign of SketchPlugin_MacroCircle: move all geometric constructions to GeomAPI_Circ2d. --- src/GeomAPI/GeomAPI_Circ2d.cpp | 334 +++++++++++++++++- src/GeomAPI/GeomAPI_Circ2d.h | 16 + src/SketchPlugin/SketchPlugin_MacroCircle.cpp | 119 ++++++- src/SketchPlugin/SketchPlugin_MacroCircle.h | 27 ++ 4 files changed, 484 insertions(+), 12 deletions(-) diff --git a/src/GeomAPI/GeomAPI_Circ2d.cpp b/src/GeomAPI/GeomAPI_Circ2d.cpp index 5edbab178..f4533c144 100644 --- a/src/GeomAPI/GeomAPI_Circ2d.cpp +++ b/src/GeomAPI/GeomAPI_Circ2d.cpp @@ -5,22 +5,329 @@ // Author: Artem ZHIDKOV #include +#include #include #include +#include +#include #include #include +#include #include #include +#include +#include +#include +#include +#include #include #include #include +#include +#include #include +#include +#include -#include +#include #define MY_CIRC2D implPtr() +typedef std::shared_ptr CurveAdaptorPtr; + +class CircleBuilder +{ +public: + CircleBuilder(const std::shared_ptr& theBasePlane) + : myPlane(new Geom_Plane(theBasePlane->impl())) + {} + + void addCenter(const std::shared_ptr& theCenter) + { myCenter = theCenter; } + + void addPassingEntity(const std::shared_ptr& theEntity) + { + std::shared_ptr aPoint = std::dynamic_pointer_cast(theEntity); + if (aPoint) + addPassingPoint(aPoint); + else { + std::shared_ptr aShape = std::dynamic_pointer_cast(theEntity); + if (aShape) + addTangentCurve(aShape); + } + } + + void addTangentCurve(const std::shared_ptr& theEdge) + { + if (!theEdge->isEdge()) + return; + + const TopoDS_Edge& anEdge = TopoDS::Edge(theEdge->impl()); + + double aFirst, aLast; + TopLoc_Location aLoc; + CurveAdaptorPtr aCurve(new Geom2dAdaptor_Curve( + BRep_Tool::CurveOnSurface(anEdge, myPlane, aLoc, aFirst, aLast))); + + myTangentShapes.push_back(aCurve); + } + + void addPassingPoint(const std::shared_ptr& thePoint) + { + myPassingPoints.push_back(thePoint->impl()); + } + + gp_Circ2d* circle() + { + gp_Circ2d* aResult = 0; + if (myCenter) { + if (myPassingPoints.size() == 1) + aResult = circleByCenterAndPassingPoint(); + else if (myTangentShapes.size() == 1) + aResult = circleByCenterAndTangent(); + } 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: + gp_Circ2d* circleByCenterAndPassingPoint() + { + const gp_Pnt2d& aCenter = myCenter->impl(); + GccAna_Circ2dTanCen aBuilder(myPassingPoints[0], aCenter); + if (aBuilder.NbSolutions() > 0) + return new gp_Circ2d(aBuilder.ThisSolution(1)); + return 0; + } + + gp_Circ2d* circleByCenterAndTangent() + { + const gp_Pnt2d& aCenter = myCenter->impl(); + CurveAdaptorPtr aCurve = myTangentShapes[0]; + + std::shared_ptr aCircleBuilder; + if (aCurve->GetType() == GeomAbs_Line) { + aCircleBuilder = std::shared_ptr( + new GccAna_Circ2dTanCen(aCurve->Line(), aCenter)); + } else if (aCurve->GetType() == GeomAbs_Circle) { + aCircleBuilder = std::shared_ptr(new GccAna_Circ2dTanCen( + GccEnt::Unqualified(aCurve->Circle()), aCenter, Precision::Confusion())); + } + + return getProperCircle(aCircleBuilder); + } + + gp_Circ2d* getProperCircle(const std::shared_ptr& theBuilder) const + { + if (!theBuilder) + return 0; + + CurveAdaptorPtr aCurve = myTangentShapes[0]; + + gp_Circ2d* aResult = 0; + int aNbSol = theBuilder->NbSolutions(); + double aParSol, aPonTgCurve; + gp_Pnt2d aTgPnt; + for (int i = 1; i <= aNbSol && aCurve; ++i) { + theBuilder->Tangency1(i, aParSol, aPonTgCurve, aTgPnt); + if (aPonTgCurve >= aCurve->FirstParameter() && aPonTgCurve <= aCurve->LastParameter()) { + aResult = new gp_Circ2d(theBuilder->ThisSolution(i)); + break; + } + } + // unable to build circle passing through the tangent curve, + // so get a circle passing any tangent point + if (!aResult && aNbSol > 0) + aResult = new gp_Circ2d(theBuilder->ThisSolution(1)); + return aResult; + } + + + gp_Circ2d* circleByThreeTangentCurves() + { + std::shared_ptr aTgCirc[3]; + std::shared_ptr aTgLine[3]; + int aNbTgCirc = 0; + int aNbTgLine = 0; + + std::vector::iterator anIt = myTangentShapes.begin(); + for (; anIt != myTangentShapes.end(); ++anIt) { + switch ((*anIt)->GetType()) { + case GeomAbs_Line: + aTgLine[aNbTgLine++] = std::shared_ptr( + new GccEnt_QualifiedLin((*anIt)->Line(), GccEnt_unqualified)); + break; + case GeomAbs_Circle: + aTgCirc[aNbTgCirc++] = std::shared_ptr( + new GccEnt_QualifiedCirc((*anIt)->Circle(), GccEnt_unqualified)); + break; + default: + break; + } + } + if (aNbTgCirc + aNbTgLine != 3) + return 0; + + std::shared_ptr aCircleBuilder; + switch (aNbTgLine) { + case 0: + aCircleBuilder = std::shared_ptr(new GccAna_Circ2d3Tan( + *aTgCirc[0], *aTgCirc[1], *aTgCirc[2], Precision::Confusion())); + break; + case 1: + aCircleBuilder = std::shared_ptr(new GccAna_Circ2d3Tan( + *aTgCirc[0], *aTgCirc[1], *aTgLine[0], Precision::Confusion())); + break; + case 2: + aCircleBuilder = std::shared_ptr(new GccAna_Circ2d3Tan( + *aTgCirc[0], *aTgLine[0], *aTgLine[1], Precision::Confusion())); + break; + case 3: + aCircleBuilder = std::shared_ptr(new GccAna_Circ2d3Tan( + *aTgLine[0], *aTgLine[1], *aTgLine[0], Precision::Confusion())); + break; + default: + break; + } + + return getProperCircle(aCircleBuilder); + } + + gp_Circ2d* circleByPointAndTwoTangentCurves() + { + const gp_Pnt2d& aPoint = myPassingPoints[0]; + CurveAdaptorPtr aCurve1 = myTangentShapes[0]; + CurveAdaptorPtr aCurve2 = myTangentShapes[1]; + if (!aCurve1 || !aCurve2) + return 0; + + std::shared_ptr aCircleBuilder; + if (aCurve1->GetType() == GeomAbs_Line) { + if (aCurve2->GetType() == GeomAbs_Line) { + aCircleBuilder = std::shared_ptr( + new GccAna_Circ2d3Tan(GccEnt::Unqualified(aCurve1->Line()), + GccEnt::Unqualified(aCurve2->Line()), + aPoint, Precision::Confusion())); + } else if (aCurve2->GetType() == GeomAbs_Circle) { + aCircleBuilder = std::shared_ptr( + new GccAna_Circ2d3Tan(GccEnt::Unqualified(aCurve2->Circle()), + GccEnt::Unqualified(aCurve1->Line()), + aPoint, Precision::Confusion())); + } + } else if (aCurve2->GetType() == GeomAbs_Circle) { + if (aCurve2->GetType() == GeomAbs_Line) { + aCircleBuilder = std::shared_ptr( + new GccAna_Circ2d3Tan(GccEnt::Unqualified(aCurve1->Circle()), + GccEnt::Unqualified(aCurve2->Line()), + aPoint, Precision::Confusion())); + } else if (aCurve2->GetType() == GeomAbs_Circle) { + aCircleBuilder = std::shared_ptr( + new GccAna_Circ2d3Tan(GccEnt::Unqualified(aCurve2->Circle()), + GccEnt::Unqualified(aCurve1->Circle()), + aPoint, Precision::Confusion())); + } + } + + return getProperCircle(aCircleBuilder); + } + + gp_Circ2d* circleByTwoPointsAndTangentCurve() + { + const gp_Pnt2d& aPoint1 = myPassingPoints[0]; + const gp_Pnt2d& aPoint2 = myPassingPoints[1]; + CurveAdaptorPtr aCurve = myTangentShapes[0]; + if (!aCurve) + return 0; + + std::shared_ptr aCircleBuilder; + if (aCurve->GetType() == GeomAbs_Line) { + aCircleBuilder = std::shared_ptr(new GccAna_Circ2d3Tan( + GccEnt::Unqualified(aCurve->Line()), aPoint1, aPoint2, Precision::Confusion())); + } else if (aCurve->GetType() == GeomAbs_Circle) { + aCircleBuilder = std::shared_ptr(new GccAna_Circ2d3Tan( + GccEnt::Unqualified(aCurve->Circle()), aPoint1, aPoint2, Precision::Confusion())); + } + + return getProperCircle(aCircleBuilder); + } + + gp_Circ2d* circleByThreePassingPoints() + { + GccAna_Circ2d3Tan aCircleBuilder(myPassingPoints[0], + myPassingPoints[1], + myPassingPoints[2], + Precision::Confusion()); + if (aCircleBuilder.NbSolutions() > 0) + return new gp_Circ2d(aCircleBuilder.ThisSolution(1)); + return 0; + } + + + gp_Circ2d* getProperCircle(const std::shared_ptr& theBuilder) const + { + if (!theBuilder) + return 0; + + gp_Circ2d* aResult = 0; + int aNbSol = theBuilder->NbSolutions(); + 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 = aPonTgCurve >= myTangentShapes[0]->FirstParameter() && + aPonTgCurve <= myTangentShapes[0]->LastParameter(); + } + if (myTangentShapes.size() >= 2 && isApplicable) { + theBuilder->Tangency2(i, aParSol, aPonTgCurve, aTgPnt); + isApplicable = aPonTgCurve >= myTangentShapes[1]->FirstParameter() && + aPonTgCurve <= myTangentShapes[1]->LastParameter(); + } + if (myTangentShapes.size() >= 3 && isApplicable) { + theBuilder->Tangency3(i, aParSol, aPonTgCurve, aTgPnt); + isApplicable = aPonTgCurve >= myTangentShapes[2]->FirstParameter() && + aPonTgCurve <= myTangentShapes[2]->LastParameter(); + } + + if (isApplicable) { + aResult = new gp_Circ2d(theBuilder->ThisSolution(i)); + break; + } + } + // unable to build circle passing through the tangent curve => get any tangent point + if (!aResult && aNbSol > 0) + aResult = new gp_Circ2d(theBuilder->ThisSolution(1)); + return aResult; + } + +private: + Handle(Geom_Plane) myPlane; + std::shared_ptr myCenter; + std::vector myPassingPoints; + std::vector myTangentShapes; +}; + +typedef std::shared_ptr CircleBuilderPtr; + + static gp_Circ2d* newCirc2d(const double theCenterX, const double theCenterY, const gp_Dir2d theDir, const double theRadius) { @@ -87,6 +394,8 @@ static gp_Circ2d* newCirc2d(const std::shared_ptr& theFirstPoint, return newCirc2d(aCenter.X(), aCenter.Y(), aDir, aRadius); } + + GeomAPI_Circ2d::GeomAPI_Circ2d(const std::shared_ptr& theCenter, const std::shared_ptr& theCirclePoint) : GeomAPI_Interface( @@ -108,6 +417,29 @@ GeomAPI_Circ2d::GeomAPI_Circ2d(const std::shared_ptr& theFirstPoi { } +GeomAPI_Circ2d::GeomAPI_Circ2d(const std::shared_ptr& theCenter, + const std::shared_ptr& theTangent, + const std::shared_ptr& thePlane) +{ + CircleBuilderPtr aBuilder(new CircleBuilder(thePlane)); + aBuilder->addCenter(theCenter); + aBuilder->addTangentCurve(theTangent); + setImpl(aBuilder->circle()); +} + +GeomAPI_Circ2d::GeomAPI_Circ2d(const std::shared_ptr& theEntity1, + const std::shared_ptr& theEntity2, + const std::shared_ptr& theEntity3, + const std::shared_ptr& thePlane) +{ + CircleBuilderPtr aBuilder(new CircleBuilder(thePlane)); + aBuilder->addPassingEntity(theEntity1); + aBuilder->addPassingEntity(theEntity2); + aBuilder->addPassingEntity(theEntity3); + setImpl(aBuilder->circle()); +} + + const std::shared_ptr GeomAPI_Circ2d::project( const std::shared_ptr& thePoint) const { diff --git a/src/GeomAPI/GeomAPI_Circ2d.h b/src/GeomAPI/GeomAPI_Circ2d.h index df362c3e8..0777ad354 100644 --- a/src/GeomAPI/GeomAPI_Circ2d.h +++ b/src/GeomAPI/GeomAPI_Circ2d.h @@ -10,8 +10,10 @@ #include #include +class GeomAPI_Ax3; class GeomAPI_Pnt2d; class GeomAPI_Dir2d; +class GeomAPI_Shape; /**\class GeomAPI_Circ2d * \ingroup DataModel @@ -37,6 +39,20 @@ class GeomAPI_Circ2d : public GeomAPI_Interface const std::shared_ptr& theSecondPoint, const std::shared_ptr& theThirdPoint); + /// Creation of a circle defined by center and a tangent curve on the given plane + GEOMAPI_EXPORT + GeomAPI_Circ2d(const std::shared_ptr& theCenter, + const std::shared_ptr& theTangent, + const std::shared_ptr& thePlane); + + /// Creation of a circle passing through or tangent to given entities. + /// Supported items are GeomAPI_Pnt2d or GeomAPI_Shape + GEOMAPI_EXPORT + GeomAPI_Circ2d(const std::shared_ptr& theEntity1, + const std::shared_ptr& theEntity2, + const std::shared_ptr& theEntity3, + const std::shared_ptr& thePlane); + /// Return center of the circle GEOMAPI_EXPORT const std::shared_ptr center() const; diff --git a/src/SketchPlugin/SketchPlugin_MacroCircle.cpp b/src/SketchPlugin/SketchPlugin_MacroCircle.cpp index 89d137348..375fec588 100644 --- a/src/SketchPlugin/SketchPlugin_MacroCircle.cpp +++ b/src/SketchPlugin/SketchPlugin_MacroCircle.cpp @@ -7,7 +7,8 @@ #include "SketchPlugin_MacroCircle.h" #include "SketchPlugin_Circle.h" -#include "SketchPlugin_ConstraintCoincidence.h" +//#include "SketchPlugin_ConstraintCoincidence.h" +#include "SketchPlugin_Point.h" #include "SketchPlugin_Tools.h" #include @@ -22,7 +23,6 @@ #include #include -#include #include #include #include @@ -67,28 +67,74 @@ void SketchPlugin_MacroCircle::initAttributes() data()->addAttribute(FIRST_POINT_ID(), GeomDataAPI_Point2D::typeId()); data()->addAttribute(SECOND_POINT_ID(), GeomDataAPI_Point2D::typeId()); data()->addAttribute(THIRD_POINT_ID(), GeomDataAPI_Point2D::typeId()); + data()->addAttribute(FIRST_POINT_REF_ID(), ModelAPI_AttributeRefAttr::typeId()); + data()->addAttribute(SECOND_POINT_REF_ID(), ModelAPI_AttributeRefAttr::typeId()); + data()->addAttribute(THIRD_POINT_REF_ID(), ModelAPI_AttributeRefAttr::typeId()); data()->addAttribute(CIRCLE_RADIUS_ID(), ModelAPI_AttributeDouble::typeId()); data()->addAttribute(AUXILIARY_ID(), ModelAPI_AttributeBoolean::typeId()); ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), CENTER_POINT_REF_ID()); ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), PASSED_POINT_REF_ID()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), FIRST_POINT_REF_ID()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), SECOND_POINT_REF_ID()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), THIRD_POINT_REF_ID()); } void SketchPlugin_MacroCircle::execute() { - // Create circle feature. - FeaturePtr aCircleFeature = sketch()->addFeature(SketchPlugin_Circle::ID()); - std::dynamic_pointer_cast( - aCircleFeature->attribute(SketchPlugin_Circle::CENTER_ID()))->setValue(myCenter->x(), - myCenter->y()); - aCircleFeature->real(SketchPlugin_Circle::RADIUS_ID())->setValue(myRadius); - aCircleFeature->boolean(SketchPlugin_Circle::AUXILIARY_ID()) - ->setValue(boolean(AUXILIARY_ID())->value()); - aCircleFeature->execute(); + std::string aType = string(CIRCLE_TYPE())->value(); + if (aType == CIRCLE_TYPE_BY_CENTER_AND_PASSED_POINTS()) + createCircleByCenterAndPassed(); + else if (aType == CIRCLE_TYPE_BY_THREE_POINTS()) + createCircleByThreePoints(); myCenter.reset(); myRadius = 0; +} + +static void convertToPointOrTangent(const AttributeRefAttrPtr& theRefAttr, + const AttributePtr& theBaseAttr, + std::shared_ptr& thePassingPoint, + std::shared_ptr& theTangentCurve) +{ + AttributePtr anAttr = theBaseAttr; + if (theRefAttr->isObject()) { + FeaturePtr aTgFeature = ModelAPI_Feature::feature(theRefAttr->object()); + if (aTgFeature) { + if (aTgFeature->getKind() != SketchPlugin_Point::ID()) { + theTangentCurve = aTgFeature->lastResult()->shape(); + return; + } + anAttr = aTgFeature->attribute(SketchPlugin_Point::COORD_ID()); + } + } else + anAttr = theRefAttr->attr(); + + thePassingPoint = std::dynamic_pointer_cast(anAttr)->pnt(); +} + +void SketchPlugin_MacroCircle::createCircleByCenterAndPassed() +{ + AttributePtr aPassedAttr = attribute(PASSED_POINT_ID()); + AttributeRefAttrPtr aPassedRef = refattr(PASSED_POINT_REF_ID()); + // Calculate circle parameters + std::shared_ptr aCenter = + std::dynamic_pointer_cast(attribute(CENTER_POINT_ID()))->pnt(); + std::shared_ptr aPassedPoint; + std::shared_ptr aTangentCurve; + convertToPointOrTangent(aPassedRef, aPassedAttr, aPassedPoint, aTangentCurve); + + // Build a circle + std::shared_ptr aCircle; + if (aTangentCurve) { + std::shared_ptr anAxis = SketchPlugin_Sketch::plane(sketch()); + aCircle = std::shared_ptr(new GeomAPI_Circ2d(aCenter, aTangentCurve, anAxis)); + } else + aCircle = std::shared_ptr(new GeomAPI_Circ2d(aCenter, aPassedPoint)); + + // Create circle feature. + FeaturePtr aCircleFeature = createCircleFeature(aCircle); // Create constraints. SketchPlugin_Tools::createConstraint(this, @@ -103,6 +149,57 @@ void SketchPlugin_MacroCircle::execute() true); } +void SketchPlugin_MacroCircle::createCircleByThreePoints() +{ + std::string aPointAttr[3] = { FIRST_POINT_ID(), + SECOND_POINT_ID(), + THIRD_POINT_ID() }; + std::string aPointRef[3] = { FIRST_POINT_REF_ID(), + SECOND_POINT_REF_ID(), + THIRD_POINT_REF_ID() }; + std::shared_ptr aPassedEntities[3]; + for (int i = 0; i < 3; ++i) { + AttributePtr aPassedAttr = attribute(aPointAttr[i]); + AttributeRefAttrPtr aPassedRef = refattr(aPointRef[i]); + // calculate circle parameters + std::shared_ptr aPassedPoint; + std::shared_ptr aTangentCurve; + convertToPointOrTangent(aPassedRef, aPassedAttr, aPassedPoint, aTangentCurve); + + if (aPassedPoint) + aPassedEntities[i] = aPassedPoint; + else + aPassedEntities[i] = aTangentCurve; + } + + std::shared_ptr anAxis = SketchPlugin_Sketch::plane(sketch()); + std::shared_ptr aCircle = std::shared_ptr( + new GeomAPI_Circ2d(aPassedEntities[0], aPassedEntities[1], aPassedEntities[2], anAxis)); + + // Create circle feature. + FeaturePtr aCircleFeature = createCircleFeature(aCircle); + ResultPtr aCircleResult = aCircleFeature->lastResult(); + + // Create constraints. + for (int i = 0; i < 3; ++i) + SketchPlugin_Tools::createConstraint(this, aPointRef[i], NULL, aCircleResult, true); +} + +FeaturePtr SketchPlugin_MacroCircle::createCircleFeature( + const std::shared_ptr& theCircle) +{ + FeaturePtr aCircleFeature = sketch()->addFeature(SketchPlugin_Circle::ID()); + std::shared_ptr aCenter = theCircle->center(); + std::dynamic_pointer_cast( + aCircleFeature->attribute(SketchPlugin_Circle::CENTER_ID()))->setValue(aCenter->x(), + aCenter->y()); + aCircleFeature->real(SketchPlugin_Circle::RADIUS_ID())->setValue(theCircle->radius()); + aCircleFeature->boolean(SketchPlugin_Circle::AUXILIARY_ID()) + ->setValue(boolean(AUXILIARY_ID())->value()); + aCircleFeature->execute(); + return aCircleFeature; +} + AISObjectPtr SketchPlugin_MacroCircle::getAISObject(AISObjectPtr thePrevious) { if(!myCenter.get() || myRadius < tolerance) { diff --git a/src/SketchPlugin/SketchPlugin_MacroCircle.h b/src/SketchPlugin/SketchPlugin_MacroCircle.h index 4ea0ecc50..bc387acd5 100644 --- a/src/SketchPlugin/SketchPlugin_MacroCircle.h +++ b/src/SketchPlugin/SketchPlugin_MacroCircle.h @@ -14,6 +14,7 @@ #include #include +#include /**\class SketchPlugin_MacroCircle * \ingroup Plugins @@ -85,6 +86,13 @@ class SketchPlugin_MacroCircle: public SketchPlugin_SketchEntity, return ID; } + /// Reference for first point selection. + inline static const std::string& FIRST_POINT_REF_ID() + { + static const std::string ID("first_point_ref"); + return ID; + } + /// Second point id. inline static const std::string& SECOND_POINT_ID() { @@ -92,6 +100,13 @@ class SketchPlugin_MacroCircle: public SketchPlugin_SketchEntity, return ID; } + /// Reference for second point selection. + inline static const std::string& SECOND_POINT_REF_ID() + { + static const std::string ID("second_point_ref"); + return ID; + } + /// Third point id. inline static const std::string& THIRD_POINT_ID() { @@ -99,6 +114,13 @@ class SketchPlugin_MacroCircle: public SketchPlugin_SketchEntity, return ID; } + /// Reference for third point selection. + inline static const std::string& THIRD_POINT_REF_ID() + { + static const std::string ID("third_point_ref"); + return ID; + } + /// Radius of the circle inline static const std::string& CIRCLE_RADIUS_ID() { @@ -143,6 +165,11 @@ class SketchPlugin_MacroCircle: public SketchPlugin_SketchEntity, private: void resetAttribute(const std::string& theId); + void createCircleByCenterAndPassed(); + void createCircleByThreePoints(); + + FeaturePtr createCircleFeature(const std::shared_ptr& theCircle); + private: std::shared_ptr myCenter; double myRadius; -- 2.39.2