01 値・式の取得
この節では,図形からいろいろな値を取得する方法を考えます。
スライダ
スライダを作ってインタラクティブに値を取得したり,角や長さを求めたりします。
作図ツールで線分を描き,その上に点をとります。点は線分上だけを動きます。
インスペクタで両端の点を小さくし,「ピンで留める」にチェックを入れます。これがないと,線分上の点が端に来たとき,端点と一緒に動いてしまったりします。
線のラベルは非表示にしておきましょう。
スクリプトを書きます。スライダでどのように値を取得するかは,場合によって違いますので,次は一例です。
このスライダで,-5から5までの値を取得して,変数 a に代入します。
a=|A,C|/|A,B|*10-5;
|A,C| は点Aと点Cの距離を表します。距離の比を10倍して5を引けば,-5から5までになります。
取得した値は必要に応じて任意の場所に表示することができます。次の図では点の下に表示しています。
スライダの端点と,スライダ上の点の距離を利用していますので,スライダは水平である必要はありません。垂直でも,斜めにでも置けます。
スライダの長さも自由です。
円形スライダ
角度を取得するとき,直線のスライダよりは,円形のスライダの方が,角のイメージをつかみやすいでしょう。
Cinderellaでは,円周上にとった点は,その位置を点の属性 .angle で取得することができます。点がPであれば,P.angle です。返される角は 0°から360°です。
まず「半径つき円を描く」ツールで円を描き,その上に点をとります。これだけでスライダはできるのですが,わかりやすくするために,角度0のところに点をとり,中心とこの点をインスペクタで「ピンで留める」にしておきます。サイズも小さくしましょう。
th=B.angle;
で変数thに角度が入ります。
円形スライダで一般角を取得することもできます。図は同じですが,さらに,「角度を測る」ツールで角CABを測ります。表示メニューの「式による表示」を開きますと,角の名前が α0 であることがわかります。
この α0 を利用します。Text0 は角度の表示ですが,これはいらないので,選択して消去してかまいません。
なお,α は,日本語入力モードで入力します。
このスライダを使うと,円周上の点を動かしながら y=sinθ のグラフを描いていく図が簡単にできます。
スクリプトは
th=α0;
drawtext(B.xy+[0.1,0.1],th,size->16);
plot(sin(t),start->0,stop->th);
あとは,軸に目盛の数字を入れるなど,適当に体裁を整えるとよいでしょう。
角度を求める
描画面に描いた図形の内角を求めます。
前項で,円形スライダから角度を求める方法を描きました。あらためて整理しておきます。
円周上の点が表す角 : 範囲は 0°~360°
円周上にとった点は,その位置を点の属性 .angle で取得することができます。
「角度を測る」ツールを使う : 範囲は一般角
2つの線分(直線)がなす角を「角度を測る」ツールで測り,その識別子(αなど)を利用すると,一般角で取得できます。
この他の方法です。
arctan2(ベクトル) を用いる : 範囲は位置関係による
x軸の正の角と,原点を始点とするベクトルがなす角を返します。
線分(直線)のなす角を測るには
th=arctan2(C-A)-arctan2(B-A);
とします。
arctan2() が返す範囲は範囲は -π〜π です。
上のようにして∠BACを測った場合,A,B,Cの座標平面での位置関係によって th が変わるので注意が必要です。
複素数を利用する :範囲は -π〜π
複素数平面でなす角を測るときの式を利用します。
// 平面上の点p1,p2,p3 に対し、∠p1p2p3を返す。
angle3pt(p1,p2,p3):=(
regional(z1,z2,z3,z);
z1=complex(p1);
z2=complex(p2);
z3=complex(p3);
if(z1==z2,
0,
z=(z3-z2)/(z1-z2);
arctan2(gauss(z));
);
);
結果は -π〜πで返されます。前項の場合と異なり,A,B,Cの相対的な位置関係で角が決まります。
点から直線に下した垂線の足の座標
作図ツールを用いずに,計算で,垂線の足Hの座標を求めます。
ベクトルを利用します。
AB方向の単位ベクトルとベクトルACの内積を求めると AH の長さになります。
位置ベクトルを使って a+AH(b-a) とすればHの位置ベクトルが求められます。(a,bはA,Bの位置ベクトル)
// 2点p1,p2を通る直線へ点p3から下した垂線の足の座標
footofperp(p1,p2,p3):=(
regional(t,uv,foot);
if(|p2-p1|!=0,
uv=(p2-p1)/|p2-p1|;
t=(p3-p1)*uv;
foot=p1+t*uv;
,
println("Warning:p1 is same to p2");
);
foot;
);
なお,p1,p2が等しかったときのエラー処理もしています。
点と直線の距離
前項の,垂線の足の座標を用いて,点Cとの距離を求めます。
// 2点p1,p2を通る直線と点p3の距離。
distlp(p1,p2,p3):=(
abs(footofperp(p1,p2,p3)-p3);
);
2点を通る直線の方程式
有理数を座標とする2点を通る直線の方程式の係数を ax+by+c=0 の形で求めて,そのリスト[a,b,c]を返します。原理は簡単で,2点を (x1,y1),(x2,y2) としたとき,連立方程式
ax1+by1+c=0, ax2+by2+c=0
の解を求めればよいのです。
ただし,未知数がa,b,c の3つに対して式は2つですので,解は確定しません。定数項 c の値を,たとえば c=1 としてしまえば a,b の値が決まります。
この程度の連立方程式なら数式処理を使うまでもないので,手で解いてしまいましょう。
a=(y1-y2)/(x1y2-x2y1) , b=(x1-x2)/(x2y1-x1y2)
ですね。(ただし,2点のx座標が等しいときは a=1,y=0,c=-x1 です)
これを整数化するために,a,b,c すべてに x1y2-x2y1 をかけると
a=y1-y2 , b=x2-x1 , c=x1y2-x2y1
となります。
lineonpoint(p1,p2):=(
regional(x1,x2,y1,y2);
x1=p1.x;
y1=p1.y;
x2=p2.x;
y2=p2.y;
if(x1==x2,
[1,0,-x1];
,
[y1-y2,x2-x1,x1*y2-x2*y1];
);
);
【使用例】
直線 ax+by+c=0 を描く drawline() と合わせて使います。
drawline() については,「03 関数のグラフと曲線」を参照してください。
(1) 2点 (-3,0),(0,2) を通る直線
coef=lineonpoint([-3,0],[0,2]);
drawline(coef,[-6,6]);
まとめて
drawline(lineonpoint([-3,0],[0,2]),[-6,6]);
としてしまうこともできます。
(2) 2点 (3,0),(3,2) を通る直線
coef=lineonpoint([-3,0],[0,2]);
drawline(coef,[-6,6]);
なお,作図ツールで作図した点 A,B を通る直線の場合は
coef=lineonpoint(A,B);
とできます。
A,B を通る直線の方程式を取得してなんらかの計算を行うときに使えます。
2直線の交点
2直線が方程式で与えられている場合
2直線の交点を求めるには1次の連立方程式を解くことになるので,行列を使って計算できます。Cindyscriptには,それが linearsolve() という関数で用意されています。これを使ってみましょう。
連立方程式
a1x+b1y+c1=0 , a2x+b2y+c2=0
は, a1x+b1y=-c1 , a2x+b2y=-c2 と変形して,Ax=B という形にして解きます。Aが係数の行列,Bが右辺の列ベクトルです。この行列AとベクトルBを引数として linearsolve(A,B) とすると,解が返されます。
そこで,2直線の係数をリストで与えて解を返す関数を作ります。
// 2直線 a1x+b1y+c1=0 ,a2x+b2y+c2=0の交点を求める。
// coef1,coef2 はそれぞれの係数リスト
intersectline(coef1,coef2):=(
regional(mat,vec);
mat=[[coef1_1,coef1_2],[coef2_1,coef2_2]];
vec=[-coef1_3,-coef2_3];
linearsolve(mat,vec);
);
これだけのことなので,直接 linearsovle() を使ってもよく,わざわざ関数にすることもなさそうですが,intersectline() という名前にしておくのがなにかと便利なのです。
2直線が通る点で与えられている場合
p1,p2を通る直線と,p3,p4を通る直線の交点を求めます。
p1,p2を通る直線上の点は,ベクトル方程式で(座標をベクトルと同一視して)
p=(1-s)p1+s p2
と表されます。
同様に,p3,p4を通る直線上の点は,
p=(1-t)p3+t p4
と表されます。
したがって,
(1-s)p1+s p2=(1-t)p3+t p4
となり,整理すると
(p2-p1)s+(p3-p4)t=p3-p1
となります。
これをx座標と,y座標で表せば連立方程式ができます。その一方の解 を使って
p=(1-s)p1+s p2
から点pが求められます。
interll(p1,p2,p3,p4):=(
regional(mata,matb,x);
mata=[[p2_1-p1_1,p3_1-p4_1],[p2_2-p1_2,p3_2-p4_2]];
matb=[p3_1-p1_1,p3_2-p1_2];
x=linearsolve(mata,matb);
(1-x_1)*p1+x_1*p2;
);