Salome HOME
Issue #2024: Redesign of circle and arc of circle
[modules/shaper.git] / src / GeomAPI / GeomAPI_Circ2d.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3 // File:        GeomAPI_Circ2d.cpp
4 // Created:     29 May 2014
5 // Author:      Artem ZHIDKOV
6
7 #include <GeomAPI_Circ2d.h>
8 #include <GeomAPI_Ax3.h>
9 #include <GeomAPI_Pnt2d.h>
10 #include <GeomAPI_Dir2d.h>
11 #include <GeomAPI_Shape.h>
12
13 #include <BRep_Tool.hxx>
14 #include <gp_Dir2d.hxx>
15 #include <gp_Circ2d.hxx>
16 #include <gp_Lin2d.hxx>
17 #include <gp_Pnt2d.hxx>
18 #include <gp_Ax2d.hxx>
19 #include <GccAna_Circ2d3Tan.hxx>
20 #include <GccAna_Circ2dTanCen.hxx>
21 #include <GccEnt.hxx>
22 #include <GccEnt_QualifiedCirc.hxx>
23 #include <GccEnt_QualifiedLin.hxx>
24 #include <GeomLib_Tool.hxx>
25 #include <Geom2d_Circle.hxx>
26 #include <Geom2dAPI_ProjectPointOnCurve.hxx>
27 #include <Geom_Plane.hxx>
28 #include <IntAna2d_AnaIntersection.hxx>
29 #include <Precision.hxx>
30 #include <TopoDS.hxx>
31 #include <TopoDS_Edge.hxx>
32
33 #include <vector>
34
35 #define MY_CIRC2D implPtr<gp_Circ2d>()
36
37 typedef std::shared_ptr<Geom2dAdaptor_Curve>  CurveAdaptorPtr;
38
39 class CircleBuilder
40 {
41 public:
42   CircleBuilder(const std::shared_ptr<GeomAPI_Ax3>& theBasePlane)
43     : myPlane(new Geom_Plane(theBasePlane->impl<gp_Ax3>()))
44   {}
45
46   void addCenter(const std::shared_ptr<GeomAPI_Pnt2d>& theCenter)
47   { myCenter = theCenter; }
48
49   void addPassingEntity(const std::shared_ptr<GeomAPI_Interface>& theEntity)
50   {
51     std::shared_ptr<GeomAPI_Pnt2d> aPoint = std::dynamic_pointer_cast<GeomAPI_Pnt2d>(theEntity);
52     if (aPoint)
53       addPassingPoint(aPoint);
54     else {
55       std::shared_ptr<GeomAPI_Shape> aShape = std::dynamic_pointer_cast<GeomAPI_Shape>(theEntity);
56       if (aShape)
57         addTangentCurve(aShape);
58     }
59   }
60
61   void addTangentCurve(const std::shared_ptr<GeomAPI_Shape>& theEdge)
62   {
63     if (!theEdge->isEdge())
64       return;
65
66     const TopoDS_Edge& anEdge = TopoDS::Edge(theEdge->impl<TopoDS_Shape>());
67
68     double aFirst, aLast;
69     TopLoc_Location aLoc;
70     CurveAdaptorPtr aCurve(new Geom2dAdaptor_Curve(
71         BRep_Tool::CurveOnSurface(anEdge, myPlane, aLoc, aFirst, aLast)));
72
73     myTangentShapes.push_back(aCurve);
74   }
75
76   void addPassingPoint(const std::shared_ptr<GeomAPI_Pnt2d>& thePoint)
77   {
78     myPassingPoints.push_back(thePoint->impl<gp_Pnt2d>());
79   }
80
81   gp_Circ2d* circle()
82   {
83     gp_Circ2d* aResult = 0;
84     if (myCenter) {
85       if (myPassingPoints.size() == 1)
86         aResult = circleByCenterAndPassingPoint();
87       else if (myTangentShapes.size() == 1)
88         aResult = circleByCenterAndTangent();
89     } else {
90       switch (myPassingPoints.size()) {
91       case 0:
92         aResult = circleByThreeTangentCurves();
93         break;
94       case 1:
95         aResult = circleByPointAndTwoTangentCurves();
96         break;
97       case 2:
98         aResult = circleByTwoPointsAndTangentCurve();
99         break;
100       case 3:
101         aResult = circleByThreePassingPoints();
102         break;
103       default:
104         break;
105       }
106     }
107     return aResult;
108   }
109
110 private:
111   gp_Circ2d* circleByCenterAndPassingPoint()
112   {
113     const gp_Pnt2d& aCenter = myCenter->impl<gp_Pnt2d>();
114     GccAna_Circ2dTanCen aBuilder(myPassingPoints[0], aCenter);
115     if (aBuilder.NbSolutions() > 0)
116       return new gp_Circ2d(aBuilder.ThisSolution(1));
117     return 0;
118   }
119
120   gp_Circ2d* circleByCenterAndTangent()
121   {
122     const gp_Pnt2d& aCenter = myCenter->impl<gp_Pnt2d>();
123     CurveAdaptorPtr aCurve = myTangentShapes[0];
124
125     std::shared_ptr<GccAna_Circ2dTanCen> aCircleBuilder;
126     if (aCurve->GetType() == GeomAbs_Line) {
127       aCircleBuilder = std::shared_ptr<GccAna_Circ2dTanCen>(
128           new GccAna_Circ2dTanCen(aCurve->Line(), aCenter));
129     } else if (aCurve->GetType() == GeomAbs_Circle) {
130       aCircleBuilder = std::shared_ptr<GccAna_Circ2dTanCen>(new GccAna_Circ2dTanCen(
131           GccEnt::Unqualified(aCurve->Circle()), aCenter, Precision::Confusion()));
132     }
133
134     return getProperCircle(aCircleBuilder);
135   }
136
137   gp_Circ2d* getProperCircle(const std::shared_ptr<GccAna_Circ2dTanCen>& theBuilder) const
138   {
139     if (!theBuilder)
140       return 0;
141
142     CurveAdaptorPtr aCurve = myTangentShapes[0];
143
144     gp_Circ2d* aResult = 0;
145     int aNbSol = theBuilder->NbSolutions();
146     double aParSol, aPonTgCurve;
147     gp_Pnt2d aTgPnt;
148     for (int i = 1; i <= aNbSol && aCurve; ++i) {
149       theBuilder->Tangency1(i, aParSol, aPonTgCurve, aTgPnt);
150       if (aPonTgCurve >= aCurve->FirstParameter() && aPonTgCurve <= aCurve->LastParameter()) {
151         aResult = new gp_Circ2d(theBuilder->ThisSolution(i));
152         break;
153       }
154     }
155     // unable to build circle passing through the tangent curve,
156     // so get a circle passing any tangent point
157     if (!aResult && aNbSol > 0)
158       aResult =  new gp_Circ2d(theBuilder->ThisSolution(1));
159     return aResult;
160   }
161
162
163   gp_Circ2d* circleByThreeTangentCurves()
164   {
165     std::shared_ptr<GccEnt_QualifiedCirc> aTgCirc[3];
166     std::shared_ptr<GccEnt_QualifiedLin>  aTgLine[3];
167     int aNbTgCirc = 0;
168     int aNbTgLine = 0;
169
170     std::vector<CurveAdaptorPtr>::iterator anIt = myTangentShapes.begin();
171     for (; anIt != myTangentShapes.end(); ++anIt) {
172       switch ((*anIt)->GetType()) {
173       case GeomAbs_Line:
174         aTgLine[aNbTgLine++] = std::shared_ptr<GccEnt_QualifiedLin>(
175             new GccEnt_QualifiedLin((*anIt)->Line(), GccEnt_unqualified));
176         break;
177       case GeomAbs_Circle:
178         aTgCirc[aNbTgCirc++] = std::shared_ptr<GccEnt_QualifiedCirc>(
179             new GccEnt_QualifiedCirc((*anIt)->Circle(), GccEnt_unqualified));
180         break;
181       default:
182         break;
183       }
184     }
185     if (aNbTgCirc + aNbTgLine != 3)
186       return 0;
187
188     std::shared_ptr<GccAna_Circ2d3Tan> aCircleBuilder;
189     switch (aNbTgLine) {
190     case 0:
191       aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
192           *aTgCirc[0], *aTgCirc[1], *aTgCirc[2], Precision::Confusion()));
193       break;
194     case 1:
195       aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
196           *aTgCirc[0], *aTgCirc[1], *aTgLine[0], Precision::Confusion()));
197       break;
198     case 2:
199       aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
200           *aTgCirc[0], *aTgLine[0], *aTgLine[1], Precision::Confusion()));
201       break;
202     case 3:
203       aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
204           *aTgLine[0], *aTgLine[1], *aTgLine[0], Precision::Confusion()));
205       break;
206     default:
207       break;
208     }
209
210     return getProperCircle(aCircleBuilder);
211   }
212
213   gp_Circ2d* circleByPointAndTwoTangentCurves()
214   {
215     const gp_Pnt2d& aPoint = myPassingPoints[0];
216     CurveAdaptorPtr aCurve1 = myTangentShapes[0];
217     CurveAdaptorPtr aCurve2 = myTangentShapes[1];
218     if (!aCurve1 || !aCurve2)
219       return 0;
220
221     std::shared_ptr<GccAna_Circ2d3Tan> aCircleBuilder;
222     if (aCurve1->GetType() == GeomAbs_Line) {
223       if (aCurve2->GetType() == GeomAbs_Line) {
224         aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(
225             new GccAna_Circ2d3Tan(GccEnt::Unqualified(aCurve1->Line()),
226                                   GccEnt::Unqualified(aCurve2->Line()),
227                                   aPoint, Precision::Confusion()));
228       } else if (aCurve2->GetType() == GeomAbs_Circle) {
229         aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(
230             new GccAna_Circ2d3Tan(GccEnt::Unqualified(aCurve2->Circle()),
231                                   GccEnt::Unqualified(aCurve1->Line()),
232                                   aPoint, Precision::Confusion()));
233       }
234     } else if (aCurve1->GetType() == GeomAbs_Circle) {
235       if (aCurve2->GetType() == GeomAbs_Line) {
236         aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(
237             new GccAna_Circ2d3Tan(GccEnt::Unqualified(aCurve1->Circle()),
238                                   GccEnt::Unqualified(aCurve2->Line()),
239                                   aPoint, Precision::Confusion()));
240       } else if (aCurve2->GetType() == GeomAbs_Circle) {
241         aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(
242             new GccAna_Circ2d3Tan(GccEnt::Unqualified(aCurve2->Circle()),
243                                   GccEnt::Unqualified(aCurve1->Circle()),
244                                   aPoint, Precision::Confusion()));
245       }
246     }
247
248     return getProperCircle(aCircleBuilder);
249   }
250
251   gp_Circ2d* circleByTwoPointsAndTangentCurve()
252   {
253     const gp_Pnt2d& aPoint1 = myPassingPoints[0];
254     const gp_Pnt2d& aPoint2 = myPassingPoints[1];
255     CurveAdaptorPtr aCurve = myTangentShapes[0];
256     if (!aCurve)
257       return 0;
258
259     std::shared_ptr<GccAna_Circ2d3Tan> aCircleBuilder;
260     if (aCurve->GetType() == GeomAbs_Line) {
261       aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
262         GccEnt::Unqualified(aCurve->Line()), aPoint1, aPoint2, Precision::Confusion()));
263     } else if (aCurve->GetType() == GeomAbs_Circle) {
264       aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
265           GccEnt::Unqualified(aCurve->Circle()), aPoint1, aPoint2, Precision::Confusion()));
266     }
267
268     return getProperCircle(aCircleBuilder);
269   }
270
271   gp_Circ2d* circleByThreePassingPoints()
272   {
273     GccAna_Circ2d3Tan aCircleBuilder(myPassingPoints[0],
274                                      myPassingPoints[1],
275                                      myPassingPoints[2],
276                                      Precision::Confusion());
277     if (aCircleBuilder.NbSolutions() > 0)
278       return new gp_Circ2d(aCircleBuilder.ThisSolution(1));
279     return 0;
280   }
281
282
283   gp_Circ2d* getProperCircle(const std::shared_ptr<GccAna_Circ2d3Tan>& theBuilder) const
284   {
285     if (!theBuilder)
286       return 0;
287
288     gp_Circ2d* aResult = 0;
289     int aNbSol = theBuilder->NbSolutions();
290     double aParSol, aPonTgCurve;
291     gp_Pnt2d aTgPnt;
292     for (int i = 1; i <= aNbSol; ++i) {
293       bool isApplicable = false;
294       if (myTangentShapes.size() >= 1) {
295         theBuilder->Tangency1(i, aParSol, aPonTgCurve, aTgPnt);
296         isApplicable = aPonTgCurve >= myTangentShapes[0]->FirstParameter() &&
297                        aPonTgCurve <= myTangentShapes[0]->LastParameter();
298       }
299       if (myTangentShapes.size() >= 2 && isApplicable) {
300         theBuilder->Tangency2(i, aParSol, aPonTgCurve, aTgPnt);
301         isApplicable = aPonTgCurve >= myTangentShapes[1]->FirstParameter() &&
302                        aPonTgCurve <= myTangentShapes[1]->LastParameter();
303       }
304       if (myTangentShapes.size() >= 3 && isApplicable) {
305         theBuilder->Tangency3(i, aParSol, aPonTgCurve, aTgPnt);
306         isApplicable = aPonTgCurve >= myTangentShapes[2]->FirstParameter() &&
307                        aPonTgCurve <= myTangentShapes[2]->LastParameter();
308       }
309
310       if (isApplicable) {
311         aResult = new gp_Circ2d(theBuilder->ThisSolution(i));
312         break;
313       }
314     }
315     // unable to build circle passing through the tangent curve => get any tangent point
316     if (!aResult && aNbSol > 0)
317       aResult =  new gp_Circ2d(theBuilder->ThisSolution(1));
318     return aResult;
319   }
320
321 private:
322   Handle(Geom_Plane) myPlane;
323   std::shared_ptr<GeomAPI_Pnt2d> myCenter;
324   std::vector<gp_Pnt2d> myPassingPoints;
325   std::vector<CurveAdaptorPtr> myTangentShapes;
326 };
327
328 typedef std::shared_ptr<CircleBuilder> CircleBuilderPtr;
329
330
331 static gp_Circ2d* newCirc2d(const double theCenterX, const double theCenterY, const gp_Dir2d theDir,
332                             const double theRadius)
333 {
334   gp_Pnt2d aCenter(theCenterX, theCenterY);
335   return new gp_Circ2d(gp_Ax2d(aCenter, theDir), theRadius);
336 }
337
338 static gp_Circ2d* newCirc2d(const double theCenterX, const double theCenterY,
339                             const double thePointX, const double thePointY)
340 {
341   gp_Pnt2d aCenter(theCenterX, theCenterY);
342   gp_Pnt2d aPoint(thePointX, thePointY);
343
344   double aRadius = aCenter.Distance(aPoint);
345
346   if (aCenter.IsEqual(aPoint, Precision::Confusion()))
347     return NULL;
348
349   gp_Dir2d aDir(thePointX - theCenterX, thePointY - theCenterY);
350
351   return newCirc2d(theCenterX, theCenterY, aDir, aRadius);
352 }
353
354 static gp_Circ2d* newCirc2d(const std::shared_ptr<GeomAPI_Pnt2d>& theFirstPoint,
355                             const std::shared_ptr<GeomAPI_Pnt2d>& theSecondPoint,
356                             const std::shared_ptr<GeomAPI_Pnt2d>& theThirdPoint)
357 {
358   gp_XY aFirstPnt(theFirstPoint->x(), theFirstPoint->y());
359   gp_XY aSecondPnt(theSecondPoint->x(), theSecondPoint->y());
360   gp_XY aThirdPnt(theThirdPoint->x(), theThirdPoint->y());
361
362   gp_XY aVec12 = aSecondPnt - aFirstPnt;
363   gp_XY aVec23 = aThirdPnt - aSecondPnt;
364   gp_XY aVec31 = aFirstPnt - aThirdPnt;
365
366   // coefficients to calculate center
367   double aCoeff1, aCoeff2, aCoeff3;
368
369   // square of parallelogram
370   double aSquare2 = aVec12.Crossed(aVec23);
371   aSquare2 *= aSquare2 * 2.0;
372   if (aSquare2 < 1.e-20) {
373     // if two points are equal, build a circle on two different points as on diameter
374     double aSqLen12 = aVec12.SquareModulus();
375     double aSqLen23 = aVec23.SquareModulus();
376     double aSqLen31 = aVec31.SquareModulus();
377     if (aSqLen12 < Precision::SquareConfusion() &&
378         aSqLen23 < Precision::SquareConfusion() &&
379         aSqLen31 < Precision::SquareConfusion())
380       return NULL;
381     aCoeff1 = aCoeff2 = aCoeff3 = 1.0 / 3.0;
382   }
383   else {
384     aCoeff1 = aVec23.Dot(aVec23) / aSquare2 * aVec12.Dot(aVec31.Reversed());
385     aCoeff2 = aVec31.Dot(aVec31) / aSquare2 * aVec23.Dot(aVec12.Reversed());
386     aCoeff3 = aVec12.Dot(aVec12) / aSquare2 * aVec31.Dot(aVec23.Reversed());
387   }
388   // center
389   gp_XY aCenter = aFirstPnt * aCoeff1 + aSecondPnt * aCoeff2 + aThirdPnt * aCoeff3;
390   // radius
391   double aRadius = (aFirstPnt - aCenter).Modulus();
392
393   gp_Dir2d aDir(aFirstPnt - aCenter);
394   return newCirc2d(aCenter.X(), aCenter.Y(), aDir, aRadius);
395 }
396
397
398
399 GeomAPI_Circ2d::GeomAPI_Circ2d(const std::shared_ptr<GeomAPI_Pnt2d>& theCenter,
400                                const std::shared_ptr<GeomAPI_Pnt2d>& theCirclePoint)
401     : GeomAPI_Interface(
402         newCirc2d(theCenter->x(), theCenter->y(), theCirclePoint->x(), theCirclePoint->y()))
403 {
404 }
405
406 GeomAPI_Circ2d::GeomAPI_Circ2d(const std::shared_ptr<GeomAPI_Pnt2d>& theCenter,
407                                const std::shared_ptr<GeomAPI_Dir2d>& theDir, double theRadius)
408     : GeomAPI_Interface(
409         newCirc2d(theCenter->x(), theCenter->y(), theDir->impl<gp_Dir2d>(), theRadius))
410 {
411 }
412
413 GeomAPI_Circ2d::GeomAPI_Circ2d(const std::shared_ptr<GeomAPI_Pnt2d>& theFirstPoint,
414                                const std::shared_ptr<GeomAPI_Pnt2d>& theSecondPoint,
415                                const std::shared_ptr<GeomAPI_Pnt2d>& theThirdPoint)
416     : GeomAPI_Interface(newCirc2d(theFirstPoint, theSecondPoint, theThirdPoint))
417 {
418 }
419
420 GeomAPI_Circ2d::GeomAPI_Circ2d(const std::shared_ptr<GeomAPI_Pnt2d>& theCenter,
421                                const std::shared_ptr<GeomAPI_Shape>& theTangent,
422                                const std::shared_ptr<GeomAPI_Ax3>&   thePlane)
423 {
424   CircleBuilderPtr aBuilder(new CircleBuilder(thePlane));
425   aBuilder->addCenter(theCenter);
426   aBuilder->addTangentCurve(theTangent);
427   setImpl(aBuilder->circle());
428 }
429
430 GeomAPI_Circ2d::GeomAPI_Circ2d(const std::shared_ptr<GeomAPI_Interface>& theEntity1,
431                                const std::shared_ptr<GeomAPI_Interface>& theEntity2,
432                                const std::shared_ptr<GeomAPI_Interface>& theEntity3,
433                                const std::shared_ptr<GeomAPI_Ax3>&       thePlane)
434 {
435   CircleBuilderPtr aBuilder(new CircleBuilder(thePlane));
436   aBuilder->addPassingEntity(theEntity1);
437   aBuilder->addPassingEntity(theEntity2);
438   aBuilder->addPassingEntity(theEntity3);
439   setImpl(aBuilder->circle());
440 }
441
442
443 const std::shared_ptr<GeomAPI_Pnt2d> GeomAPI_Circ2d::project(
444     const std::shared_ptr<GeomAPI_Pnt2d>& thePoint) const
445 {
446   std::shared_ptr<GeomAPI_Pnt2d> aResult;
447   if (!MY_CIRC2D)
448     return aResult;
449
450   const gp_Pnt2d& aCenter = MY_CIRC2D->Location();
451   const gp_Pnt2d& aPoint = thePoint->impl<gp_Pnt2d>();
452
453   double aDist = aCenter.Distance(aPoint);
454   if (aDist < Precision::Confusion())
455     return aResult;
456
457   if (Abs(aDist - MY_CIRC2D->Radius()) < Precision::Confusion()) {
458     // Point on the circle
459     aResult = std::shared_ptr<GeomAPI_Pnt2d>(
460         new GeomAPI_Pnt2d(thePoint->x(), thePoint->y()));
461   } else {
462     gp_Dir2d aDir(aPoint.XY() - aCenter.XY());
463     gp_XY aNewPoint = aCenter.XY() + aDir.XY() * MY_CIRC2D->Radius();
464     aResult = std::shared_ptr<GeomAPI_Pnt2d>(
465         new GeomAPI_Pnt2d(aNewPoint.X(), aNewPoint.Y()));
466   }
467
468   return aResult;
469 }
470
471 const std::shared_ptr<GeomAPI_Pnt2d> GeomAPI_Circ2d::center() const
472 {
473   if (!MY_CIRC2D)
474     return std::shared_ptr<GeomAPI_Pnt2d>();
475   const gp_Pnt2d& aCenter = MY_CIRC2D->Location();
476   return std::shared_ptr<GeomAPI_Pnt2d>(new GeomAPI_Pnt2d(aCenter.X(), aCenter.Y()));
477 }
478
479 double GeomAPI_Circ2d::radius() const
480 {
481   if (!MY_CIRC2D)
482     return 0.0;
483   return MY_CIRC2D->Radius();
484 }
485
486 //=================================================================================================
487 const bool GeomAPI_Circ2d::parameter(const std::shared_ptr<GeomAPI_Pnt2d> thePoint,
488                                    const double theTolerance,
489                                    double& theParameter) const
490 {
491   Handle(Geom2d_Circle) aCurve = new Geom2d_Circle(*MY_CIRC2D);
492   return GeomLib_Tool::Parameter(aCurve, thePoint->impl<gp_Pnt2d>(),
493                                  theTolerance, theParameter) == Standard_True;
494 }
495
496 //=================================================================================================
497 void GeomAPI_Circ2d::D0(const double theU, std::shared_ptr<GeomAPI_Pnt2d>& thePoint)
498 {
499   Handle(Geom2d_Circle) aCurve = new Geom2d_Circle(*MY_CIRC2D);
500   gp_Pnt2d aPnt;
501   aCurve->D0(theU, aPnt);
502   thePoint.reset(new GeomAPI_Pnt2d(aPnt.X(), aPnt.Y()));
503 }
504