のつづきです。今回は直線関係を整備していきます。
直線クラス
はじめに直線クラスを整備する目的について説明します。
目的の1つ目は、CANVASに直線を描画するためです。これはそれ以上の説明は不要でしょう。
目的の2つ目は、もっと複雑な図形を扱うための基本となるからです。長方形以外の四角形や、その他の多角形、折れ線グラフのような線などすべて直線が基本となります。
目的の3つ目は、今後の図形に関連する計算処理をするための道具としての直線が必要になるからです。
この文章を書いている時点で、「ブロックくずしを作る」シリーズはブロックくずしを作る7まで書いています。今後、球とブロックの衝突判定を作っていく予定ですが、そのときに球(=Circle)とブロック(=Rectangle)の「距離」や「位置関係」などの判定を考えるときに、直線クラスを使っていきたいと考えているのです。
Line
Lineは2つの点を結ぶ線分です。
/**
* 線分(2点を結ぶ直線)を表すクラス
*/
export class Line {
/**
* 始点X座標
*/
public X1: number;
/**
* 始点Y座標
*/
public Y1: number;
/**
* 終点X座標
*/
public X2: number;
/**
* 終点Y座標
*/
public Y2: number;
/**
* コンストラクタ
* @param x1 始点X座標
* @param y1 始点Y座標
* @param x2 終点X座標
* @param y2 終点Y座標
*/
constructor(x1: number, y1: number, x2: number, y2: number) {
this.X1 = x1;
this.Y1 = y1;
this.X2 = x2;
this.Y2 = y2;
}
/**
* srcをコピーするa
* @param src
*/
public Copy(src: Line) {
this.X1 = src.X1;
this.Y1 = src.Y1;
this.X2 = src.X2;
this.Y2 = src.Y2;
}
/**
* クローン(新たなLine)を作る。
*/
public Clone() {
return new Line(this.X1, this.Y1, this.X2, this.Y2);
}
/**
* @returns 線分の長さ
*/
public Length() {
let dx = this.X2 - this.X1;
let dy = this.Y2 - this.Y1;
return Math.sqrt(dx * dx + dy * dy);
}
}
(X1,Y1)を始点、(X2,Y2)を終点とする線分を表すクラスです。特別に変わったことはしていませんが、線分の長さを必要とすることは多いため、Length()メソッドとして追加しています。
RectangleやCicrcleなどと違い、IPointクラスを継承していません。IPointを継承し、(X,Y)を線分の中点として取り扱うようにすることも考えたのですが、そのように取りつかう必要を感じなかったため、採用しませんでした。
StraightLine
直線方程式の一般式です。
/**
* 直線(端のない直線:straight line)を表すクラス
*/
export class StraightLine {
/**
* 直線方程式の一般系 ax + by + c = 0 のaの値
*/
public A: number;
/**
* 直線方程式の一般系 ax + by + c = 0 のbの値
*/
public B: number;
/**
* 直線方程式の一般系 ax + by + c = 0 のcの値
*/
public C: number;
/**
* 直線方程式の一般系 ax + by + c = 0 で表される直線のコンストラクタ
*
* a,b,cを指定しないdefaultでは、a=-1,b=1.c=0がセットされる。
* これは y = xとなる直線のときのパラメーターである。
* @param a 直線方程式の一般系 ax + by + c = 0 の a
* @param b 直線方程式の一般系 ax + by + c = 0 の b
* @param c 直線方程式の一般系 ax + by + c = 0 の c
*/
constructor(a: number = -1, b: number = 1, c: number = 0) {
this.A = a;
this.B = b;
this.C = c;
}
/**
* srcをコピーする
* @param src
*/
public Copy(src: StraightLine) {
this.A = src.A;
this.B = src.B;
this.C = src.C;
}
/**
* クローン(新たなLine)を作る。
*/
public Clone() {
return new StraightLine(this.A, this.B, this.C);
}
/**
* (x1,y1)と(x2,y2)の2点を通る直線となるように、パラメーターa, b, cをセットする
* @param x1
* @param y1
* @param x2
* @param y2
*/
public SetLine(x1: number, y1: number, x2: number, y2: number) {
this.A = y1 - y2;
this.B = x2 - x1;
this.C = x1 * y2 - x2 * y1;
}
}
コード中のコメントに書いてある通りで、直線方程式の一般系 \(ax+by+c=0\) の情報を保持するためのクラスです。
このクラスは、図形の計算処理ために使用します。
SetLine()の説明
直線のa, b, cを直接指定して、下のようなコードでStraightLineを使うことはほとんどないはずです。
let sline = new StraightLine(0, 2, 5);
上の式でどんな直線ができるのか、直感的にはわかりませんよね?
そこで、もう少し普通に情報をセットするためにSetLine()メソッドを用意しています。例えば、2つの点(0,10)と(5,5)を結ぶ直線をセットするときは、以下のようなコードになります。
let sline = new StraightLine();
sline.SetLine(0,10,5,5);
SetLine()のコードの説明
直線\(ax+by+c=0\) が2つの点\((X_1,Y_1)\)と\((X_2,Y_2)\)を通るとき、以下の連立方程式が成り立ちます。
\begin{cases}
aX_1+bY_1+c=0\\
aX_2+bY_2+c=0
\end{cases}
上の式から下の式をそのまま引くと、
\begin{split}
(aX_1+bY_1+c)-(aX_2+bY_2+c)&=0-0\\
a(X_1-X_2)+b(Y_1-Y_2)&=0\\
a(X_1-X_2)&=-b(Y_1-Y_2)\\
\end{split}
となり、\[\frac{a}{b}=-\frac{Y_1-Y_2}{X_1-X_2}\]という式ができます。つまり、
\[
\begin{cases}
a=k(Y_1-Y_2)\\
b=k(X_2-X_1)\\
\end{cases}
(kは任意の数)……(1)
\]
となります。
また、\(aX_1+bY_1+c=0\)を変形すると、
\[
c=-(aX_1+bY_1)\\
\]
これに(1)のa,bを代入すると、
\begin{split}
c&=-k(Y_1-Y_2)X_1-k(X_2-X_1)Y_1\\
&=-kX_1Y_1+kX_1Y_2-kX_2Y_1+kX_1Y_1\\
&=k(X_1Y_2-X_2Y_1)
\end{split}
\(k=1\)とし、
\[
\begin{cases}
a=(Y_1-Y_2)\\
b=(X_2-X_1)\\
c=(X_1Y_2-X_2Y_1)
\end{cases}
\]
になります。
コメント