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

Asymptote 《関数編》

line bisector() -- 二等分線を作る -- import geometry

line bisector(point , point) :二つの点でできる線分の垂直二等分線を作る

point P=(4,0) , Q=(0,5);
dot("$P$",P,E);
dot("$Q$",Q,W);

line m = bisector(P,Q);
draw(m);

となれば,垂直の記号を図に書き込みたくなります。

line p = line(P,Q);
point M = intersectionpoint(p,m);
markrightangle(p.A , M , m.A , size=5mm);

draw(P--Q , dashed);

のように,markrightangle(point , point , point) 関数を使えば実現できます。生成された直線で直線上の点があらわでなくても,m.A のように指定することができます。
 ついでに,PM=QM を表す記号もつけたくなります。
 上記の draw 関数の引数を下記のように変えます。また,点M も表示します。

draw(P--Q,dashed, StickIntervalMarker(2,2));

dot("$M$",M ,2S+.5W);

 P--Q のように線分を表すのは無骨すぎると思うときには, segment(P,Q) のように表すこともできます。
 StickIntervalMarker(int , int) 関数の第1引数は,線分の分割数を指定しています。二等分するときには "2" を指定します。第2引数は,記号の線の本数を指定しています。"3" にすれば3本線の記号が表示されます。

line bisector(line , line) :二つの直線でできる角のうち,指定した2直線で時計回りにできる角の二等分線を作る

point P=(0,4), Q=(5,0);
line p = line(P,Q);
dot("$P$",P,E+.5N);dot("$Q$",Q,E+.5N);draw("$p$",p);
line l = line((0,0),(5,3));
draw(l);

point M = intersectionpoint(p,l);

line m = bisector(p,l);
draw("$m$",m);

dot("$M$",M ,2S);

 もう一方の角の二等分線は,line bisector(line , line , real) 関数の第3引数を "90" にして,90度回転させることで得ることができます。

line m = bisector(p,l,90);
draw("$m$",m);

dot("$M$",M ,2W);

 次に,角が等しい記号を書いてみましょう。

  • 直角を表す関数は,
    markrightangle(point , point , point)
    でした。
  • 弧を描いて等しい角を表す関数は,
    markangle(point , point , point , StickIntervalMarker(i=2,n=1))
    です。

 markangle 関数で,弧を描き,StickIntervalMarker 関数で,短い線分を描きます。
 StickIntervalMarker 関数の引数のうち,"i=2" は,指定された角を分割して描く線分の個数です。もう一つの引数 "n=1" は,短い線分の本数を指定します。

markangle(l.A , M , p.B , StickIntervalMarker(i=2,n=1));

 弧の半径が大きいようです。半径を半分に,また,描く弧を2重線にしてみましょう。

markangle(2 , radius=0.5*markangleradius() ,
          l.A , M , p.B , StickIntervalMarker(i=2,n=1));

 弧を描くためだけに,2直線 l と p の交点 M を求めていました。交点を使わず,2直線で角を指定できます。

markangle(2 , radius=0.5*markangleradius() ,
          l , p , StickIntervalMarker(i=2,n=1));

 直線 l の起点が逆のようです。そこで,

markangle(2 , radius=0.5*markangleradius() ,
          reverse(l) , p , StickIntervalMarker(i=2,n=1));


 うまくできました。


line perpendicular() -- 垂線を作る -- import geometry

line perpendicular(point , line) :point を通り,line に垂直な直線を作る

triangle triangle() -- 三角形を作る -- import geometry

triangle t = triangle(point , point , point) :三点を指定して三角形を作る


point P=(4,0) , Q=(0,1) , R=(3.5,3);
triangle t = triangle(P,Q,R);
show(t);

 のように,インスタンス t を作った場合でも,各点の名前は引数の順に点A,点B,点Cとなり,辺を参照する変数もそれぞれ t.BC , t.CA , t.AB となります。また,点に向かい合う辺に小文字の名前 "a","b","c" が与えられます。

triangle t = triangle(real , real , real) :三辺の長さを指定して三角形を作る


triangle t = triangle(3 , 4 , 5);
show(t);

circle circle();-- 円を作る -- import geometry

  • circle circle(point , real):中心 と 半径 で円を作る
  • circle circle(point , point):直径とする2点で円を作る
  • circle circle(point , point , point):3点を通る円を作る
  • circle incircle(point , point , point): 3点でできる三角形の内接円を作る

全て1度に作ってしまいます。

3点A,B,C,を指定し、点Aを中心とする円がC1、辺ACを直径とする円がC2、三角形の外接円がC3そして、三角形の内接円がC4です。


import geometry;
size(7cm,0);

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

triangle t = triangle(A,B,C);
draw(t, red);
dot("$A$", A,S+.5W);
dot("$B$", B,W);
dot("$C$", C);

circle c1 = circle(A, 1);
draw(c1);
dot("$C_1$",c1.C,N);

circle c2 = circle(A, C);
draw(c2);
dot("$C_2$",c2.C,NE);

circle c3=circle(A,B,C);
draw(c3);
dot("$C_3$",c3.C,2W);

circle c4=incircle(A,B,C);
draw(c4);
dot("$C_4$",c4.C,N);

line tangent();-- 円の接線を作る -- import geometry

円cの中心 c.C と円の中心以外の点Pを結ぶ半直線と円cとの交点Tは、

point T = point(c, P);

で得ることができます。



import geometry;
unitsize(1cm);

circle c = circle((0,0), (3,0));
point P = (2,0);

point T = point(c, P);

draw(c);
dot("$c.C$", c.C, W);
dot("$P$", P);

dot("$T$", T);


2種類の接線を引きます。

円周上の1点に接する接線と、円の外側の1点から引く接線です。

  • line tangent(circle, point) :半直線c.CPと円との交点での接線を作る
  • line[] tangents(circle, point) :外部の点Pを通る接線を作る



import geometry;
unitsize(1cm);

circle c = circle((0,0), 2);
point P = (5,-3);

point T = point(c, P);
// 点Tを接点とする接線を求める。
line l = tangent(c, P);
draw("$l$", l);

// 外部の点Pを通る接線の、接点を求める。
point[] Tm = intersectionpoints(c, circle(c.C, P));

line[] m = tangents(c, P);

draw(m[0]);
dot("$T_1$", Tm[0], S);
draw(m[1]);
dot("$T_2$", Tm[1], NE);

draw(c);
dot("$c.C$", c.C, W);

dot("$T$", T, W);
dot("$P$", P, S+W);

座標軸

座標軸の表示の基本

 まずは座標軸だけを描いて見ましょう。以後は、縦10cm、横10cm の大きさに固定します。

unitsize(x=1cm);
import graph;  

real Xmin = -5, Xmax = 5;
real Ymin = -5, Ymax = 5;

xlimits( Xmin, Xmax);
ylimits( Ymin, Ymax);
xaxis();
yaxis();

labelx("$O$",0,SW);
dot((0,0));

 まず矢印とx、yの表示を加えてみましょう。

xaxis(Label("$x$",align=2E), Arrow(2mm));
yaxis(Label("$y$",align=2N), Arrow(2mm));

 次は、目盛りをつけます。

xaxis(Label("$x$",align=2E), Ticks(modify=NoZero, begin=false, end=true), Arrow(2mm));

 おや?矢印の表示のために5mmほど先端が延びて、目盛りがついてしまうようですね。
では、Xmax の値を少し減らしてみましょう。

xlimits( Xmin, Xmax - .1);

 ついでに、y軸のほうは目盛りだけで、数値なしにしてみます。

yaxis(Label("$y$",align=2N), Ticks("%", begin=false, end=true),Arrow(2mm));

 目盛りの線の長さを調節したいですね。
長いほうは、『Size=‥』
短いほうは、『size=‥』
で指定できます。

xaxis(Label("$x$",align=2E), Ticks(modify=NoZero, begin=false, end=true, Size=1mm, size=.7mm), Arrow(2mm));

 ここまでのソースを書いておきます。

unitsize(x=1cm);
import graph;  

real Xmin = -5, Xmax = 5;
real Ymin = -5, Ymax = 5;

xlimits( Xmin, Xmax - .1);
ylimits( Ymin, Ymax - .1);
xaxis(Label("$x$",align=2E), Ticks(modify=NoZero, begin=false, end=true, Size=1mm, size=.7mm), Arrow(2mm));
yaxis(Label("$y$",align=2N), Ticks("%", begin=false, end=true),Arrow(2mm));

labelx("$O$",0,SW);
dot((0,0));

関数のグラフ

関数の定義
real f(real x) {return x^2/5;}

のように定義できます。

 グラフを描画するには、
graph(f,Xmin,Xmax)
を、draw()の引き数に与えます。

draw(graph(f,Xmin,Xmax));

 ソース

unitsize(x=1cm);
import graph;  

/************************************************************
 *  座標軸の描画を関数定義しました。
 *         使用する前に定義しておく必要があります。
 ************************************************************/
void myAxis(real Xmin, real Xmax, real Ymin, real Ymax) {
  xlimits( Xmin, Xmax - .1);
  ylimits( Ymin, Ymax - .1);
  xaxis(Label("$x$",align=2E), Ticks(modify=NoZero, begin=false, end=true, Size=1mm, size=.7mm), Arrow(2mm));
  yaxis(Label("$y$",align=2N), Ticks(modify=NoZero, begin=false, end=true, Size=1mm, size=.7mm), Arrow(2mm));
  labelx("$O$",0,SW);
  dot((0,0));
}
/***********************************************************/

real Xmin = -5, Xmax = 5;
real Ymin = -5, Ymax = 5;


real f(real x) {return x^2/5;}
draw(graph(f,Xmin,Xmax),red);

myAxis(Xmin, Xmax, Ymin, Ymax);

Asymptote 《関数編》 目次

 使ったことがある関数について,まとめておくことにします。

 書き順は基本的にランダムですが,引数が異なるオーバーロードされている関数は一つにまとまるように記述していきます。(少しずつ追加編集していきます。)

geometry

line bisector() -- 二等分線を作る

1. line bisector(point , point) :二つの点でできる線分の垂直二等分線を作る

    • markrightangle(point , point , point , size=5mm) :直角記号
    • StickIntervalMarker(int , int) :等しい線分を表す記号

 
2. line bisector(line , line) :二つの直線でできる角のうち,小さい角の内側の二等分線を作る

    • markangle(point , point , point) :角を表す記号
    • markangle(point , point , point , StickIntervalMarker(i=2,n=1)) :角の二等分を表す記号
    • markangle(line , line) :角を表す記号
    • markangle(int , line , line) :角を表す記号(弧の本数を指定)
    • markangle( radius=0.5*markangleradius() , line , line) :角を表す記号(弧の半径を指定)
line perpendicular() -- 垂線を作る

1. line perpendicular(point , line) :point を通り,line に垂直な直線を作る

triangle triangle() -- 三角形を作る

1. triangle t = triangle(point , point , point) :三点を指定して三角形を作る

2. triangle t = triangle(real , real , real) :三辺の長さを指定して三角形を作る

circle circle() -- 円を作る

1. circle c = circle(point , real):中心 と 半径 で円を作る

2. circle c = circle(point , point):直径とする2点で円を作る

3. circle circle(point , point , point):3点を通る円を作る

4. circle incircle(point , point , point): 3点でできる三角形の内接円を作る

line tangent();-- 円の接線を作る

1. point T = point(c, P):円の中心 c.C と点Pを結ぶ半直線と円cとの交点T

2. line tangent(circle, point) :半直線c.CPと円との交点での接線を作る

3. line[] tangents(circle, point) :外部の点Pを通る接線を作る



hatena-mode

 円の接線を引くこと。

これは,だいぶ前に調べて分かっていたのですが,
(私のホームページ http://www.denpoya.org では公開していました。)
はてな」の編集がめんどくさくて,サボっていました。

 Emacs の 素晴らしい「はてなの編集モード」を見つけることができました。

http://d.hatena.ne.jp/tarao/20130110/1357821338

 WindowsのEmacs24にインストールするのに手間どってしまいましたが,(flimのmakeでした。makeの環境を整理する必要がありました。)使えるようになり,たいへん楽になりました。

 たいへん感謝しております。これからは,バリバリ(たぶん,ウソ)更新していけるかな,と思います。

アポロニウス【問題5】の解答

問題5の解答

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

幾何的に作図をしようと思ったのですが,うまい方法が見つからず,解析的に作図をすることにしました。その結果,ソースがかなりプログラムになってしまいました。わかりにくいかとは思いますが,ご容赦ください。

(1) 2本の直線が平行になる場合

(2) 2本の直線が平行にならない場合



(1) 2本の直線が平行になる場合


ソース

import geometry;
unitsize(1cm);

/**
 * 2直線が平行な場合
 *    この2直線の距離が求める接円の直径となる。
 *  そこで,接円の中心をx軸上とし,与えられる円の中心をy軸上とした。
 */

// 2直線と円を与える
real r = 3, R = 2, Y = 2.5;

circle cG = circle((0,Y), R);
line l1 = line((-R,r),(R,r));  // y=r
line l2 = line((-R,-r),(R,-r));// y=-r

// 接円の中心を求める
real x = sqrt((R+r)^2-Y^2);
circle c1 = circle((x,0),r);
circle c2 = circle((-x,0),r);

draw(l1);draw(l2);draw(cG);
draw(c1, red);
dot("$C_1$",c1.C);
draw(c2, red);
dot("$C_2$",c2.C);


(2) 2本の直線が平行にならない場合


ソース

import geometry;
size(0,10cm);

/**
 * 2直線が平行でない場合
 *    この2直線の交点を原点とする。
 *  接円の中心は、2等分線上にある。これをx軸とする。
 *  与えられる2直線は、y=mx , y=-mx で表される。
 *
 *    接円があまり大きくならないように注意しましょう。
 */

// 2直線と円を与える
/**
 *  円が2直線の上側にある
 *real m = 1, R = .5, X = 1, Y = 2;
 */
/**
 *  円が直線のひとつと接している
 *real m = 1, R = sqrt(2)/2, X = 2, Y = 1;
 */
/**
 *  円が直線のひとつと2点で交わる
 *real m = 1, R = 1, X = 2, Y = 1;
 */
/**
 *  円が2直線の右側にある
 *real m = 1, R = .5, X = 1, Y = 0;
 */
/**
 *  円が2直線でできる4つの領域にまたがる
 *real m = 1, R = 1, X = 0, Y =0;
 */

real m = 1, R = 1, X = 0, Y =0;

real sin = m/sqrt(1+m^2);
real cos = 1/sqrt(1+m^2);
// 2直線と円を描く
circle cG = circle((X,Y), R);
line l1 = line((0,0),(1,m));  // y=mx
line l2 = line((0,0),(1,-m)); // y=-mx
draw(l1);draw(l2);draw(cG);

// 判別式
real discriminant(real R, real X, real Y, real sin, real cos){
  real D = (X+R*sin)^2 - cos^2*(X^2+Y^2-R^2);

  return D;
}

/**
 *  接円の中心を求める
 */
real [] zcal(real X, real Y, real sin, real cos){
  real[] x={0,0,0,0};
  if(Y==0){
    x[0] = (X-R)/(1-sin);
    x[1] = (X+R)/(1-sin);
    x[2] = (X-R)/(1+sin);
    x[3] = (X+R)/(1+sin);
  }else{
    real D = discriminant(R, X, Y, sin, cos);
    if(D>=0){
      x[0] = (X+R*sin - sqrt(D))/(cos^2);
      x[1] = (X+R*sin + sqrt(D))/(cos^2);
    }
    real D = discriminant(-R, X, Y, sin, cos);
    if(D>=0){
      x[2] = (X-R*sin - sqrt(D))/(cos^2);
      x[3] = (X-R*sin + sqrt(D))/(cos^2);
    }
  }
  return x;
}
/**
 *  接円を描画する
 */
void drawC(real R, real X, real Y, real sin, real cos, int i=0){
  circle c1,c2;
  real[] z = zcal(X,Y,sin,cos);
  if(i==0){
    if(z[0]!=0){
      c1 = circle((z[0],0),z[0]*sin);
      draw(c1, red);
      dot(format("$C_{%i1}$",i),c1.C);
      if(z[1]!=z[0]){
	c2 = circle((z[1],0),z[1]*sin);
	draw(c2, red);
	dot(format("$C_{%i2}$",i),c2.C);
      }
    }
    if(z[2]!=0){
      c1 = circle((z[2],0),z[2]*sin);
      draw(c1, red);
      dot(format("$C_{%i3}$",i),c1.C);
      if(z[3]!=z[2]){
	c2 = circle((z[3],0),z[3]*sin);
	draw(c2, red);
	dot(format("$C_{%i4}$",i),c2.C);
      }
    }
  }
  z = zcal(Y,X,cos,sin);
  if(z[0]!=0){
    i=1;
    c1 = circle((0,z[0]),z[0]*cos);
    draw(c1, blue);
    dot(format("$C_{%i1}$",i),c1.C);
    if(z[1]!=z[0]){
      c2 = circle((0,z[1]),z[1]*cos);
      draw(c2, blue);
      dot(format("$C_{%i2}$",i),c2.C);
    }
  }
  if(z[2]!=0){
    i=1;
    c1 = circle((0,z[2]),z[2]*cos);
    draw(c1, blue);
    dot(format("$C_{%i3}$",i),c1.C);
    if(z[3]!=z[2]){
      c2 = circle((0,z[3]),z[3]*cos);
      draw(c2, blue);
      dot(format("$C_{%i4}$",i),c2.C);
    }
  }
  
}

real D = discriminant(R, X, Y, sin, cos);
if(D >= 0){
  drawC(R, X, Y, sin, cos);
}else if(D < 0){
  drawC(R, X, Y, sin, cos, 1);
}

Asymptote 《関数編》 目次

 使ったことがある関数について,まとめておくことにします。

 書き順は基本的にランダムですが,引数が異なるオーバーロードされている関数は一つにまとまるように記述していきます。(少しずつ追加編集していきます。)

geometry

line bisector() -- 二等分線を作る

1. line bisector(point , point) :二つの点でできる線分の垂直二等分線を作る

    • markrightangle(point , point , point , size=5mm) :直角記号
    • StickIntervalMarker(int , int) :等しい線分を表す記号

 
2. line bisector(line , line) :二つの直線でできる角のうち,小さい角の内側の二等分線を作る

    • markangle(point , point , point) :角を表す記号
    • markangle(point , point , point , StickIntervalMarker(i=2,n=1)) :角の二等分を表す記号
    • markangle(line , line) :角を表す記号
    • markangle(int , line , line) :角を表す記号(弧の本数を指定)
    • markangle( radius=0.5*markangleradius() , line , line) :角を表す記号(弧の半径を指定)
line perpendicular() -- 垂線を作る

1. line perpendicular(point , line) :point を通り,line に垂直な直線を作る

triangle triangle() -- 三角形を作る

1. triangle t = triangle(point , point , point) :三点を指定して三角形を作る

2. triangle t = triangle(real , real , real) :三辺の長さを指定して三角形を作る

circle circle() -- 円を作る

1. circle c = circle(point , real):中心 と 半径 で円を作る

2. circle c = circle(point , point):直径とする2点で円を作る

3. circle circle(point , point , point):3点を通る円を作る

4. circle incircle(point , point , point): 3点でできる三角形の内接円を作る

line tangent();-- 円の接線を作る

1. point T = point(c, P):円の中心 c.C と点Pを結ぶ半直線と円cとの交点T

2. line tangent(circle, point) :半直線c.CPと円との交点での接線を作る

3. line[] tangents(circle, point) :外部の点Pを通る接線を作る



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

 アポロニウスの【問題3】『2点を通り一つの直線に接する円』の作図方法は,

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

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

証明




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

問題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つの円に接する円