satoh

知っているようで知らないデジタル図形処理 ~任意形状図形の選択から交点・接点・接線計算まで、方程式では解けないノウハウを紹介

   

2つの円の接線を求める

2円の接線のパターン

2つの円に引かれる接線を求めてみましょう。
2円の接線が存在する条件としては、
・片方の円がもう片方の円内部に完全に含まれないこと
となります。2円の接線-存在しない(内包円)
上図の場合は接線存在はしません。当然のことながら、

2円の接線-存在しない(同心円) 上図のような同心円もそれに含まれます。
では存在するパターンはどのようなものが考えられるでしょうか。
・2円が重なりあう場合
・2円が離れている場合
が考えられます。以下参照してください。
2円の接線-2本
上図は「2円が重なりあう場合」です。この場合、接線は2種類、接点は4点存在します。
2円の接線-4本
上図は「2円が離れている場合」です。この場合、接線は4種類、接点は8点存在します。
このように図示してみるとわかりやすいのですが、いざプログラムで求めようとすると結構面倒になります。

プログラム

ごちゃごちゃ説明するよりもプログラムで示すほうがいいでしょう。
早速プログラム書いてみます。このプログラムでは浮動小数点演算の例外処理をあえて書いてないので、利用時は注意してください。
また求める接線は、接点群として結果を返します。関数自体の返値は接点数です。

#define         GEO_ERR_01       5.0            // 同心円か否かの判定距離
#define         GEO_ERR_02       5.0            // 接しているか否かの判定距離

#define         FABS(x)         ((x) < 0.0 ? -(x) : (x))

typedef struct {
    double      x ;
    double      y ;
} FPOINT ;

typedef struct {
    FPOINT      pc ;
    double      r ;
} FCIRCLE ;

//---------------------------------------------------------------------------------------------
//
//      円と円の接点を求める(接線)
//
//---------------------------------------------------------------------------------------------
int geoGetTangentOf2CC(
						FCIRCLE cc1,            // 第1円
						FCIRCLE cc2,            // 第2円
						FPOINT  *pt)            // 接点群(接線群をなしている)
{
	int    ind = 0 ;
	int    loop ;
	double dx, dy, cr, d1, d2, d3, sn, cs ;
	double xr[8], yr[8] ;

	if  (cc1.r <= 0.0 || cc2.r <= 0.0) goto ret ;

	dx = cc2.pc.x - cc1.pc.x ;
	dy = cc2.pc.y - cc1.pc.y ;
	cr = sqrt(dx * dx + dy * dy) ;

	if  (cr <= GEO_ERR_01) goto ret ;

	d1 = cc1.r - cc2.r - cr ;
	d2 = cc2.r - cc1.r - cr ;
	d3 = cr - cc1.r - cc2.r ;

	if  (     d1  >  GEO_ERR_02 ||      d2  >  GEO_ERR_02) goto ret ;
	if  (FABS(d1) <= GEO_ERR_02 || FABS(d2) <= GEO_ERR_02) goto ret ;

	if  (d3 > GEO_ERR_02) {
		ind = 8 ;
	} else {
		ind = 4 ;
	}

	xr[0]  = cc1.r * (cc1.r - cc2.r) / cr ;
	yr[0]  = sqrt(cc1.r * cc1.r - xr[0] * xr[0]) ;

	xr[1]  = cc2.r * (cc1.r - cc2.r) / cr ;
	yr[1]  = sqrt(cc2.r * cc2.r - xr[1] * xr[1]) ;
	xr[1] += cr ;

	xr[2]  =  xr[0] ;
	yr[2]  = -yr[0] ;

	xr[3]  =  xr[1] ;
	yr[3]  = -yr[1] ;

	if  (ind == 8) {
		xr[4] =  cc1.r * (cc1.r + cc2.r) / cr ;
		yr[4] = sqrt(cc1.r * cc1.r - xr[4] * xr[4]) ;

		xr[5]  = -cc2.r * (cc1.r + cc2.r) / cr ;
		yr[5]  = -sqrt(cc2.r * cc2.r - xr[5] * xr[5]) ;
		xr[5] += cr ;

		xr[6]  =  xr[4] ;
		yr[6]  = -yr[4] ;

		xr[7]  =  xr[5] ;
		yr[7]  = -yr[5] ;
	}

	sn  = dy / cr ;
	cs  = dx / cr ;

	for (loop = 0 ; loop < ind ; ++loop) {
		pt[loop].x = xr[loop] * cs - yr[loop] * sn + cc1.pc.x ;
		pt[loop].y = xr[loop] * sn + yr[loop] * cs + cc1.pc.y ;
	}

ret:;
	return ind ;
}

 - C, アルゴリズム, 交点・接線, 図形, 計算