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

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);

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

問題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 B1=reflect(l)*b, I=intersectionpoint(l,m), B2=rotate(180,I)*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);

 

問題4の解答

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

問題5の解答

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

問題6の解答

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

問題7の解答

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

問題8の解答

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

問題9の解答

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

問題10の解答

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

アポロニウスの円【問題編】

 問題1.3つの点を通る円

 問題2.3本の直線に接する円

 問題3.2つの点を通り直線に接する円

 問題4.1つの点を通り2本の直線に接する円

 問題5.1つの円と2本の直線に接する円

 問題6.2つの点を通り1つの円に接する円

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

 問題8.2つの円と1本の直線に接する円

 問題9.1つの点を通り2つの円に接する円

 問題10.3つの円に接する円

Asymptote 習作編

for 文も使えます

ソース

import graph;

size(200,0);

for (real m=0;m<8;m+=1) {
  real f(real x) {return -1*x +m;}
  draw(graph(f,-1,m+1,operator ..));

  for (real n=7-m;n>-1;n-=1) {
    dot((m,n));
  }
}

xaxis("$x$");
yaxis("$y$");

label("$O$",(0,0),SW);

labelx(1,S); labelx(7,S);
labely(1,W);labely(7,W);

 Asymptote  きみに

ほれました。

 つきあいが長くなるな。そう感じました。

 君ならどんなのでも作図してくれそうだな。そう思いました。

 作図してもらいたい。そう思いました。

 おたがいがよく知りあう期間が必要だな。そう思いました。

 
 

 たくさんのサンプルたちがありました。

 そうか。君はこんなことがこんなに簡単にできちゃうんだ。

 あるとき問題クンが現れました。

 話してみたら,『2点を通り,一つの直線に接する円をかけ』だということに気づきました。

 サンプルたちの中によい香りを放つ図形クンがありました。

 その図形クンが答えを持っていました。

 
 

 立体クンがやってきました。

 サンプルクンたちに聞いてみることにしました。

 えっ!

 きみは!

 うごくんだ!