高校数学をプログラミングで解く(数学B編)「2-6 空間における平面・直線の方程式」
マガジンリスト > 数学B編 2.空間のベクトル > 2-6 空間における平面・直線の方程式
はじめに
今回は、数学Bで学ぶ「空間における平面・直線の方程式」について、平面の方程式に基づいた平面や直線の方程式に基づいた直線を描くプログラムを作成します。
空間における平面・直線の方程式
まず、空間における平面・直線の方程式についてまとめておきます。
平面の方程式
$${\vec{n}=(a,b,c) \neq \vec{0}}$$、平面上の任意の点を$${\mathrm{P}(x,y,z)}$$、また$${\mathrm{A}(x_1,y_1,z_1)}$$とします。
① 点$${\mathrm{A}}$$を通り、$${\vec{n}}$$に垂直な平面($${\overrightarrow{\mathrm{OP}}=\vec{p}, \overrightarrow{\mathrm{OA}}=\vec{a}}$$とします。)
$$
\vec{n} \cdot \overrightarrow{\mathrm{AP}}=0 \\ \vec{n} \cdot (\vec{p}-\vec{a})=0 \\ a(x-x_1)+b(y-y_1)+c(z-z_1)=0
$$
② 一般形
$$
ax+by+cz+d=0
$$
は平面を表し、法線ベクトルは$${\vec{n}=(a,b,c)}$$となる。
直線の方程式
$${\vec{d}=(l,m,n) \neq \vec{0}}$$、直線上の任意の点を$${\mathrm{P}(x,y,z)}$$、また$${\mathrm{A}(x_1,y_1,z_1)}$$、$${\mathrm{B}(x_2,y_2,z_2)}$$とします。
① 点$${\mathrm{A}}$$を通り、$${\vec{d}}$$に平行な直線($${\overrightarrow{\mathrm{OP}}=\vec{p}, \overrightarrow{\mathrm{OA}}=\vec{a}}$$とします。)
$$
\begin{array}{lll}
\vec{p}=\vec{a}+t\vec{d} & \Leftrightarrow & x=x_1+lt, y=y_1+mt, z=z_1+nt & \mathrm{(成分表示)} \\
& \Leftrightarrow & lmn \neq 0 \mathrm{のとき} & \\
& & \frac{x-x_1}{l}=\frac{y-y_1}{m}=\frac{z-z_1}{n}(=t) & \mathrm{(} t \mathrm{を消去)}
\end{array}
$$
② 2点$${\mathrm{A,B}}$$を通る直線($${\overrightarrow{\mathrm{OP}}=\vec{p}, \overrightarrow{\mathrm{OA}}=\vec{a}, \overrightarrow{\mathrm{OB}}=\vec{b}}$$とします。)
$$
\vec{p}=\vec{a}+t(\vec{b}-\vec{a})=(1-t)\vec{a}+t\vec{b}
$$
平面の方程式に関する問題
まず、平面の方程式に関する問題を解くプログラムを作成していきます。
平面の方程式に関する問題
次の点$${\mathrm{A}}$$を通り、ベクトル$${\vec{n}}$$に垂直な平面の方程式を求め、空間上に描け。
(1) $${\mathrm{A}(1,1,1), \ \vec{n}=(1,2,1) }$$
(2) $${\mathrm{A}(1,2,1), \ \vec{n}=(1,-1,-2) }$$
平面の方程式の導出
各問題の平面の方程式を求めていきます。
(1)の平面の方程式は、
$$
\vec{n} \cdot \overrightarrow{\mathrm{AP}} = (x-1)+2(y-1)+(z-1)=x+2y+z-3=0
$$
となります。同様にして、(2)の平面の方程式は、
$$
\vec{n} \cdot \overrightarrow{\mathrm{AP}} = (x-1)-(y-2)-2(z-1)=x-y-2z+4=0
$$
となります。
平面を描くプログラム
平面の方程式を用いて空間上に平面を描いていく際に、記事『高校数学をプログラミングで解く(数学B編)「2-1 空間の座標」』の『プログラムの解説「平面の描画」』で解説したものと同じ方法で描いていきます。そのため、プログラムの中で、平面の方程式の一般形を
$$
z = \alpha x + \beta y + \gamma \ \ \left( \alpha=-\frac{a}{c}, \beta=-\frac{b}{c}, \gamma=-\frac{d}{c} \right)
$$
の形に変形して利用しています。
記事『高校数学をプログラミングで解く(数学B編)「2-0-3 右手系で図形を描く」』で作成したスケッチ「draw3DCoordinates_temp」を再利用します。以下の zip ファイルをダウンロードして展開または解凍してください。
そして、スケッチの名前(フォルダ名)を「drawPlanes2」と変更し、またスケッチ「drawPlanes2」内の「draw3DCoordinates_temp.pde」ファイルの名前を「drawPlanes2.pde」に変更します。そして、pdeファイル「drawPlanes2.pde」をダブルクリックしてスケッチ「drawPlanes2」の開発環境ウィンドウを立ち上げます。開発環境ウィンドウのタブ欄で「drawPlanes2」タブを選択し、そのテキストエリアのソースコードを以下で書き換えます。
// 平面を描く
float range; // 座標系での表示範囲-range≦x,y.z≦range
float res; // 座標系のサイズをキャンバスのサイズに変換するパラメータ
float angle = 0.0;
PVector a1, a2; // 問題(1)(2)で平面が通る点Aの座標
PVector n1, n2; // 問題(1)(2)の法線ベクトル
void setup(){
size(400, 400, P3D);
noFill();
ortho();
range = 10.0;
res = width / 2.0 / range;
// 問題(1)の点Aの座標と法線ベクトルn
a1 = new PVector(1.0, 1.0, 1.0);
n1 = new PVector(1.0, 2.0, 1.0);
// 問題(2)の点Aの座標と法線ベクトルn
a2 = new PVector(1.0, 2.0, 1.0);
n2 = new PVector(1.0, -1.0, -2.0);
}
void draw(){
background(204); // 背景をグレーにする
// 視点を設定する
camera(200.0, -200.0, 100.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0);
// マウスボタンが押されたときの処理
if(mousePressed){
if( mouseButton == LEFT ){ // 左ボタンがおされたときはz軸時計周りに回転
angle = angle + 1.0;
} else if( mouseButton == RIGHT ){ // 右ボタンがおされたときはz軸反時計周りに回転
angle = angle -1.0;
} else {
}
}
// z軸周りに回転
rotateZ(radians(angle));
// 座標軸の設定
strokeWeight(1);
fill(255,0,0);
stroke(255,0,0);
arrow3D(0.0,0.0,0.0,range * res,0.0,0.0); // x軸(赤色)
fill(0,255,0);
stroke(0,255,0);
arrow3D(0.0,0.0,0.0,0.0,range * res,0.0); // y軸(緑色)
fill(0,0,255);
stroke(0,0,255);
arrow3D(0.0,0.0,0.0,0.0,0.0,range * res); // z軸(青色)
noFill();
stroke(0,0,0);
// 以下に、図形を描いていく
float a,b,c,d; // 平面の方程式の一般形の係数
float alpha, beta, gamma; // 一般形をz= αx+βy+γの形にした時の係数
// 平面上の点を表す座標
PVector plane_point = new PVector(0.0,0.0,0.0);
// 平面を点群で表わすときの各軸方向に並べる点の数
int plot_num = 100;
// 図形を描く範囲
float m_range = -range/2.0;
float p_range = range/2.0;
// 問題(1)の平面(黄色)
// 平面のパラメータ
a = n1.x;
b = n1.y;
c = n1.z;
d = -n1.x*a1.x-n1.y*a1.y-n1.z*a1.z;
alpha = -a/c;
beta = -b/c;
gamma = -d/c;
// 点Aをプロットする
strokeWeight(5);
stroke(255,255,0);
point_rhs(a1.copy().mult(res));
// 法線ベクトルを描く
strokeWeight(1);
stroke(255,255,0);
fill(255,255,0);
arrow3D(a1.x*res,a1.y*res,a1.z*res,(n1.x+a1.x)*res,(n1.y+a1.y)*res,(n1.z+a1.z)*res);
// 平面を描画
stroke(255,255,0);
for(int i=0; i<plot_num; i++){
for(int j=0; j<plot_num; j++){
plane_point.x = m_range + i*(p_range-m_range)/plot_num;
plane_point.y = m_range + j*(p_range-m_range)/plot_num;
plane_point.z = alpha * plane_point.x + beta * plane_point.y + gamma;
point_rhs(plane_point.copy().mult(res));
}
}
// 問題(2)の平面(水色)
// 平面のパラメータ
a = n2.x;
b = n2.y;
c = n2.z;
d = -n2.x*a2.x-n2.y*a2.y-n2.z*a2.z;
alpha = -a/c;
beta = -b/c;
gamma = -d/c;
// 点Aをプロットする
strokeWeight(5);
stroke(0,255,255);
point_rhs(a2.copy().mult(res));
// 法線ベクトルを描く
strokeWeight(1);
stroke(0,255,255);
fill(0,255,255);
arrow3D(a2.x*res,a2.y*res,a2.z*res,(n2.x+a2.x)*res,(n2.y+a2.y)*res,(n2.z+a2.z)*res);
// 平面を描画
stroke(0,255,255);
for(int i=0; i<plot_num; i++){
for(int j=0; j<plot_num; j++){
plane_point.x = m_range + i*(p_range-m_range)/plot_num;
plane_point.y = m_range + j*(p_range-m_range)/plot_num;
plane_point.z = alpha * plane_point.x + beta * plane_point.y + gamma;
point_rhs(plane_point.copy().mult(res));
}
}
}
ソースコード1 平面を描くプログラム
このスケッチ「drawPlanes2」を実行すると、図1のように、実行ウィンドウのキャンバスに空間の座標系を表す3つの空間ベクトル(赤色:$${x}$$軸、緑色:$${y}$$軸、青色:$${z}$$軸)を基準にして、(1)の平面の方程式による平面を黄色、(2)の平面の方程式による平面を水色で表示します。
なお、実行ウィンドウのキャンバス内をクリックすると$${z}$$軸回りに図形が回転するので、いろいろな角度から空間ベクトルや平面を眺めてみてください。
直線の方程式に関する問題
次に、直線の方程式に基づいて直線を描くプログラムを作成していきます。
直線の方程式に関する問題
次の点$${\mathrm{A}}$$を通り、$${\vec{d}}$$に平行な直線の方程式を媒介変数$${t}$$を用いて表し、その直線を空間上に描け。
(1) $${\mathrm{A}(1,1,-1), \ \vec{d}=(2,3,1) }$$
(2) $${\mathrm{A}(3,2,1), \ \vec{d}=(0,2,1) }$$
直線の方程式の導出
各問題の直線の方程式を求めていきます。
(1)の直線の方程式は、
$$
x=1+2t, \ y=1+3t, \ z=-1+t
$$
となります。(2)の直線の方程式は、
$$
x=3, \ y=2+2t, \ z=1+t
$$
となります。
直線の方程式による直線の描き方
直線の方程式に基づいて直線を描く方法はいくつかありますが、今回は、媒介変数$${t}$$の範囲を$${-5 \leq t \leq 5}$$として、この$${t}$$の範囲での直線上の両端の点を求めて、これらの2点を結ぶことで直線を描くことにします。
また、プログラムの中での直線の方程式は、上記で求めた成分表示ではなく、$${\vec{p}=\vec{a}+t\vec{d}}$$のようなベクトルの形で利用しています。
直線を描くプログラム
先程ダウンロードしたスケッチ「draw3DCoordinates_temp」を再度利用します。スケッチの名前(フォルダ名)を「drawLines」と変更し、またスケッチ「drawLines」内の「draw3DCoordinates_temp.pde」ファイルの名前を「drawLines.pde」に変更します。そして、pdeファイル「drawLines.pde」をダブルクリックしてスケッチ「drawLines」の開発環境ウィンドウを立ち上げます。開発環境ウィンドウのタブ欄で「drawLines」タブを選択し、そのテキストエリアのソースコードを以下で書き換えます。
// 直線を描く
float range; // 座標系での表示範囲-range≦x,y.z≦range
float res; // 座標系のサイズをキャンバスのサイズに変換するパラメータ
float angle = 0.0;
PVector a1, a2; // 問題(1)(2)で直線が通る点Aの座標
PVector d1, d2; // 問題(1)(2)の方向ベクトル
void setup(){
size(400, 400, P3D);
noFill();
ortho();
range = 10.0;
res = width / 2.0 / range;
// 問題(1)の点Aの座標と方向ベクトルn
a1 = new PVector(1.0, 1.0, -1.0);
d1 = new PVector(2.0, 3.0, 1.0);
// 問題(2)の点Aの座標と方向ベクトルn
a2 = new PVector(3.0, 2.0, 1.0);
d2 = new PVector(0.0, 2.0, 1.0);
}
void draw(){
background(204); // 背景をグレーにする
// 視点を設定する
camera(200.0, -200.0, 100.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0);
// マウスボタンが押されたときの処理
if(mousePressed){
if( mouseButton == LEFT ){ // 左ボタンがおされたときはz軸時計周りに回転
angle = angle + 1.0;
} else if( mouseButton == RIGHT ){ // 右ボタンがおされたときはz軸反時計周りに回転
angle = angle -1.0;
} else {
}
}
// z軸周りに回転
rotateZ(radians(angle));
// 座標軸の設定
strokeWeight(1);
fill(255,0,0);
stroke(255,0,0);
arrow3D(0.0,0.0,0.0,range * res,0.0,0.0); // x軸(赤色)
fill(0,255,0);
stroke(0,255,0);
arrow3D(0.0,0.0,0.0,0.0,range * res,0.0); // y軸(緑色)
fill(0,0,255);
stroke(0,0,255);
arrow3D(0.0,0.0,0.0,0.0,0.0,range * res); // z軸(青色)
noFill();
stroke(0,0,0);
// 以下に、図形を描いていく
// 媒介変数tの範囲
float m_t = -5.0;
float p_t = 5.0;
// tの範囲での直線上の両端の点を表す座標
PVector m_p, p_p;
// 問題(1)の直線(黄色)
// 点Aをプロットする
strokeWeight(5);
stroke(255,255,0);
point_rhs(a1.copy().mult(res));
// 方向ベクトルを描く
strokeWeight(2);
stroke(255,255,0);
fill(255,255,0);
arrow3D(a1.x*res,a1.y*res,a1.z*res,(d1.x+a1.x)*res,(d1.y+a1.y)*res,(d1.z+a1.z)*res);
// 直線を描画
m_p = a1.copy().mult(res).add( d1.copy().mult(res).mult(m_t) );
p_p = a1.copy().mult(res).add( d1.copy().mult(res).mult(p_t) );
strokeWeight(1);
stroke(255,255,0);
line_rhs(m_p,p_p);
// 問題(2)の直線(水色)
// 点Aをプロットする
strokeWeight(5);
stroke(0,255,255);
point_rhs(a2.copy().mult(res));
// 方向ベクトルを描く
strokeWeight(2);
stroke(0,255,255);
fill(0,255,255);
arrow3D(a2.x*res,a2.y*res,a2.z*res,(d2.x+a2.x)*res,(d2.y+a2.y)*res,(d2.z+a2.z)*res);
// 直線を描画
m_p = a2.copy().mult(res).add( d2.copy().mult(res).mult(m_t) );
p_p = a2.copy().mult(res).add( d2.copy().mult(res).mult(p_t) );
strokeWeight(1);
stroke(0,255,255);
line_rhs(m_p,p_p);
}
ソースコード2 直線を描くプログラム
このスケッチ「drawLines」を実行すると、図2のように、実行ウィンドウのキャンバスに空間の座標系を表す3つの空間ベクトル(赤色:$${x}$$軸、緑色:$${y}$$軸、青色:$${z}$$軸)を基準にして、(1)の直線の方程式による直線を黄色、(2)の直線の方程式による直線を水色で表示します。
なお、今度も実行ウィンドウのキャンバス内をクリックすると$${z}$$軸回りに図形が回転するので、いろいろな角度から空間ベクトルや直線を眺めてみてください。
まとめ
今回は、数学Bで学ぶ「空間における平面・直線の方程式」について、平面の方程式に基づいた平面や直線の方程式に基づいた直線を描くプログラムを作成しました。
今回、平面は点群を用いて描き、直線はその両端の点の座標を求めてそれらを結ぶことで描きました。ただ、平面や直線の描き方はこれらの方法だけではなく、他にもいくつか思いつきます。たとえば、平面は、別途「tools_rhs.pde」の中に定義した関数 rect_rhs を利用して、空間上に四角形を描くことで表現することができそうです。また、直線は媒介変数$${t}$$を適当な範囲(たとえば$${-5 \leq t \leq 5}$$)を指定した上でこの範囲を$${N}$$個に分割、それぞれの$${t}$$の値で$${\vec{p}}$$を計算して、 point_rhs 関数で点列を描いていくという方法が考えられます。
このように描き方は一通りではなく、いろいろな描き方がありますので、いろいろな描き方を考えて試してみてください。
参考文献
改訂版 教科書傍用 スタンダード 数学B(数研出版、ISBN9784410209468)
この記事が気に入ったらサポートをしてみませんか?