Salome HOME
Issue #2083: Creation of sketch arc by center have unexpected behavior
[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       aCircleBuilder = std::shared_ptr<GccAna_Circ2dTanCen>(
144           new GccAna_Circ2dTanCen(aCurve->Line(), aCenter));
145     } else if (aCurve->GetType() == GeomAbs_Circle) {
146       aCircleBuilder = std::shared_ptr<GccAna_Circ2dTanCen>(new GccAna_Circ2dTanCen(
147           GccEnt::Unqualified(aCurve->Circle()), aCenter, Precision::Confusion()));
148     }
149
150     return getProperCircle(aCircleBuilder);
151   }
152
153   Circ2dPtr getProperCircle(const std::shared_ptr<GccAna_Circ2dTanCen>& theBuilder) const
154   {
155     if (!theBuilder)
156       return Circ2dPtr();
157
158     CurveAdaptorPtr aCurve = myTangentShapes[0];
159
160     int aNbSol = theBuilder->NbSolutions();
161     if (aNbSol == 0)
162       return Circ2dPtr();
163
164     int anAppropriateSolution = 1;
165     double aMinDist = Precision::Infinite();
166
167     double aParSol, aPonTgCurve;
168     gp_Pnt2d aTgPnt;
169     for (int i = 1; i <= aNbSol && aCurve; ++i) {
170       theBuilder->Tangency1(i, aParSol, aPonTgCurve, aTgPnt);
171       if (isParamOnCurve(aPonTgCurve, aCurve)) {
172         double aDist = distanceToClosestPoint(theBuilder->ThisSolution(i));
173         if (aDist < aMinDist) {
174           anAppropriateSolution = i;
175           aMinDist = aDist;
176         }
177       }
178     }
179
180     return Circ2dPtr(new gp_Circ2d(theBuilder->ThisSolution(anAppropriateSolution)));
181   }
182
183   double distanceToClosestPoint(const gp_Circ2d& theCirc) const
184   {
185     if (myClosestPoint) {
186       double aDist = myClosestPoint->impl<gp_Pnt2d>().Distance(theCirc.Location());
187       return fabs(aDist - theCirc.Radius());
188     }
189     return 0.0;
190   }
191
192
193   Circ2dPtr circleByThreeTangentCurves()
194   {
195     VectorOfGccCirc aTgCirc;
196     VectorOfGccLine aTgLine;
197     convertTangentCurvesToGccEnt(aTgCirc, aTgLine);
198
199     if (aTgCirc.size() + aTgLine.size() != 3)
200       return Circ2dPtr();
201
202     std::shared_ptr<GccAna_Circ2d3Tan> aCircleBuilder;
203     switch (aTgLine.size()) {
204     case 0:
205       aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
206           *aTgCirc[0], *aTgCirc[1], *aTgCirc[2], Precision::Confusion()));
207       break;
208     case 1:
209       aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
210           *aTgCirc[0], *aTgCirc[1], *aTgLine[0], Precision::Confusion()));
211       break;
212     case 2:
213       aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
214           *aTgCirc[0], *aTgLine[0], *aTgLine[1], Precision::Confusion()));
215       break;
216     case 3:
217       aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
218           *aTgLine[0], *aTgLine[1], *aTgLine[0], Precision::Confusion()));
219       break;
220     default:
221       break;
222     }
223
224     return getProperCircle(aCircleBuilder);
225   }
226
227   Circ2dPtr circleByPointAndTwoTangentCurves()
228   {
229     const gp_Pnt2d& aPoint = myPassingPoints[0];
230     CurveAdaptorPtr aCurve1 = myTangentShapes[0];
231     CurveAdaptorPtr aCurve2 = myTangentShapes[1];
232     if (!aCurve1 || !aCurve2)
233       return Circ2dPtr();
234
235     std::shared_ptr<GccAna_Circ2d3Tan> aCircleBuilder;
236     if (aCurve1->GetType() == GeomAbs_Line) {
237       if (aCurve2->GetType() == GeomAbs_Line) {
238         aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(
239             new GccAna_Circ2d3Tan(GccEnt::Unqualified(aCurve1->Line()),
240                                   GccEnt::Unqualified(aCurve2->Line()),
241                                   aPoint, Precision::Confusion()));
242       } else if (aCurve2->GetType() == GeomAbs_Circle) {
243         aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(
244             new GccAna_Circ2d3Tan(GccEnt::Unqualified(aCurve2->Circle()),
245                                   GccEnt::Unqualified(aCurve1->Line()),
246                                   aPoint, Precision::Confusion()));
247       }
248     } else if (aCurve1->GetType() == GeomAbs_Circle) {
249       if (aCurve2->GetType() == GeomAbs_Line) {
250         aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(
251             new GccAna_Circ2d3Tan(GccEnt::Unqualified(aCurve1->Circle()),
252                                   GccEnt::Unqualified(aCurve2->Line()),
253                                   aPoint, Precision::Confusion()));
254       } else if (aCurve2->GetType() == GeomAbs_Circle) {
255         aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(
256             new GccAna_Circ2d3Tan(GccEnt::Unqualified(aCurve2->Circle()),
257                                   GccEnt::Unqualified(aCurve1->Circle()),
258                                   aPoint, Precision::Confusion()));
259       }
260     }
261
262     return getProperCircle(aCircleBuilder);
263   }
264
265   Circ2dPtr circleByTwoPointsAndTangentCurve()
266   {
267     const gp_Pnt2d& aPoint1 = myPassingPoints[0];
268     const gp_Pnt2d& aPoint2 = myPassingPoints[1];
269     CurveAdaptorPtr aCurve = myTangentShapes[0];
270     if (!aCurve)
271       return Circ2dPtr();
272
273     std::shared_ptr<GccAna_Circ2d3Tan> aCircleBuilder;
274     if (aCurve->GetType() == GeomAbs_Line) {
275       aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
276         GccEnt::Unqualified(aCurve->Line()), aPoint1, aPoint2, Precision::Confusion()));
277     } else if (aCurve->GetType() == GeomAbs_Circle) {
278       aCircleBuilder = std::shared_ptr<GccAna_Circ2d3Tan>(new GccAna_Circ2d3Tan(
279           GccEnt::Unqualified(aCurve->Circle()), aPoint1, aPoint2, Precision::Confusion()));
280     }
281
282     return getProperCircle(aCircleBuilder);
283   }
284
285   Circ2dPtr circleByThreePassingPoints()
286   {
287     GccAna_Circ2d3Tan aCircleBuilder(myPassingPoints[0],
288                                      myPassingPoints[1],
289                                      myPassingPoints[2],
290                                      Precision::Confusion());
291     if (aCircleBuilder.NbSolutions() > 0)
292       return Circ2dPtr(new gp_Circ2d(aCircleBuilder.ThisSolution(1)));
293     return Circ2dPtr();
294   }
295
296   Circ2dPtr getProperCircle(const std::shared_ptr<GccAna_Circ2d3Tan>& theBuilder) const
297   {
298     if (!theBuilder)
299       return Circ2dPtr();
300
301     int aNbSol = theBuilder->NbSolutions();
302     if (aNbSol == 0)
303       return Circ2dPtr();
304
305     int anAppropriateSolution = 1;
306     double aMinDist = Precision::Infinite();
307
308     double aParSol, aPonTgCurve;
309     gp_Pnt2d aTgPnt;
310     for (int i = 1; i <= aNbSol; ++i) {
311       bool isApplicable = false;
312       if (myTangentShapes.size() >= 1) {
313         theBuilder->Tangency1(i, aParSol, aPonTgCurve, aTgPnt);
314         isApplicable = isParamOnCurve(aPonTgCurve, myTangentShapes[0]);
315       }
316       if (myTangentShapes.size() >= 2 && isApplicable) {
317         theBuilder->Tangency2(i, aParSol, aPonTgCurve, aTgPnt);
318         isApplicable = isParamOnCurve(aPonTgCurve, myTangentShapes[1]);
319       }
320       if (myTangentShapes.size() >= 3 && isApplicable) {
321         theBuilder->Tangency3(i, aParSol, aPonTgCurve, aTgPnt);
322         isApplicable = isParamOnCurve(aPonTgCurve, myTangentShapes[2]);
323       }
324
325       if (isApplicable) {
326         double aDist = distanceToClosestPoint(theBuilder->ThisSolution(i));
327         if (aDist < aMinDist) {
328           anAppropriateSolution = i;
329           aMinDist = aDist;
330         }
331       }
332     }
333
334     return Circ2dPtr(new gp_Circ2d(theBuilder->ThisSolution(anAppropriateSolution)));
335   }
336
337
338   Circ2dPtr circleByRadiusAndTwoTangentCurves()
339   {
340     VectorOfGccCirc aTgCirc;
341     VectorOfGccLine aTgLine;
342     convertTangentCurvesToGccEnt(aTgCirc, aTgLine);
343
344     if (aTgCirc.size() + aTgLine.size() != 2)
345       return Circ2dPtr();
346
347     std::shared_ptr<GccAna_Circ2d2TanRad> aCircleBuilder;
348     switch (aTgLine.size()) {
349     case 0:
350       aCircleBuilder = std::shared_ptr<GccAna_Circ2d2TanRad>(new GccAna_Circ2d2TanRad(
351           *aTgCirc[0], *aTgCirc[1], myRadius, Precision::Confusion()));
352       break;
353     case 1:
354       aCircleBuilder = std::shared_ptr<GccAna_Circ2d2TanRad>(new GccAna_Circ2d2TanRad(
355           *aTgCirc[0], *aTgLine[0], myRadius, Precision::Confusion()));
356       break;
357     case 2:
358       aCircleBuilder = std::shared_ptr<GccAna_Circ2d2TanRad>(new GccAna_Circ2d2TanRad(
359           *aTgLine[0], *aTgLine[1], myRadius, Precision::Confusion()));
360       break;
361     default:
362       break;
363     }
364
365     return getProperCircle(aCircleBuilder);
366   }
367
368   Circ2dPtr getProperCircle(const std::shared_ptr<GccAna_Circ2d2TanRad>& theBuilder) const
369   {
370     if (!theBuilder)
371       return Circ2dPtr();
372
373     int aNbSol = theBuilder->NbSolutions();
374     if (aNbSol == 0)
375       return Circ2dPtr();
376
377     int anAppropriateSolution = 1;
378     double aMinDist = Precision::Infinite();
379
380     double aParSol, aPonTgCurve;
381     gp_Pnt2d aTgPnt;
382     for (int i = 1; i <= aNbSol; ++i) {
383       bool isApplicable = false;
384       if (myTangentShapes.size() >= 1) {
385         theBuilder->Tangency1(i, aParSol, aPonTgCurve, aTgPnt);
386         isApplicable = isParamInCurve(aPonTgCurve, myTangentShapes[0]);
387       }
388       if (myTangentShapes.size() >= 2 && isApplicable) {
389         theBuilder->Tangency2(i, aParSol, aPonTgCurve, aTgPnt);
390         isApplicable = isParamInCurve(aPonTgCurve, myTangentShapes[1]);
391       }
392
393       if (isApplicable) {
394         double aDist = distanceToClosestPoint(theBuilder->ThisSolution(i));
395         if (aDist < aMinDist) {
396           anAppropriateSolution = i;
397           aMinDist = aDist;
398         }
399       }
400     }
401
402     return Circ2dPtr(new gp_Circ2d(theBuilder->ThisSolution(anAppropriateSolution)));
403   }
404
405
406   void convertTangentCurvesToGccEnt(VectorOfGccCirc& theTangentCircles,
407                                     VectorOfGccLine& theTangentLines)
408   {
409     theTangentCircles.reserve(3);
410     theTangentLines.reserve(3);
411
412     std::vector<CurveAdaptorPtr>::iterator anIt = myTangentShapes.begin();
413     for (; anIt != myTangentShapes.end(); ++anIt) {
414       switch ((*anIt)->GetType()) {
415       case GeomAbs_Line:
416         theTangentLines.push_back(
417             std::shared_ptr<GccEnt_QualifiedLin>(
418             new GccEnt_QualifiedLin((*anIt)->Line(), GccEnt_unqualified))
419         );
420         break;
421       case GeomAbs_Circle:
422         theTangentCircles.push_back(
423             std::shared_ptr<GccEnt_QualifiedCirc>(
424             new GccEnt_QualifiedCirc((*anIt)->Circle(), GccEnt_unqualified))
425         );
426         break;
427       default:
428         break;
429       }
430     }
431   }
432
433
434   // boundary parameters of curve are NOT applied
435   static bool isParamInCurve(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     return theParameter > theCurve->FirstParameter() &&
443            theParameter < theCurve->LastParameter();
444   }
445
446   // boundary parameters of curve are applied too
447   static bool isParamOnCurve(double& theParameter, const CurveAdaptorPtr& theCurve)
448   {
449     if (theCurve->IsPeriodic()) {
450       theParameter = ElCLib::InPeriod(theParameter,
451                                       theCurve->FirstParameter(),
452                                       theCurve->FirstParameter() + theCurve->Period());
453     }
454     return theParameter >= theCurve->FirstParameter() &&
455            theParameter <= theCurve->LastParameter();
456   }
457
458 private:
459   Handle(Geom_Plane) myPlane;
460   std::shared_ptr<GeomAPI_Pnt2d> myCenter;
461   std::vector<gp_Pnt2d> myPassingPoints;
462   std::vector<CurveAdaptorPtr> myTangentShapes;
463   double myRadius;
464   std::shared_ptr<GeomAPI_Pnt2d> myClosestPoint;
465 };
466
467
468
469
470
471 GeomAlgoAPI_Circ2dBuilder::GeomAlgoAPI_Circ2dBuilder(const std::shared_ptr<GeomAPI_Ax3>& thePlane)
472   : myPlane(thePlane),
473     myRadius(0.)
474 {
475 }
476
477 void GeomAlgoAPI_Circ2dBuilder::setRadius(const double theRadius)
478 {
479   myRadius = theRadius;
480 }
481
482 void GeomAlgoAPI_Circ2dBuilder::setCenter(const std::shared_ptr<GeomAPI_Pnt2d>& theCenter)
483 {
484   myCenter = theCenter;
485 }
486
487 void GeomAlgoAPI_Circ2dBuilder::addTangentCurve(const std::shared_ptr<GeomAPI_Shape>& theEdge)
488 {
489   if (theEdge->isEdge())
490     myTangentShapes.push_back(theEdge);
491 }
492
493 void GeomAlgoAPI_Circ2dBuilder::addPassingPoint(const std::shared_ptr<GeomAPI_Pnt2d>& thePoint)
494 {
495   myPassingPoints.push_back(thePoint);
496 }
497
498 void GeomAlgoAPI_Circ2dBuilder::setClosestPoint(const std::shared_ptr<GeomAPI_Pnt2d>& thePoint)
499 {
500   myClosestPoint = thePoint;
501 }
502
503 std::shared_ptr<GeomAPI_Circ2d> GeomAlgoAPI_Circ2dBuilder::circle(
504     const std::shared_ptr<GeomAPI_Pnt2d>& theFirstPoint,
505     const std::shared_ptr<GeomAPI_Pnt2d>& theSecondPoint,
506     const std::shared_ptr<GeomAPI_Pnt2d>& theThirdPoint)
507 {
508   std::shared_ptr<GeomAPI_Ax3> aPlane(new GeomAPI_Ax3);
509
510   GeomAlgoAPI_Circ2dBuilder aBuilder(aPlane);
511   aBuilder.addPassingPoint(theFirstPoint);
512   aBuilder.addPassingPoint(theSecondPoint);
513   aBuilder.addPassingPoint(theThirdPoint);
514   return aBuilder.circle();
515 }
516
517 std::shared_ptr<GeomAPI_Circ2d> GeomAlgoAPI_Circ2dBuilder::circle()
518 {
519   CircleBuilder aCircleBuilder(myPlane);
520   aCircleBuilder.setCenter(myCenter);
521   aCircleBuilder.setTangentCurves(myTangentShapes);
522   aCircleBuilder.setPassingPoints(myPassingPoints);
523   aCircleBuilder.setClosestPoint(myClosestPoint);
524   aCircleBuilder.setRadius(myRadius);
525   Circ2dPtr aCirc2d = aCircleBuilder.circle();
526
527   std::shared_ptr<GeomAPI_Circ2d> aCircle;
528   if (aCirc2d) {
529     const gp_Pnt2d& aCenter = aCirc2d->Location();
530     const gp_Dir2d& aXAxis = aCirc2d->XAxis().Direction();
531
532     std::shared_ptr<GeomAPI_Pnt2d> aCircleCenter(new GeomAPI_Pnt2d(aCenter.X(), aCenter.Y()));
533     std::shared_ptr<GeomAPI_Dir2d> aCircleDir(new GeomAPI_Dir2d(aXAxis.X(), aXAxis.Y()));
534
535     aCircle = std::shared_ptr<GeomAPI_Circ2d>(
536         new GeomAPI_Circ2d(aCircleCenter, aCircleDir, aCirc2d->Radius()));
537   }
538   return aCircle;
539 }