コンピュータ、ソフトウェアに関すること。忘れないようにね。

アポロニウスの円【解答編】

問題1の解答

【問題1】3つの点を通る円

ソース

import geometry;

size(6cm,0);

point A=(0,0) , B=(4,0) , C=(3,5);

circle c= circle(A,B,C);
draw(c);

dot("$A$",A,SW);
dot("$B$",B,E);
dot("$C$",C,NE);

問題2の解答

【問題2】3本の直線に接する円

 この問題は,3本の直線がそれぞれ交点を持ち三角形ができる場合と,3本のうち2本が平行になる場合があります。

 (1)3本の直線がそれぞれ交点を持ち三角形ができる場合

 解答は,三角形の内接円と,傍接円の計4個あります。

ソース

import geometry;

size(20cm,0);

// 三つの直線の交点を指定
point A=(0,0) , B=(8,0) , C=(3,5);
// 三本の直線を描画
line l1=line(A,B) , l2=line(B,C) , l3=line(C,A);
draw(l1);draw(l2);draw(l3);

// 内接円を描画
circle c= incircle(A,B,C);
draw(c);

dot("$A$",A,S+2W);
dot("$B$",B,S+3E);
dot("$C$",C,2N);

// 円の中心を描画
dot("$I$",c.C,E);
// 接点を描画
point[] P=intersectionpoints(l1,c);
dot("$P$",P[0],S);
point[] Q=intersectionpoints(l2,c);
dot("$Q$",Q[0],NE);
point[] R=intersectionpoints(l3,c);
dot("$R$",R[0],NW);

// 三角形の傍接円
triangle t=triangle(A,B,C);

circle ec=excircle(t.AB);
clipdraw(ec, 0.8green);
dot(ec.C, green);

ec=excircle(t.AC);
clipdraw(ec, 0.8green);
dot(ec.C, green);

ec=excircle(t.BC);
clipdraw(ec, 0.8green);
dot(ec.C, green);

 直線が先に与えられている場合には,

point A=intersectionpoint(l2,l3);

のように交点を求めてやればよいでしょう。


 (2)3本のうち2本が平行になる場合
 この場合の解答は,2個の接円になります。

 もっとエレガントな解答があるかと思いますが,ズバリという関数が見つからなかったので,角の二等分線を作り,その交点から接円の中心を求めてみました。

ソース

import geometry;

/*
  2本の平行線に挟まれる接円を返す関数

  点Pを中心に,直線pl1とl3の二等分線を90度回転させている。
 */
circle pararelLineIntersectionCircle( point P , line pl1 ,line pl2 , line l3){
  // 2本の角の二等分線から,その交点を求める。その交点が接円の中心
  line b1 = rotate(90,P)*bisector(l3,pl1);
  line b2 = bisector(l3,pl2);
  point pC = intersectionpoint(b1,b2);

  // 直径となる,平行線と円の接点を求める
  line m=perpendicular(pC,pl2);
  point[] M;
  M[0] = intersectionpoint(pl1,m);
  M[1] = intersectionpoint(pl2,m);
  // 接円を返す
  return circle(M[0],M[1]);
}

size(10cm,0);

// 三つの直線を指定する点
point A=(0,0) , B=(8,0) , C=(0,6) , D=(5,6);
// 三本の直線を描画
line l1=line(A,B) , l2=line(C,D) , l3=line(B,D);
draw(l1);draw(l2);draw(l3);
// 点を描画
dot("$A$",A,2S);
dot("$B$",B,S+2E);
dot("$C$",C,2N);
dot("$D$",D,NE);

// 接円を取得
circle c = pararelLineIntersectionCircle(D,l2,l1,l3);
draw(c);
// 円の中心を描画
dot("$I$",c.C,E);
// 接点を描画
point [] M;
M[0] = intersectionpoints(c,l1)[0];
M[1] = intersectionpoints(c,l2)[0];
M[2] = intersectionpoints(c,l3)[0];
dot("$P$",M[0],S);
dot("$Q$",M[1],N);
dot("$R$",M[2],NE);

// 反対側の接円を取得
circle c = pararelLineIntersectionCircle(B,l1,l2,l3);
draw(c);
// 円の中心を描画
dot("$I'$",c.C,E);
// 接点を描画
M[0] = intersectionpoints(c,l1)[0];
M[1] = intersectionpoints(c,l2)[0];
M[2] = intersectionpoints(c,l3)[0];
dot("$P'$",M[0],S);
dot("$Q'$",M[1],N);
dot("$R'$",M[2],SW);

問題3の解答

【問題3】2つの点を通り直線に接する円
 この問題は,与えられた2点でできる直線が,与えられた直線と平行になる場合と平行にならない場合があります。

 平行になる場合には,解答となる円は一つに定まりますが,平行にならないときは二つの円が解答になります。
 【問題2】の時にはソースが二つになりましたが,今回は,一つのソースで両方の場合に対処できるようにしてみました。そのために,if 文を使っています。

 (1)与えられた2点でできる直線が,与えられた直線と平行になる場合

 (2)与えられた2点でできる直線が,与えられた直線と平行にならない場合

 どちらも一つのソースで,指定する点の座標を変えるだけで作図することができます。

import geometry;

/* =======================================================================
 *
 * 2点を通り1つの直線に接する円を返す
 *
 *     2点でできる直線が,与えられた直線と平行な時は
 *    2点の垂直二等分線と,与えられた直線との交点を求め,
 *    円を通る3番目の点としています。
 *
 *     2点でできる直線が,与えられた直線と平行でない時は
 *    2個の接円が得られるので,int select を使って選べるようにしました。
 */
circle circlePPL(point a, point b, line l, int select = 0 )
{
  if(select > 0) select = 1;
  else select = 0;
  
  point[] P;
  line  m = line(a,b);
  if( parallel(l , m) ) {
    select = 0;
    line lm = bisector(a,b);
    P[select] = intersectionpoint(l,lm);
  } else {
    point K=intersectionpoint(l,m), B1=rotate(180,K)*b ,B2=reflect(l)*b;
    circle cI = circle(a,B1,B2);
    P = intersectionpoints(l,cI);
  }
  return circle(a,b,P[select]);
}
/* ======================================================================= */

size(10cm,0);

// 円に接するべき直線を与える
point pL1=(0,0), pL2=(5,0);
line l = line(pL1,pL2);

// 円が通るべき2点を与える
point pA=(1,1), pB=(2,3);

/*
 * 2点を通り1つの直線に接する円を受け取り描画
 *
 *   for ループで回すために,本筋と関係ない余分な変数を使っています
 */
int k=2;
if ( parallel(l, line(pA,pB)) ) k=1;

circle c;
point[] pT;
for(int i=0;i<k;i+=1) {
  // 接円を取得して描画
  c = circlePPL(pA, pB, l , i);
  dot(format("$O_{%i}$",i+1) , c.C , E);
  draw(c);

  // 直線との接点を描画
  pT = intersectionpoints(c,l);
  dot(format("$T_{%i}$",i+1) ,pT[0], 2S);
}

// 与えられている2点と直線を描画
draw("$l$",l);
dot("$A$",pA,2W);
dot("$B$",pB,N+E);

  作図方法は,

● 直線ABと直線lとの交点をKとし,点Kについて点Bと対称な点をB1とする。
● 直線lについて点Bと対称な点をB2とする。
● このとき,3点A,B1,B2を通る円と直線lとの交点T1,T2が,求める円が直線lと接する接点となる。

 なぜ,これで作図ができるのか「?」でしたが,やっと証明することができました。
証明に興味がある方はぜひご覧ください。

アポロニウスの【問題3】の作図ができるわけ

問題4の解答

【問題4】1つの点を通り2本の直線に接する円

点Aを通り、直線OPとOQに接する円は2個あります。

ソース

import geometry;
size(10cm,0);

/* =======================================================================
 *
 * 1点を通り、2直線に接する円を返す
 *
 */
circle circlePLL(point A, line l1, line l2, int select = 0){ 
  if(select > 0) select = 1;
  else select = 0;

  line m = bisector(l1,l2);
  line n1 = perpendicular(l1.B , l1);
  point cP1 = intersectionpoint(m,n1);

  circle c1 = circle(cP1,length(segment(l1.B, cP1)));

  line lA = line(A, intersectionpoint(l1 , l2));
  point[] clA = intersectionpoints(lA, c1);
  line n = parallel(A, line(l1.B, clA[select]));
  point cP = intersectionpoint(l1, n);

  line n2 = perpendicular(cP , l1);
  point cP2 = intersectionpoint(m,n2);

  return circle(cP2,length(segment(cP, cP2)));
}


// 円に接するべき2直線を与える
point pL11=(0,0), pL12=(5,0);
line l1 = line(pL11,pL12);
point pL21=(0,0), pL22=(6,5);
line l2 = line(pL21,pL22);

// 円が通るべき1点を与える
point A=(4,2);

circle c;
for (int i = 0;i < 2; i+=1){
  c = circlePLL(A, l1, l2, i);
  draw(c); 
  dot(format("$C_{%i}$",i+1) , c.C , S);
}
dot("$A$",A,NE);
dot("$O$",pL11, NW);
dot("$P$",pL12, S);
dot("$Q$",pL22, NW);
draw(l1);
draw(l2);

問題5の解答

【問題5】1つの円と2本の直線に接する円

問題6の解答

【問題6】2つの点を通り1つの円に接する円

問題7の解答

【問題7】1つの点を通り1つの円と1本の直線に接する円

問題8の解答

【問題8】2つの円と1本の直線に接する円

問題9の解答

【問題9】1つの点を通り2つの円に接する円

問題10の解答

【問題10】3つの円に接する円