1 // Copyright (c) 1995-1999 Matra Datavision
2 // Copyright (c) 1999-2014 OPEN CASCADE SAS
4 // This file is part of Open CASCADE Technology software library.
6 // This library is free software; you can redistribute it and / or modify it
7 // under the terms of the GNU Lesser General Public version 2.1 as published
8 // by the Free Software Foundation, with special exception defined in the file
9 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
10 // distribution for complete text of the license and disclaimer of any warranty.
12 // Alternatively, this file may be used under the terms of Open CASCADE
13 // commercial license or contractual agreement.
15 //=========================================================================
16 // Straight line tangent to two circles or tangent to a circle and passing +
18 //=========================================================================
20 #include <GccAna_Lin2d2Tan.ixx>
24 #include <gp_Dir2d.hxx>
25 #include <gp_Vec2d.hxx>
26 #include <gp_Circ2d.hxx>
27 #include <Standard_OutOfRange.hxx>
28 #include <StdFail_NotDone.hxx>
29 #include <GccEnt_BadQualifier.hxx>
31 //=========================================================================
32 // Straight line passing through two points. +
33 // =============================== +
34 //=========================================================================
37 GccAna_Lin2d2Tan (const gp_Pnt2d& ThePoint1,
38 const gp_Pnt2d& ThePoint2 ,
39 const Standard_Real Tolerance ):
51 Standard_Real Tol = Abs(Tolerance);
52 WellDone = Standard_False;
54 Standard_Real dist = ThePoint1.Distance(ThePoint2);
55 qualifier1(1) = GccEnt_noqualifier;
56 qualifier2(1) = GccEnt_noqualifier;
58 gp_Dir2d dir(ThePoint2.X()-ThePoint1.X(),ThePoint2.Y()-ThePoint1.Y());
59 linsol(1) = gp_Lin2d(ThePoint1,dir);
60 // ===================================
61 WellDone = Standard_True;
63 pnttg1sol(1) = ThePoint1;
64 pnttg2sol(1) = ThePoint2;
65 par1sol(NbrSol)=ElCLib::Parameter(linsol(NbrSol),pnttg1sol(NbrSol));
66 par2sol(NbrSol)=ElCLib::Parameter(linsol(NbrSol),pnttg2sol(NbrSol));
72 //=========================================================================
73 // Straight line tangent to a circle passing by a point. +
74 // ================================================= +
75 // Basing on the qualifier attached to circle Qualified1 (C1) define +
76 // the direction of the tangent to be calculated. +
77 // This tangent will have connection point P1 (point of tangency with the circle. +
78 // It has angle A (sinus R1/dist or -R1/dist) with straight line +
79 // passing by the center of the circle and ThePoint. +
80 //=========================================================================
83 GccAna_Lin2d2Tan (const GccEnt_QualifiedCirc& Qualified1,
84 const gp_Pnt2d& ThePoint ,
85 const Standard_Real Tolerance ):
97 Standard_Real Tol = Abs(Tolerance);
98 WellDone = Standard_False;
100 if (!(Qualified1.IsEnclosed() || Qualified1.IsEnclosing() ||
101 Qualified1.IsOutside() || Qualified1.IsUnqualified())) {
102 GccEnt_BadQualifier::Raise();
105 gp_Circ2d C1 = Qualified1.Qualified();
106 Standard_Real R1 = C1.Radius();
108 if (Qualified1.IsEnclosed()) { GccEnt_BadQualifier::Raise(); }
109 // ============================
110 else if (Tol < R1-ThePoint.Distance(C1.Location())) {
111 WellDone = Standard_True;
113 else if (Abs(ThePoint.Distance(C1.Location())-R1) <= Tol) {
114 gp_Dir2d dir(gp_Vec2d(C1.Location(),ThePoint));
115 linsol(1) = gp_Lin2d(ThePoint,gp_Dir2d(Standard_Real(-dir.Y()),
116 Standard_Real(dir.X())));
117 // =====================================================================
118 qualifier1(1) = Qualified1.Qualifier();
119 qualifier2(1) = GccEnt_noqualifier;
120 WellDone = Standard_True;
122 pnttg1sol(1) = ThePoint;
123 pnttg2sol(1) = ThePoint;
126 Standard_Real signe = 1;
127 Standard_Real dist = ThePoint.Distance(C1.Location());
128 Standard_Real d = dist - Sqrt(dist*dist - R1*R1);
129 if (Qualified1.IsEnclosing()) {
130 // =============================
134 else if (Qualified1.IsOutside()) {
138 else if (Qualified1.IsUnqualified()) {
139 // ====================================
143 for (Standard_Integer i = 1 ; i <= NbrSol ; i++) {
144 gp_Pnt2d P1(C1.Location().Rotated(ThePoint,ASin(signe*R1/dist)));
145 gp_Dir2d D1(gp_Vec2d(P1,ThePoint));
146 P1=gp_Pnt2d(P1.XY() + d*D1.XY());
147 linsol(i) = gp_Lin2d(P1,gp_Dir2d(gp_Vec2d(P1,ThePoint)));
148 // ========================================================
149 qualifier1(i) = Qualified1.Qualifier();
150 qualifier2(i) = GccEnt_noqualifier;
152 pnttg2sol(i) = ThePoint;
155 WellDone = Standard_True;
157 for (Standard_Integer i = 1 ; i <= NbrSol ; i++) {
158 par1sol(i)=ElCLib::Parameter(linsol(i),pnttg1sol(i));
159 par2sol(i)=ElCLib::Parameter(linsol(i),pnttg2sol(i));
160 pararg1(i)=ElCLib::Parameter(C1,pnttg1sol(i));
165 //=========================================================================
166 // Straight line tangent to two circles. +
167 // ==================================== +
168 // In the boundary cas (two interior circles are tangent to each other) +
169 // take the straight line orthogonal to the straight line connecting +
171 // In other cases subject the center of C1 (Qualified1) or of +
172 // C2 (Qualified2), provided that R1 is greater than R2, to a +
173 // rotation of angle A with sinus(A) = (R1+R2)/dist or +
174 // sinus(A) = (R1-R2)/dist or +
175 // sinus(A) = (R2-R1)/dist or +
176 // sinus(A) = (-R1-R2)/dist +
177 // The point found this way is P1 or P2. +
178 // The direction of the straight line to be calculated should pass by +
179 // the center of rotation (center of C1 or of C2) and P1 or P2. +
180 // Then translate the straight line to make it tangent to C1. +
181 //=========================================================================
184 GccAna_Lin2d2Tan (const GccEnt_QualifiedCirc& Qualified1,
185 const GccEnt_QualifiedCirc& Qualified2,
186 const Standard_Real Tolerance ):
198 Standard_Real Tol = Abs(Tolerance);
199 WellDone = Standard_False;
201 if (!(Qualified1.IsEnclosed() || Qualified1.IsEnclosing() ||
202 Qualified1.IsOutside() || Qualified1.IsUnqualified()) ||
203 !(Qualified2.IsEnclosed() || Qualified2.IsEnclosing() ||
204 Qualified2.IsOutside() || Qualified2.IsUnqualified())) {
205 GccEnt_BadQualifier::Raise();
208 gp_Circ2d C1 = Qualified1.Qualified();
209 gp_Circ2d C2 = Qualified2.Qualified();
211 if (Qualified1.IsEnclosed() || Qualified2.IsEnclosed()) {
212 // =======================================================
213 GccEnt_BadQualifier::Raise();
216 Standard_Real R1 = C1.Radius();
217 Standard_Real R2 = C2.Radius();
219 Standard_Integer signe = 1;
220 Standard_Real dist = C1.Location().Distance(C2.Location());
221 if (Tol < Max(R1,R2)-dist-Min(R1,R2) ) { WellDone = Standard_True; }
222 else if (!Qualified1.IsUnqualified() || !Qualified2.IsUnqualified()) {
223 // ====================================================================
224 if (Qualified1.IsEnclosing() && Qualified2.IsEnclosing()) {
225 // =========================================================
226 if (Abs(dist+Min(R1,R2)-Max(R1,R2)) <= Tol && dist >= Tol) {
227 if (R1<R2) { D1 = gp_Dir2d(gp_Vec2d(C2.Location(),C1.Location()));}
228 else { D1 = gp_Dir2d(gp_Vec2d(C1.Location(),C2.Location())); }
229 gp_Pnt2d P1(C1.Location().XY()+R1*D1.XY());
230 linsol(1) = gp_Lin2d(P1,gp_Dir2d(-D1.Y(),D1.X()));
231 // =================================================
232 qualifier1(1) = Qualified1.Qualifier();
233 qualifier2(1) = Qualified2.Qualifier();
236 WellDone = Standard_True;
240 gp_Pnt2d P1(C2.Location().Rotated(C1.Location(),
241 ASin((R1-R2)/dist)));
242 D1 = gp_Dir2d(gp_Vec2d(C1.Location(),P1));
243 P1=gp_Pnt2d((C1.Location().XY()+gp_XY(R1*D1.Y(),-R1*D1.X())));
244 linsol(1) = gp_Lin2d(P1,D1);
245 // ===========================
246 qualifier1(1) = Qualified1.Qualifier();
247 qualifier2(1) = Qualified1.Qualifier();
249 pnttg2sol(1) = gp_Pnt2d(C2.Location().XY()+
250 gp_XY(R2*D1.Y(),-R2*D1.X()));
251 WellDone = Standard_True;
255 else if ((Qualified1.IsEnclosing() && Qualified2.IsOutside()) ||
256 // ================================================================
257 (Qualified2.IsEnclosing() && Qualified1.IsOutside())) {
258 // =====================================================
259 if (Qualified1.IsEnclosing() && Qualified2.IsOutside()) {
265 if (R1+R2-dist > Tol) { WellDone = Standard_True; }
266 else if (Abs(dist-R1-R2)<Tol && dist>Tol) {
267 D1 = gp_Dir2d(gp_Vec2d(C1.Location(),C2.Location()));
268 gp_Pnt2d P1(C1.Location().XY()+R1*D1.XY());
269 linsol(1) = gp_Lin2d(P1,gp_Dir2d(-D1.Y(),D1.X()));
270 // =================================================
271 qualifier1(1) = Qualified1.Qualifier();
272 qualifier2(1) = Qualified1.Qualifier();
275 WellDone = Standard_True;
279 gp_Pnt2d P1(C2.Location().Rotated(C1.Location(),
280 ASin(signe*(R1+R2)/dist)));
281 D1 = gp_Dir2d(gp_Vec2d(C1.Location(),P1));
282 P1=gp_Pnt2d(C1.Location().XY()+signe*(gp_XY(R1*D1.Y(),
284 linsol(1) = gp_Lin2d(P1,D1);
285 // ===========================
286 qualifier1(1) = Qualified1.Qualifier();
287 qualifier2(1) = Qualified1.Qualifier();
289 pnttg2sol(1) = gp_Pnt2d(C2.Location().XY()+
290 signe*(gp_XY(-R2*D1.Y(),R2*D1.X())));
291 WellDone = Standard_True;
295 else if (Qualified1.IsOutside() && Qualified2.IsOutside()) {
296 // =========================================================
297 if (Abs(dist+Min(R1,R2)-Max(R1,R2)) < Tol && dist > Tol) {
298 if (R1<R2) { D1 = gp_Dir2d(gp_Vec2d(C2.Location(),C1.Location()));}
299 else { D1 = gp_Dir2d(gp_Vec2d(C1.Location(),C2.Location())); }
300 linsol(1) = gp_Lin2d(gp_Pnt2d(C1.Location().XY()+R1*D1.XY()),
301 // =============================================================
302 gp_Dir2d(D1.Y(),-D1.X()));
303 // =========================
304 qualifier1(1) = Qualified1.Qualifier();
305 qualifier2(1) = Qualified1.Qualifier();
306 pnttg1sol(1) = gp_Pnt2d(C1.Location().XY()+R1*D1.XY());
307 pnttg2sol(1) = pnttg1sol(1);
308 WellDone = Standard_True;
312 gp_Pnt2d P1(C2.Location().Rotated(C1.Location(),
313 ASin((R2-R1)/dist)));
314 D1 = gp_Dir2d(gp_Vec2d(C1.Location(),P1));
315 P1 = gp_Pnt2d(C1.Location().XY()+gp_XY(-R1*D1.Y(),R1*D1.X()));
316 linsol(1) = gp_Lin2d(P1,D1);
317 // ===========================
318 qualifier1(1) = Qualified1.Qualifier();
319 qualifier2(1) = Qualified1.Qualifier();
321 pnttg2sol(1) = gp_Pnt2d(C2.Location().XY()+
322 (gp_XY(-R2*D1.Y(),R2*D1.X())));
323 WellDone = Standard_True;
328 if ((Qualified1.IsUnqualified() && Qualified2.IsEnclosing()) ||
329 // ===============================================================
330 (Qualified2.IsUnqualified() && Qualified1.IsEnclosing())) {
331 // =========================================================
332 if (Qualified2.IsUnqualified()) { signe = 1; }
334 if (Abs(dist+Min(R1,R2)-Max(R1,R2)) < Tol && dist > Tol) {
335 if (R1<R2) { D1=gp_Dir2d(gp_Vec2d(C2.Location(),C1.Location()));}
336 else { D1 = gp_Dir2d(gp_Vec2d(C1.Location(),C2.Location())); }
337 linsol(1) = gp_Lin2d(gp_Pnt2d(C1.Location().XY()+R1*D1.XY()),
338 // =============================================================
339 gp_Dir2d(-D1.Y(),D1.X()));
340 // =========================
341 qualifier1(1) = Qualified1.Qualifier();
342 qualifier2(1) = Qualified1.Qualifier();
343 pnttg1sol(1) = gp_Pnt2d(C1.Location().XY()+R1*D1.XY());
344 pnttg2sol(1) = pnttg1sol(1);
345 WellDone = Standard_True;
349 gp_Pnt2d P1(C2.Location().Rotated(C1.Location(),
350 ASin((R1-R2)/dist)));
351 D1 = gp_Dir2d(gp_Vec2d(C1.Location(),P1));
352 P1 = gp_Pnt2d(C1.Location().XY()+gp_XY(R1*D1.Y(),-R1*D1.X()));
353 linsol(1) = gp_Lin2d(P1,D1);
354 // ===========================
355 qualifier1(1) = Qualified1.Qualifier();
356 qualifier2(1) = Qualified1.Qualifier();
358 pnttg2sol(1) = gp_Pnt2d(C2.Location().XY()+
359 signe*(gp_XY(R2*D1.Y(),-R2*D1.X())));
360 WellDone = Standard_True;
362 if (Min(R1,R2)+Max(R1,R2)<dist) {
363 gp_Pnt2d P2(C2.Location().Rotated(C1.Location(),
364 ASin(signe*(R1+R2)/dist)));
365 gp_Dir2d D2(gp_Vec2d(C1.Location(),P2));
366 P2=gp_Pnt2d(C1.Location().XY()+
367 signe*(gp_XY(R1*D2.Y(),-R1*D2.X())));
368 linsol(2) = gp_Lin2d(P2,D2);
369 // ===========================
370 qualifier1(1) = Qualified1.Qualifier();
371 qualifier2(1) = Qualified1.Qualifier();
373 pnttg2sol(2) = gp_Pnt2d(C2.Location().XY()+
374 (gp_XY(-R2*D2.Y(),R2*D2.X())));
379 else if ((Qualified1.IsUnqualified() && Qualified2.IsOutside()) ||
380 // ==================================================================
381 (Qualified2.IsUnqualified() && Qualified1.IsOutside())) {
382 // =======================================================
383 if (Qualified2.IsUnqualified()) { signe = 1; }
385 if (Abs(dist+Min(R1,R2)-Max(R1,R2)) <= Tol && dist >= Tol) {
386 if (R1<R2) { D1=gp_Dir2d(gp_Vec2d(C2.Location(),C1.Location()));}
387 else { D1 = gp_Dir2d(gp_Vec2d(C1.Location(),C2.Location())); }
388 linsol(1) = gp_Lin2d(gp_Pnt2d(C1.Location().XY()+R1*D1.XY()),
389 // =============================================================
390 gp_Dir2d(D1.Y(),-D1.X()));
391 // =========================
392 qualifier1(1) = Qualified1.Qualifier();
393 qualifier2(1) = Qualified1.Qualifier();
394 pnttg1sol(1) = gp_Pnt2d(C1.Location().XY()+R1*D1.XY());
395 pnttg2sol(1) = pnttg1sol(1);
396 WellDone = Standard_True;
400 gp_Pnt2d P1(C2.Location().Rotated(C1.Location(),
401 ASin(signe*(R2-R1)/dist)));
402 D1 = gp_Dir2d(gp_Vec2d(C1.Location(),P1));
403 P1 = gp_Pnt2d(C1.Location().XY()+gp_XY(-R1*D1.Y(),R1*D1.X()));
404 linsol(1) = gp_Lin2d(P1,D1);
405 // ===========================
406 qualifier1(1) = Qualified1.Qualifier();
407 qualifier2(1) = Qualified1.Qualifier();
409 pnttg2sol(1) = gp_Pnt2d(C2.Location().XY()+
410 signe*(gp_XY(-R2*D1.Y(),R2*D1.X())));
411 WellDone = Standard_True;
413 if (Min(R1,R2)+Max(R1,R2)<dist) {
414 gp_Pnt2d P2(C2.Location().Rotated(C1.Location(),
415 ASin(signe*(-R2-R1)/dist)));
416 gp_Dir2d D2(gp_Vec2d(C1.Location(),P2));
417 P2=gp_Pnt2d(C1.Location().XY()+
418 signe*(gp_XY(-R1*D2.Y(),R1*D2.X())));
419 linsol(2) = gp_Lin2d(P2,D2);
420 // ===========================
421 qualifier1(1) = Qualified1.Qualifier();
422 qualifier2(1) = Qualified1.Qualifier();
424 pnttg2sol(2) = gp_Pnt2d(C2.Location().XY()+
425 signe*(gp_XY(R2*D2.Y(),-R2*D2.X())));
433 for (Standard_Integer i = 1 ; i <= 2 ; i++) {
436 gp_Pnt2d P1(C2.Location().Rotated(C1.Location(),
437 ASin(signe*(R2-R1)/dist)));
438 D1 = gp_Dir2d(gp_Vec2d(C1.Location(),P1));
439 P1 = gp_Pnt2d(C1.Location().XY()+signe*gp_XY(-R1*D1.Y(),R1*D1.X()));
440 linsol(NbrSol) = gp_Lin2d(P1,D1);
441 // ===========================
442 qualifier1(NbrSol) = Qualified1.Qualifier();
443 qualifier2(NbrSol) = Qualified1.Qualifier();
444 pnttg1sol(NbrSol) = P1;
445 pnttg2sol(NbrSol) = gp_Pnt2d(C2.Location().XY()+
446 signe*(gp_XY(-R2*D1.Y(),R2*D1.X())));
447 WellDone = Standard_True;
448 if (Min(R1,R2)+Max(R1,R2)<dist) {
449 gp_Pnt2d P2(C2.Location().Rotated(C1.Location(),
450 ASin(signe*(R2+R1)/dist)));
451 gp_Dir2d D2(gp_Vec2d(C1.Location(),P2));
452 P2=gp_Pnt2d(C1.Location().XY()+
453 signe*(gp_XY(R1*D2.Y(),-R1*D2.X())));
455 linsol(NbrSol) = gp_Lin2d(P2,D2);
456 // ================================
457 qualifier1(NbrSol) = Qualified1.Qualifier();
458 qualifier2(NbrSol) = Qualified1.Qualifier();
459 pnttg1sol(NbrSol) = P2;
460 pnttg2sol(NbrSol) = gp_Pnt2d(C2.Location().XY()+
461 signe*(gp_XY(-R2*D2.Y(),R2*D2.X())));
466 for (Standard_Integer i = 1 ; i <= NbrSol ; i++) {
467 par1sol(i)=ElCLib::Parameter(linsol(i),pnttg1sol(i));
468 par2sol(i)=ElCLib::Parameter(linsol(i),pnttg2sol(i));
469 pararg1(i)=ElCLib::Parameter(C1,pnttg1sol(i));
470 pararg2(i)=ElCLib::Parameter(C2,pnttg2sol(i));
474 Standard_Boolean GccAna_Lin2d2Tan::
475 IsDone () const { return WellDone; }
477 Standard_Integer GccAna_Lin2d2Tan::
478 NbSolutions () const { return NbrSol; }
480 gp_Lin2d GccAna_Lin2d2Tan::
481 ThisSolution (const Standard_Integer Index) const {
483 if (Index > NbrSol || Index <= 0) { Standard_OutOfRange::Raise(); }
484 return linsol(Index);
487 void GccAna_Lin2d2Tan::
488 WhichQualifier(const Standard_Integer Index ,
489 GccEnt_Position& Qualif1 ,
490 GccEnt_Position& Qualif2 ) const
492 if (!WellDone) { StdFail_NotDone::Raise(); }
493 else if (Index <= 0 ||Index > NbrSol) { Standard_OutOfRange::Raise(); }
495 Qualif1 = qualifier1(Index);
496 Qualif2 = qualifier2(Index);
500 void GccAna_Lin2d2Tan::
501 Tangency1 (const Standard_Integer Index,
502 Standard_Real& ParSol,
503 Standard_Real& ParArg,
504 gp_Pnt2d& PntSol) const {
505 if (!WellDone) { StdFail_NotDone::Raise(); }
506 else if (Index <= 0 ||Index > NbrSol) { Standard_OutOfRange::Raise(); }
508 ParSol = par1sol(Index);
509 ParArg = pararg1(Index);
510 PntSol = gp_Pnt2d(pnttg1sol(Index));
514 void GccAna_Lin2d2Tan::
515 Tangency2 (const Standard_Integer Index ,
516 Standard_Real& ParSol ,
517 Standard_Real& ParArg ,
518 gp_Pnt2d& PntSol ) const {
519 if (!WellDone) { StdFail_NotDone::Raise(); }
520 else if (Index <= 0 ||Index > NbrSol) { Standard_OutOfRange::Raise(); }
522 ParSol = par2sol(Index);
523 ParArg = pararg2(Index);
524 PntSol = gp_Pnt2d(pnttg2sol(Index));