Salome HOME
Issue #2161: Fatal error when creating a circle coincident to an existing circle...
[modules/shaper.git] / src / GeomAlgoAPI / GeomAlgoAPI_Circ2dBuilder.cpp
1 // Copyright (C) 2017-20xx CEA/DEN, EDF R&D
2
3 // File:        GeomAlgoAPI_Circ2dBuilder.cpp
4 // Created:     3 April 2017
5 // Author:      Artem ZHIDKOV
6
7 #include <GeomAlgoAPI_Circ2dBuilder.h>
8 #include <GeomAPI_Ax3.h>
9 #include <GeomAPI_Circ2d.h>
10 #include <GeomAPI_Pnt2d.h>
11 #include <GeomAPI_Dir2d.h>
12 #include <GeomAPI_Shape.h>
13
14 #include <BRep_Tool.hxx>
15 #include <ElCLib.hxx>
16 #include <GccAna_Circ2d2TanRad.hxx>
17 #include <GccAna_Circ2d3Tan.hxx>
18 #include <GccAna_Circ2dTanCen.hxx>
19 #include <GccEnt.hxx>
20 #include <GccEnt_QualifiedCirc.hxx>
21 #include <GccEnt_QualifiedLin.hxx>
22 #include <Geom2dAdaptor_Curve.hxx>
23 #include <Geom_Plane.hxx>
24 #include <TopoDS.hxx>
25 #include <TopoDS_Edge.hxx>
26
27 #include <cmath>
28
29 typedef std::shared_ptr<gp_Circ2d> Circ2dPtr;
30 typedef std::shared_ptr<Geom2dAdaptor_Curve> CurveAdaptorPtr;
31 typedef std::vector< std::shared_ptr<GccEnt_QualifiedCirc> > VectorOfGccCirc;
32 typedef std::vector< std::shared_ptr<GccEnt_QualifiedLin> >  VectorOfGccLine;
33
34
35 // Provide different mechanisms to create circle:
36 // * by passing points
37 // * by tangent edges
38 // * with specified radius
39 // * etc.
40 class CircleBuilder
41 {
42 public:
43   CircleBuilder(const std::shared_ptr<GeomAPI_Ax3>& theBasePlane)
44     : myPlane(new Geom_Plane(theBasePlane->impl<gp_Ax3>())),
45       myRadius(0.0)
46   {}
47
48   void setRadius(const double theRadius)
49   { myRadius = theRadius; }
50
51   void setCenter(const std::shared_ptr<GeomAPI_Pnt2d>& theCenter)
52   { myCenter = theCenter; }
53
54   void setTangentCurves(const std::vector< std::shared_ptr<GeomAPI_Shape> >& theEdges)
55   {
56     std::vector< std::shared_ptr<GeomAPI_Shape> >::const_iterator anEdgeIt;
57     for (anEdgeIt = theEdges.begin(); anEdgeIt != theEdges.end(); ++anEdgeIt) {
58       const TopoDS_Edge& anEdge = TopoDS::Edge((*anEdgeIt)->impl<TopoDS_Shape>());
59
60       double aFirst, aLast;
61       TopLoc_Location aLoc;
62       Handle(Geom2d_Curve) aCurve = BRep_Tool::CurveOnSurface(anEdge, myPlane, aLoc, aFirst, aLast);
63       CurveAdaptorPtr aCurveAdaptor(new Geom2dAdaptor_Curve(aCurve, aFirst, aLast));
64
65       // sort curves (circles should become first)
66       if (aCurveAdaptor->GetType() == GeomAbs_Circle)
67         myTangentShapes.insert(myTangentShapes.begin(), aCurveAdaptor);
68       else
69         myTangentShapes.push_back(aCurveAdaptor);
70     }
71   }
72
73   void setPassingPoints(const std::vector< std::shared_ptr<GeomAPI_Pnt2d> >& thePoints)
74   {
75     std::vector< std::shared_ptr<GeomAPI_Pnt2d> >::const_iterator aPIt;
76     for (aPIt = thePoints.begin(); aPIt != thePoints.end(); ++aPIt)
77       myPassingPoints.push_back((*aPIt)->impl<gp_Pnt2d>());
78   }
79
80   void setClosestPoint(const std::shared_ptr<GeomAPI_Pnt2d>& thePoint)
81   { myClosestPoint = thePoint; }
82
83
84   Circ2dPtr circle()
85   {
86     Circ2dPtr aResult;
87     if (myCenter) {
88       if (myPassingPoints.size() == 1)
89         aResult = circleByCenterAndPassingPoint();
90       else if (myTangentShapes.size() == 1)
91         aResult = circleByCenterAndTangent();
92       else if (myRadius > 0.0)
93         aResult = circleByCenterAndRadius();
94     } else if (myRadius > 0.0) {
95       if (myTangentShapes.size() == 2)
96         aResult = circleByRadiusAndTwoTangentCurves();
97     } else {
98       switch (myPassingPoints.size()) {
99       case 0:
100         aResult = circleByThreeTangentCurves();
101         break;
102       case 1:
103         aResult = circleByPointAndTwoTangentCurves();
104         break;
105       case 2:
106         aResult = circleByTwoPointsAndTangentCurve();
107         break;
108       case 3:
109         aResult = circleByThreePassingPoints();
110         break;
111       default:
112         break;
113       }
114     }
115     return aResult;
116   }
117
118 private:
119   Circ2dPtr circleByCenterAndRadius()
120   {
121     const gp_Pnt2d& aCenter = myCenter->impl<gp_Pnt2d>();
122     return Circ2dPtr(new gp_Circ2d(gp_Ax2d(aCenter, gp::DX2d()), myRadius));
123   }
124
125   Circ2dPtr circleByCenterAndPassingPoint()
126   {
127     const gp_Pnt2d& aCenter = myCenter->impl<gp_Pnt2d>();
128     if (aCenter.SquareDistance(myPassingPoints[0]) > Precision::SquareConfusion()) {
129       GccAna_Circ2dTanCen aBuilder(myPassingPoints[0], aCenter);
130       if (aBuilder.NbSolutions() > 0)
131         return Circ2dPtr(new gp_Circ2d(aBuilder.ThisSolution(1)));
132     }
133     return Circ2dPtr();
134   }
135
136   Circ2dPtr circleByCenterAndTangent()
137   {
138     const gp_Pnt2d& aCenter = myCenter->impl<gp_Pnt2d>();
139     CurveAdaptorPtr aCurve = myTangentShapes[0];
140
141     std::shared_ptr<GccAna_Circ2dTanCen> aCircleBuilder;
142     if (aCurve->GetType() == GeomAbs_Line &&
143         aCurve->Line().Distance(aCenter) > Precision::Confusion()) {
144       aCircleBuilder = std::shared_ptr<GccAna_Circ2dTanCen>(
145           new GccAna_Circ2dTanCen(aCurve->Line(), aCenter));
146     } else if (aCurve->GetType() == GeomAbs_Circle) {
147       aCircleBuilder = std::shared_ptr<GccAna_Circ2dTanCen>(new GccAna_Circ2dTanCen(
148           GccEnt::Unqualified(aCurve->Circle()), aCenter, Precision::Confusion()));
149     }
150
151     return getProperCircle(aCircleBuilder);
152   }
153
154   Circ2dPtr getProperCircle(const std::shared_ptr<GccAna_Circ2dTanCen>& theBuilder) const
155   {
156     if (!theBuilder)
157       return Circ2dPtr();
158
159     CurveAdaptorPtr aCurve = myTangentShapes[0];
160
161     int aNbSol = theBuilder->NbSolutions();
162     if (aNbSol == 0)
163       return Circ2dPtr();
164
165     int anAppropriateSolution = 1;
166     double aMinDist = Precision::Infinite();
167
168     double aParSol, aPonTgCurve;
169     gp_Pnt2d aTgPnt;
170     for (int i = 1; i <= aNbSol && aNbSol > 1 && aCurve; ++i) {
171       theBuilder->Tangency1(i, aParSol, aPonTgCurve, aTgPnt);
172       if (isParamOnCurve(aPonTgCurve, aCurve)) {
173         double aDist = distanceToClosestPoint(theBuilder->ThisSolution(i));
174         if (aDist < aMinDist) {
175           anAppropriateSolution = i;
176           aMinDist = aDist;
177         }
178       }
179     }
180
181     return Circ2dPtr(new gp_Circ2d(theBuilder->ThisSolution(anAppropriateSolution)));
182   }
183
184   double distanceToClosestPoint(const gp_Circ2d& theCirc) const
185   {
186     if (myClosestPoint) {
187       double aDist = myClosestPoint->impl<gp_Pnt2d>().Distance(theCirc.Location());
188       return fabs(aDist - theCirc.Radius());
189     }
190     return 0.0;
191   }
192
193
194   Circ2dPtr circleByThreeTangentCurves()
195   {
196     VectorOfGccCirc aTgCirc;
197     VectorOfGccLine aTgLine;
198     convertTangentCurvesToGccEnt(aTgCirc, aTgLine);
199
200     if (aTgCirc.size() + aTgLine.size() != 3)
201       return Circ2dPtr();
202
203     std::shared_ptr<GccAna_Circ2d3Tan> aCircleBuilder;
204     switch (aTgLine.size()) {
205     case 0:
206       aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
207           *aTgCirc[0], *aTgCirc[1], *aTgCirc[2], Precision::Confusion()));
208       break;
209     case 1:
210       aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
211           *aTgCirc[0], *aTgCirc[1], *aTgLine[0], Precision::Confusion()));
212       break;
213     case 2:
214       aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
215           *aTgCirc[0], *aTgLine[0], *aTgLine[1], Precision::Confusion()));
216       break;
217     case 3:
218       aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
219           *aTgLine[0], *aTgLine[1], *aTgLine[0], Precision::Confusion()));
220       break;
221     default:
222       break;
223     }
224
225     return getProperCircle(aCircleBuilder);
226   }
227
228   Circ2dPtr circleByPointAndTwoTangentCurves()
229   {
230     const gp_Pnt2d& aPoint = myPassingPoints[0];
231     CurveAdaptorPtr aCurve1 = myTangentShapes[0];
232     CurveAdaptorPtr aCurve2 = myTangentShapes[1];
233     if (!aCurve1 || !aCurve2)
234       return Circ2dPtr();
235
236     std::shared_ptr<GccAna_Circ2d3Tan> aCircleBuilder;
237     if (aCurve1->GetType() == GeomAbs_Line) {
238       if (aCurve2->GetType() == GeomAbs_Line) {
239         aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(
240             new GccAna_Circ2d3Tan(GccEnt::Unqualified(aCurve1->Line()),
241                                   GccEnt::Unqualified(aCurve2->Line()),
242                                   aPoint, Precision::Confusion()));
243       } else if (aCurve2->GetType() == GeomAbs_Circle) {
244         aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(
245             new GccAna_Circ2d3Tan(GccEnt::Unqualified(aCurve2->Circle()),
246                                   GccEnt::Unqualified(aCurve1->Line()),
247                                   aPoint, Precision::Confusion()));
248       }
249     } else if (aCurve1->GetType() == GeomAbs_Circle) {
250       if (aCurve2->GetType() == GeomAbs_Line) {
251         aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(
252             new GccAna_Circ2d3Tan(GccEnt::Unqualified(aCurve1->Circle()),
253                                   GccEnt::Unqualified(aCurve2->Line()),
254                                   aPoint, Precision::Confusion()));
255       } else if (aCurve2->GetType() == GeomAbs_Circle) {
256         aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(
257             new GccAna_Circ2d3Tan(GccEnt::Unqualified(aCurve2->Circle()),
258                                   GccEnt::Unqualified(aCurve1->Circle()),
259                                   aPoint, Precision::Confusion()));
260       }
261     }
262
263     return getProperCircle(aCircleBuilder);
264   }
265
266   Circ2dPtr circleByTwoPointsAndTangentCurve()
267   {
268     const gp_Pnt2d& aPoint1 = myPassingPoints[0];
269     const gp_Pnt2d& aPoint2 = myPassingPoints[1];
270     CurveAdaptorPtr aCurve = myTangentShapes[0];
271     if (!aCurve)
272       return Circ2dPtr();
273
274     std::shared_ptr<GccAna_Circ2d3Tan> aCircleBuilder;
275     if (aCurve->GetType() == GeomAbs_Line) {
276       aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
277         GccEnt::Unqualified(aCurve->Line()), aPoint1, aPoint2, Precision::Confusion()));
278     } else if (aCurve->GetType() == GeomAbs_Circle) {
279       aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
280           GccEnt::Unqualified(aCurve->Circle()), aPoint1, aPoint2, Precision::Confusion()));
281     }
282
283     return getProperCircle(aCircleBuilder);
284   }
285
286   Circ2dPtr circleByThreePassingPoints()
287   {
288     GccAna_Circ2d3Tan aCircleBuilder(myPassingPoints[0],
289                                      myPassingPoints[1],
290                                      myPassingPoints[2],
291                                      Precision::Confusion());
292     if (aCircleBuilder.NbSolutions() > 0)
293       return Circ2dPtr(new gp_Circ2d(aCircleBuilder.ThisSolution(1)));
294     return Circ2dPtr();
295   }
296
297   Circ2dPtr getProperCircle(const std::shared_ptr<GccAna_Circ2d3Tan>& theBuilder) const
298   {
299     if (!theBuilder)
300       return Circ2dPtr();
301
302     int aNbSol = theBuilder->NbSolutions();
303     if (aNbSol == 0)
304       return Circ2dPtr();
305
306     int anAppropriateSolution = 1;
307     double aMinDist = Precision::Infinite();
308
309     double aParSol, aPonTgCurve;
310     gp_Pnt2d aTgPnt;
311     for (int i = 1; i <= aNbSol && aNbSol > 1; ++i) {
312       bool isApplicable = false;
313       if (myTangentShapes.size() >= 1) {
314         theBuilder->Tangency1(i, aParSol, aPonTgCurve, aTgPnt);
315         isApplicable = isParamOnCurve(aPonTgCurve, myTangentShapes[0]);
316       }
317       if (myTangentShapes.size() >= 2 && isApplicable) {
318         theBuilder->Tangency2(i, aParSol, aPonTgCurve, aTgPnt);
319         isApplicable = isParamOnCurve(aPonTgCurve, myTangentShapes[1]);
320       }
321       if (myTangentShapes.size() >= 3 && isApplicable) {
322         theBuilder->Tangency3(i, aParSol, aPonTgCurve, aTgPnt);
323         isApplicable = isParamOnCurve(aPonTgCurve, myTangentShapes[2]);
324       }
325
326       if (isApplicable) {
327         double aDist = distanceToClosestPoint(theBuilder->ThisSolution(i));
328         if (aDist < aMinDist) {
329           anAppropriateSolution = i;
330           aMinDist = aDist;
331         }
332       }
333     }
334
335     return Circ2dPtr(new gp_Circ2d(theBuilder->ThisSolution(anAppropriateSolution)));
336   }
337
338
339   Circ2dPtr circleByRadiusAndTwoTangentCurves()
340   {
341     VectorOfGccCirc aTgCirc;
342     VectorOfGccLine aTgLine;
343     convertTangentCurvesToGccEnt(aTgCirc, aTgLine);
344
345     if (aTgCirc.size() + aTgLine.size() != 2)
346       return Circ2dPtr();
347
348     std::shared_ptr<GccAna_Circ2d2TanRad> aCircleBuilder;
349     switch (aTgLine.size()) {
350     case 0:
351       aCircleBuilder = std::shared_ptr<GccAna_Circ2d2TanRad>(new GccAna_Circ2d2TanRad(
352           *aTgCirc[0], *aTgCirc[1], myRadius, Precision::Confusion()));
353       break;
354     case 1:
355       aCircleBuilder = std::shared_ptr<GccAna_Circ2d2TanRad>(new GccAna_Circ2d2TanRad(
356           *aTgCirc[0], *aTgLine[0], myRadius, Precision::Confusion()));
357       break;
358     case 2:
359       aCircleBuilder = std::shared_ptr<GccAna_Circ2d2TanRad>(new GccAna_Circ2d2TanRad(
360           *aTgLine[0], *aTgLine[1], myRadius, Precision::Confusion()));
361       break;
362     default:
363       break;
364     }
365
366     return getProperCircle(aCircleBuilder);
367   }
368
369   Circ2dPtr getProperCircle(const std::shared_ptr<GccAna_Circ2d2TanRad>& theBuilder) const
370   {
371     if (!theBuilder)
372       return Circ2dPtr();
373
374     int aNbSol = theBuilder->NbSolutions();
375     if (aNbSol == 0)
376       return Circ2dPtr();
377
378     int anAppropriateSolution = 1;
379     double aMinDist = Precision::Infinite();
380
381     double aParSol, aPonTgCurve;
382     gp_Pnt2d aTgPnt;
383     for (int i = 1; i <= aNbSol && aNbSol > 1; ++i) {
384       bool isApplicable = false;
385       if (myTangentShapes.size() >= 1) {
386         theBuilder->Tangency1(i, aParSol, aPonTgCurve, aTgPnt);
387         isApplicable = isParamInCurve(aPonTgCurve, myTangentShapes[0]);
388       }
389       if (myTangentShapes.size() >= 2 && isApplicable) {
390         theBuilder->Tangency2(i, aParSol, aPonTgCurve, aTgPnt);
391         isApplicable = isParamInCurve(aPonTgCurve, myTangentShapes[1]);
392       }
393
394       if (isApplicable) {
395         double aDist = distanceToClosestPoint(theBuilder->ThisSolution(i));
396         if (aDist < aMinDist) {
397           anAppropriateSolution = i;
398           aMinDist = aDist;
399         }
400       }
401     }
402
403     return Circ2dPtr(new gp_Circ2d(theBuilder->ThisSolution(anAppropriateSolution)));
404   }
405
406
407   void convertTangentCurvesToGccEnt(VectorOfGccCirc& theTangentCircles,
408                                     VectorOfGccLine& theTangentLines)
409   {
410     theTangentCircles.reserve(3);
411     theTangentLines.reserve(3);
412
413     std::vector<CurveAdaptorPtr>::iterator anIt = myTangentShapes.begin();
414     for (; anIt != myTangentShapes.end(); ++anIt) {
415       switch ((*anIt)->GetType()) {
416       case GeomAbs_Line:
417         theTangentLines.push_back(
418             std::shared_ptr<GccEnt_QualifiedLin>(
419             new GccEnt_QualifiedLin((*anIt)->Line(), GccEnt_unqualified))
420         );
421         break;
422       case GeomAbs_Circle:
423         theTangentCircles.push_back(
424             std::shared_ptr<GccEnt_QualifiedCirc>(
425             new GccEnt_QualifiedCirc((*anIt)->Circle(), GccEnt_unqualified))
426         );
427         break;
428       default:
429         break;
430       }
431     }
432   }
433
434
435   static void adjustPeriod(double& theParameter, const CurveAdaptorPtr& theCurve)
436   {
437     if (theCurve->Curve()->IsPeriodic()) {
438       theParameter = ElCLib::InPeriod(theParameter,
439                                       theCurve->FirstParameter(),
440                                       theCurve->FirstParameter() + theCurve->Period());
441     }
442   }
443
444   // boundary parameters of curve are NOT applied
445   static bool isParamInCurve(double& theParameter, const CurveAdaptorPtr& theCurve)
446   {
447     adjustPeriod(theParameter, theCurve);
448     return theParameter > theCurve->FirstParameter() &&
449            theParameter < theCurve->LastParameter();
450   }
451
452   // boundary parameters of curve are applied too
453   static bool isParamOnCurve(double& theParameter, const CurveAdaptorPtr& theCurve)
454   {
455     adjustPeriod(theParameter, theCurve);
456     return theParameter >= theCurve->FirstParameter() &&
457            theParameter <= theCurve->LastParameter();
458   }
459
460 private:
461   Handle(Geom_Plane) myPlane;
462   std::shared_ptr<GeomAPI_Pnt2d> myCenter;
463   std::vector<gp_Pnt2d> myPassingPoints;
464   std::vector<CurveAdaptorPtr> myTangentShapes;
465   double myRadius;
466   std::shared_ptr<GeomAPI_Pnt2d> myClosestPoint;
467 };
468
469
470
471
472
473 GeomAlgoAPI_Circ2dBuilder::GeomAlgoAPI_Circ2dBuilder(const std::shared_ptr<GeomAPI_Ax3>& thePlane)
474   : myPlane(thePlane),
475     myRadius(0.)
476 {
477 }
478
479 void GeomAlgoAPI_Circ2dBuilder::setRadius(const double theRadius)
480 {
481   myRadius = theRadius;
482 }
483
484 void GeomAlgoAPI_Circ2dBuilder::setCenter(const std::shared_ptr<GeomAPI_Pnt2d>& theCenter)
485 {
486   myCenter = theCenter;
487 }
488
489 void GeomAlgoAPI_Circ2dBuilder::addTangentCurve(const std::shared_ptr<GeomAPI_Shape>& theEdge)
490 {
491   if (theEdge->isEdge())
492     myTangentShapes.push_back(theEdge);
493 }
494
495 void GeomAlgoAPI_Circ2dBuilder::addPassingPoint(const std::shared_ptr<GeomAPI_Pnt2d>& thePoint)
496 {
497   myPassingPoints.push_back(thePoint);
498 }
499
500 void GeomAlgoAPI_Circ2dBuilder::setClosestPoint(const std::shared_ptr<GeomAPI_Pnt2d>& thePoint)
501 {
502   myClosestPoint = thePoint;
503 }
504
505 std::shared_ptr<GeomAPI_Circ2d> GeomAlgoAPI_Circ2dBuilder::circle(
506     const std::shared_ptr<GeomAPI_Pnt2d>& theFirstPoint,
507     const std::shared_ptr<GeomAPI_Pnt2d>& theSecondPoint,
508     const std::shared_ptr<GeomAPI_Pnt2d>& theThirdPoint)
509 {
510   std::shared_ptr<GeomAPI_Ax3> aPlane(new GeomAPI_Ax3);
511
512   GeomAlgoAPI_Circ2dBuilder aBuilder(aPlane);
513   aBuilder.addPassingPoint(theFirstPoint);
514   aBuilder.addPassingPoint(theSecondPoint);
515   aBuilder.addPassingPoint(theThirdPoint);
516   return aBuilder.circle();
517 }
518
519 std::shared_ptr<GeomAPI_Circ2d> GeomAlgoAPI_Circ2dBuilder::circle()
520 {
521   CircleBuilder aCircleBuilder(myPlane);
522   aCircleBuilder.setCenter(myCenter);
523   aCircleBuilder.setTangentCurves(myTangentShapes);
524   aCircleBuilder.setPassingPoints(myPassingPoints);
525   aCircleBuilder.setClosestPoint(myClosestPoint);
526   aCircleBuilder.setRadius(myRadius);
527   Circ2dPtr aCirc2d = aCircleBuilder.circle();
528
529   std::shared_ptr<GeomAPI_Circ2d> aCircle;
530   if (aCirc2d) {
531     const gp_Pnt2d& aCenter = aCirc2d->Location();
532     const gp_Dir2d& aXAxis = aCirc2d->Position().XDirection();
533
534     std::shared_ptr<GeomAPI_Pnt2d> aCircleCenter(new GeomAPI_Pnt2d(aCenter.X(), aCenter.Y()));
535     std::shared_ptr<GeomAPI_Dir2d> aCircleDir(new GeomAPI_Dir2d(aXAxis.X(), aXAxis.Y()));
536
537     aCircle = std::shared_ptr<GeomAPI_Circ2d>(
538         new GeomAPI_Circ2d(aCircleCenter, aCircleDir, aCirc2d->Radius()));
539   }
540   return aCircle;
541 }