bool GetTangentsToCircles ( const Circle2<Real>& circle0, const Circle2<Real>& circle1, Line2<Real> line[4] ) { Vector2<Real> W = circle1.Center - circle0.Center; Real wLenSqr = W.SquaredLength(); Real rSum = circle0.Radius + circle1.Radius; if ( wLenSqr <= rSum * rSum ) { // Circles are either intersecting or nested. return false; } Real r0Sqr = circle0.Radius * circle0.Radius; Real tmp, a; Real rDiff = circle1.Radius - circle0.Radius; if ( Math<Real>::FAbs( rDiff ) >= Math<Real>::ZERO_TOLERANCE ) { // Solve (R1^2-R0^2)*s^2 + 2*R0^2*s - R0^2 = 0.. Real r1Sqr = circle1.Radius * circle1.Radius; Real c0 = -r0Sqr; Real c1 = ( ( Real )2 ) * r0Sqr; Real c2 = circle1.Radius * circle1.Radius - r0Sqr; Real minusHalfInvC2 = ( ( Real ) - 0.5 ) / c2; Real discr = Math<Real>::FAbs( c1 * c1 - ( ( Real )4 ) * c0 * c2 ); Real root = Math<Real>::Sqrt( discr ); Real s, oneMinusS; // Get the first root. s = ( c1 + root ) * minusHalfInvC2; line[0].Origin = circle0.Center + s * W; line[1].Origin = line[0].Origin; if ( s >= ( Real )0.5 ) { tmp = Math<Real>::FAbs( wLenSqr - r0Sqr / ( s * s ) ); a = Math<Real>::Sqrt( tmp ); } else { oneMinusS = ( Real )1 - s; tmp = Math<Real>::FAbs( wLenSqr - r1Sqr / ( oneMinusS * oneMinusS ) ); a = Math<Real>::Sqrt( tmp ); } GetDirections( W, a, line[0].Direction, line[1].Direction ); // Get the second root. s = ( c1 - root ) * minusHalfInvC2; line[2].Origin = circle0.Center + s * W; line[3].Origin = line[2].Origin; if ( s >= ( Real )0.5 ) { tmp = Math<Real>::FAbs( wLenSqr - r0Sqr / ( s * s ) ); a = Math<Real>::Sqrt( tmp ); } else { oneMinusS = ( Real )1 - s; tmp = Math<Real>::FAbs( wLenSqr - r1Sqr / ( oneMinusS * oneMinusS ) ); a = Math<Real>::Sqrt( tmp ); } GetDirections( W, a, line[2].Direction, line[3].Direction ); } else { // Circles effectively have same radius. // Midpoint of circle centers. Vector2<Real> mid = ( ( Real )0.5 ) * ( circle0.Center + circle1.Center ); // Tangent lines passing through midpoint. tmp = Math<Real>::FAbs( wLenSqr - ( ( Real )4 ) * r0Sqr ); a = Math<Real>::Sqrt( tmp ); GetDirections( W, a, line[0].Direction, line[1].Direction ); line[0].Origin = mid; line[1].Origin = mid; // Normalize W. W /= Math<Real>::Sqrt( wLenSqr ); // Tangent lines parallel to normalized W: // 1. D = W // 2. a. P = mid+R0*perp(W), perp(a,b) = (b,-a) // b. P = mid-R0*perp(W) line[2].Origin.X() = mid.X() + circle0.Radius * W.Y(); line[2].Origin.Y() = mid.Y() - circle0.Radius * W.X(); line[2].Direction = W; line[3].Origin.X() = mid.X() - circle0.Radius * W.Y(); line[3].Origin.Y() = mid.Y() + circle0.Radius * W.X(); line[3].Direction = W; } return true; }
bool GetTangentsToCircles (const Circle2<Real>& rkCircle0, const Circle2<Real>& rkCircle1, Line2<Real> akLine[4]) { Vector2<Real> kW = rkCircle1.Center - rkCircle0.Center; Real fWLenSqr = kW.SquaredLength(); Real fRSum = rkCircle0.Radius + rkCircle1.Radius; if (fWLenSqr <= fRSum*fRSum) { // circles are either intersecting or nested return false; } Real fR0Sqr = rkCircle0.Radius*rkCircle0.Radius; Real fTmp, fA; Real fRDiff = rkCircle1.Radius - rkCircle0.Radius; if (Math<Real>::FAbs(fRDiff) >= Math<Real>::ZERO_TOLERANCE) { // solve (R1^2-R0^2)*s^2 + 2*R0^2*s - R0^2 = 0. Real fR1Sqr = rkCircle1.Radius*rkCircle1.Radius; Real fC0 = -fR0Sqr; Real fC1 = ((Real)2.0)*fR0Sqr; Real fC2 = rkCircle1.Radius*rkCircle1.Radius - fR0Sqr; Real fMHalfInvC2 = ((Real)-0.5)/fC2; Real fDiscr = Math<Real>::FAbs(fC1*fC1 - ((Real)4.0)*fC0*fC2); Real fRoot = Math<Real>::Sqrt(fDiscr); Real fS, fOmS; // first root fS = (fC1 + fRoot)*fMHalfInvC2; akLine[0].Origin = rkCircle0.Center + fS*kW; akLine[1].Origin = akLine[0].Origin; if (fS >= (Real)0.5) { fTmp = Math<Real>::FAbs(fWLenSqr - fR0Sqr/(fS*fS)); fA = Math<Real>::Sqrt(fTmp); } else { fOmS = (Real)1.0 - fS; fTmp = Math<Real>::FAbs(fWLenSqr - fR1Sqr/(fOmS*fOmS)); fA = Math<Real>::Sqrt(fTmp); } GetDirections(kW,fA,akLine[0].Direction,akLine[1].Direction); // second root fS = (fC1 - fRoot)*fMHalfInvC2; akLine[2].Origin = rkCircle0.Center + fS*kW; akLine[3].Origin = akLine[2].Origin; if (fS >= (Real)0.5) { fTmp = Math<Real>::FAbs(fWLenSqr - fR0Sqr/(fS*fS)); fA = Math<Real>::Sqrt(fTmp); } else { fOmS = (Real)1.0 - fS; fTmp = Math<Real>::FAbs(fWLenSqr - fR1Sqr/(fOmS*fOmS)); fA = Math<Real>::Sqrt(fTmp); } GetDirections(kW,fA,akLine[2].Direction,akLine[3].Direction); } else { // circles effectively have same radius // midpoint of circle centers Vector2<Real> kMid = ((Real)0.5)*(rkCircle0.Center+rkCircle1.Center); // tangent lines passing through midpoint fTmp = Math<Real>::FAbs(fWLenSqr - ((Real)4.0)*fR0Sqr); fA = Math<Real>::Sqrt(fTmp); GetDirections(kW,fA,akLine[0].Direction,akLine[1].Direction); akLine[0].Origin = kMid; akLine[1].Origin = kMid; // normalize W kW /= Math<Real>::Sqrt(fWLenSqr); // tangent lines parallel to normalized W // 1. D = W // 2. a. P = mid+R0*perp(W), perp(a,b) = (b,-a) // b. P = mid-R0*perp(W) akLine[2].Origin.X() = kMid.X() + rkCircle0.Radius*kW.Y(); akLine[2].Origin.Y() = kMid.Y() - rkCircle0.Radius*kW.X(); akLine[2].Direction = kW; akLine[3].Origin.X() = kMid.X() - rkCircle0.Radius*kW.Y(); akLine[3].Origin.Y() = kMid.Y() + rkCircle0.Radius*kW.X(); akLine[3].Direction = kW; } return true; }