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