この記事の内容
- 3点の座標から角度を求める方法
- 3点の座標から回転方向を求める方法
- 角度と回転方向を求めるプログラム
こんにちは、Youta(@youta_blog)です。
今回は、座標平面上の3点の座標から角度と回転方向を求めるお話です。
あなたは学生時代、次のような問題を見たことはありますか?
座標平面内に3点A(a,\ b), B(c,\ d), C(e,\ f) がある。
ただし、A\ne B\ne Cとする。
(1) ベクトル\overrightarrow{AB}と\overrightarrow{AC}のなす角\thetaを求めよ。
(2) ベクトル\overrightarrow{AB}に対して\overrightarrow{AC}は時計回りか反時計回りかを判別せよ。
解法を忘れてしまったり、そもそもどう解けばいいかわからない…という方が多いのではないでしょうか。
でもご安心ください!
記事を読めば、この問題の対処法を学べます。
合わせて、コンピュータに自動で解かせるためのプログラムもご紹介します。
Contents [hide]
3点から角度を求める2つの方法
まずは冒頭の問題の(1)の答えを示します。
答え
\theta = \arccos{\frac{(c-a)(e-a) + (d-b)(f-b)}{\sqrt{{(c-a)}^2 + {(d-b)}^2}\sqrt{{(e-a)}^2 + {(f-b)}^2}}}
すずか
先生
ベクトルの内積を使うやり方
手順は簡単。たった5ステップです。
1ステップずつ丁寧に解説しますね。
ベクトルを作る
Aを始点、BとCは終点とします。
このときベクトル\overrightarrow{AB}と\overrightarrow{AC}ができます。
A, B, Cの各座標が分かっているので、ベクトル\overrightarrow{AB}と\overrightarrow{AC}の成分表示が可能です。
A = (a_1,\ a_2)かつB = (b_1,\ b_2) \Rightarrow \overrightarrow{AB} = (b_1 – a_1,\ b_2 – a_1)
各ベクトルの成分表示は次のようになります。
ベクトル\overrightarrow{AB}と\overrightarrow{AC}の成分表示
- \overrightarrow{AB}=(c-a,\ d-b)
- \overrightarrow{AC}=(e-a,\ f-b)
ベクトルの大きさを求める
ベクトル\overrightarrow{AB}と\overrightarrow{AC}の大きさを求めます。
成分表示からベクトルの大きさを求めるには、次の公式を利用します。
\overrightarrow{a}=(a_1,\ a_2) \Rightarrow |\overrightarrow{a}| =\sqrt{a_1^2 + a_2^2}
ベクトル\overrightarrow{AB}と\overrightarrow{AC}の成分表示を既に求めてあるので、公式に当てはめて計算です。
ベクトル\overrightarrow{AB}と\overrightarrow{AC}の大きさ
- |\overrightarrow{AB}| = \sqrt{{(c-a)}^2 + {(d-b)}^2}
- |\overrightarrow{AC}| = \sqrt{{(e-a)}^2 + {(f-b)}^2}
ベクトルの内積を求める
ベクトル\overrightarrow{AB}と\overrightarrow{AC}の内積を求めます。
ベクトルの内積を求めるには、2通りの方法があります。
- \overrightarrow{a}=(a_1,\ a_2)かつ\overrightarrow{b}=(b_1,\ b_2) \Rightarrow \overrightarrow{a} \cdot \overrightarrow{b} = a_1b_1 + a_2b_2
- \overrightarrow{a}と\overrightarrow{b}のなす角が\theta \Rightarrow \overrightarrow{a} \cdot \overrightarrow{b} = |\overrightarrow{a}| |\overrightarrow{b}|\cos{\theta}
1の公式に、ベクトル\overrightarrow{AB}と\overrightarrow{AC}の成分表示を当てはめましょう。
ベクトル\overrightarrow{AB}と\overrightarrow{AC}の内積
\overrightarrow{AB} \cdot \overrightarrow{AC} = (c-a)(e-a) + (d-b)(f-b)
角度の余弦を求める
\cos{\theta}を求めます。
内積の公式2より\overrightarrow{AB} \cdot \overrightarrow{AC} = |\overrightarrow{AB}| |\overrightarrow{AC}|\cos{\theta}とも表せました。
これを変形すると\cos{\theta} = \frac{\overrightarrow{AB} \cdot \overrightarrow{AC}}{|\overrightarrow{AB}| |\overrightarrow{AC}|}ですね。
\overrightarrow{AB} \ne \overrightarrow{0}かつ\overrightarrow{AC} \ne \overrightarrow{0}より、上記の式変形が許されます。
ベクトル\overrightarrow{AB}と\overrightarrow{AC}の内積と大きさを代入し、なす角\thetaの余弦を求めます。
ベクトル\overrightarrow{AB}と\overrightarrow{AC}のなす角\thetaの余弦
\cos{\theta} = \frac{(c-a)(e-a) + (d – b)(f – b)}{\sqrt{{(c-a)}^2 + {(d-b)}^2}\sqrt{{(e-a)}^2 + {(f-b)}^2}}
逆余弦関数で角度を求める
すずか
\cos{\theta}求めても意味なくない?
先生
結論、逆余弦関数を使います。
\cos{\theta} = x \Leftrightarrow \theta = \arccos{x} (0\le\theta\le\pi)
要は、\cos{\theta}が〇〇になる角度\thetaってな〜んだ?
この角度\thetaを求めるのが逆余弦関数です。
ベクトル\overrightarrow{AB}と\overrightarrow{AC}のなす角\thetaの余弦は既に求めてあるので、これで\thetaがわかります。
ベクトル\overrightarrow{AB}と\overrightarrow{AC}のなす角\theta
\theta = \arccos{\frac{(c-a)(e-a) + (d – b)(f – b)}{\sqrt{{(c-a)}^2 + {(d-b)}^2}\sqrt{{(e-a)}^2 + {(f-b)}^2}}}
お疲れ様でした。
以上が、ベクトルの内積を利用して3点の座標から角度を導出する流れです。
先ほどの答えと一致していますね。
余弦定理を使うやり方
手順は簡単。たった5ステップです。
1ステップずつ丁寧に解説しますね。
三角形を作る
A, B, Cを頂点とする三角形を作ります。
三角形の辺の長さを求める
辺AB, BC, CAの長さを求めます。
座標平面上の2点間の距離を求めるには、次の公式を利用します。
A=(a_1,\ a_2)かつB=(b_1,\ b_2) \Rightarrow AB = \sqrt{{(a_1 – b_1)}^2 + {(a_2 – b_2)}^2}
よって、各辺の長さが求まります。
\triangle{ABC}の各辺の長さ
- AB = \sqrt{{(a – c)}^2 + {(b – d)}^2}
- BC = \sqrt{{(c – e)}^2 + {(d – f)}^2}
- CA = \sqrt{{(e – a)}^2 + {(f – b)}^2}
三角形に余弦定理を適用する
\triangle{ABC}について次が成り立つ。
a^2 = b^2 + c^2 – 2bc\cos{\angle{A}}
b^2 = c^2 + a^2 – 2ca\cos{\angle{B}}
c^2 = a^2 + b^2 – 2ab\cos{\angle{C}}
よって、\triangle{ABC}の各辺と\angle{A}に関して下記の等式が成り立ちます。
\triangle{ABC}の各辺と\angle{A} (= \theta)の関係
{BC}^2 = {AB}^2 + {CA}^2 – 2 \cdot AB \cdot CA \cdot \cos{\theta}
角度の余弦を求める
\cos{\theta}を求めます。
\triangle{ABC}の各辺と\thetaの関係式を変形すると\cos{\theta} = \frac{{AB}^2 + {CA}^2 – {BC}^2}{2 \cdot AB \cdot CA}ですね。
\triangle{ABC}の各辺の大きさを代入し、\angle{A}( = \theta)の余弦を求めます。
\triangle{ABC}の\angle{A} (= \theta)の余弦
\cos{\theta} = \frac{(c-a)(e-a) + (d – b)(f – b)}{\sqrt{{(c-a)}^2 + {(d-b)}^2}\sqrt{{(e-a)}^2 + {(f-b)}^2}}
次の4つの等式から導きます。
- AB = \sqrt{{(a – c)}^2 + {(b – d)}^2}
- BC = \sqrt{{(c – e)}^2 + {(d – f)}^2}
- CA = \sqrt{{(e – a)}^2 + {(f – b)}^2}
- \cos{\theta} = \frac{{AB}^2 + {CA}^2 – {BC}^2}{2 \cdot AB \cdot CA}
④に①・②・③を代入します。
逆余弦関数で角度を求める
すずか
先生
結論、逆余弦関数を使います。
\cos{\theta} = x \Leftrightarrow \theta = \arccos{x} (0\le\theta\le\pi)
要は、\cos{\theta}が〇〇になる角度\thetaってな〜んだ?
この角度\thetaを求めるのが逆余弦関数です。
\triangle{ABC}の\angle{A} (=\theta)の余弦は既に求めてあるので、これで\thetaがわかります。
\triangle{ABC}の\angle{A} (= \theta)
\theta = \arccos{\frac{(c-a)(e-a) + (d – b)(f – b)}{\sqrt{{(c-a)}^2 + {(d-b)}^2}\sqrt{{(e-a)}^2 + {(f-b)}^2}}}
計算が少し大変でしたね。
以上が、余弦定理を利用して3点の座標から角度を導出する流れです。
どちらでも同じ答えが出るのは面白いですね!
3点から回転方向を求める方法
お次は冒頭の問題の(2)の答えを示します。
答え
- (c-a)(f-b)-(d-b)(e-a)>0\Rightarrow反時計回り
- (c-a)(f-b)-(d-b)(e-a)<0\Rightarrow時計回り
- (c-a)(f-b)-(d-b)(e-a)=0\Rightarrow回転なし
すずか
先生
結論、ベクトルの外積を利用します。
手順は簡単。たった3ステップです。
1ステップずつ丁寧に解説しますね。
ベクトルを作る
Aを始点、BとCは終点とします。
このときベクトル\overrightarrow{AB}と\overrightarrow{AC}ができます。
A, B, Cの各座標が分かっているので、ベクトル\overrightarrow{AB}と\overrightarrow{AC}の成分表示が可能です。
A = (a_1,\ a_2)かつB = (b_1,\ b_2) \Rightarrow \overrightarrow{AB} = (b_1 – a_1,\ b_2 – a_1)
各ベクトルの成分表示は次のようになります。
ベクトル\overrightarrow{AB}と\overrightarrow{AC}の成分表示
- \overrightarrow{AB}=(c-a,\ d-b,\ 0)
- \overrightarrow{AC}=(e-a,\ f-b,\ 0)
すずか
先生
何も難しくはありません。
ベクトル\overrightarrow{AB}と\overrightarrow{AC}は座標平面上にあるので、z成分は0ですよね。
ベクトルの外積を求める
ベクトル\overrightarrow{AB}と\overrightarrow{AC}の外積を求めます。
ベクトルの外積を求めるには、次の公式を利用します。
\overrightarrow{a}=(a_1,\ a_2, a_3)かつ\overrightarrow{b}=(b_1,\ b_2, b_3) \Rightarrow \overrightarrow{a} \times \overrightarrow{b} = (a_2b_3-a_3b_2,\ a_3b_1-a_1b_3,\ a_1b_2-a_2b_1)
ベクトル\overrightarrow{AB}と\overrightarrow{AC}の成分表示を既に求めてあるので、公式に当てはめるだけです。
ベクトル\overrightarrow{AB}と\overrightarrow{AC}の外積
\overrightarrow{AB} \times \overrightarrow{AC} = (0,\ 0,\ (c-a)(f-b)-(d-b)(e-a))
外積のz成分で回転方向を判別
外積のz成分の符号で回転方向がわかります。
回転方向には次の3通りがあります。
- z成分>0\Rightarrow反時計回り
- z成分<0\Rightarrow時計回り
- z成分=0\Rightarrow回転なし
ベクトル\overrightarrow{AB}と\overrightarrow{AC}の外積は既に求めてあるため、そのz成分で回転方向を判別します。
ベクトル\overrightarrow{AB}に対する\overrightarrow{AC}の回転方向
- (c-a)(f-b)-(d-b)(e-a)>0\Rightarrow反時計回り
- (c-a)(f-b)-(d-b)(e-a)<0\Rightarrow時計回り
- (c-a)(f-b)-(d-b)(e-a)=0\Rightarrow回転なし
以上、3点から回転方向を求める方法でした。
すずか
では、コンピューターにこの計算をお願いしましょう。
先生
プログラムを実装
先ほどの理論を踏まえた上で、Pythonでプログラムを書いてみましょう。
実装例は3つご用意しました。
ニーズに合わせてお選びください。
角度を求める
import numpy as np
def get_angle(point_a, point_b, reference_point, is_radian=False):
# 点Aまたは点Bが基準点と同じ場合、角度は0を返す
if point_a == reference_point or point_b == reference_point:
return 0
# ベクトルを計算
reference_point = np.array(reference_point)
vector_a = np.array(point_a) - reference_point
vector_b = np.array(point_b) - reference_point
# ベクトルの大きさ(長さ)を計算
magnitude_a = np.linalg.norm(vector_a)
magnitude_b = np.linalg.norm(vector_b)
# 内積を計算
dot_product = np.dot(vector_a, vector_b)
# 余弦を計算
cosine = dot_product / (magnitude_a * magnitude_b)
# 逆余弦関数を計算して角度を取得
angle = np.arccos(cosine)
# 弧度法指定ではない場合、角度を度数法に変換
return angle if is_radian else np.degrees(angle)
get_angle
関数は3点の座標から角度を求めます。
引数は次の4つです。
point_a
: 点Aの座標(リスト or タプル)point_b
: 点Bの座標(リスト or タプル)reference_point
: 基準点の座標(リスト or タプル)is_radian
: Trueだと角度を弧度法で、False(デフォルト)だと度数法で返す。
戻り値は角度です。
angle
: 角度。引数is_radian
で単位を指定。
回転方向を求める
import numpy as np
def get_rotation_direction(from_point, to_point, reference_point):
# 始点または終点が基準点と同じ場合、回転方向は定義しない
if from_point == reference_point or to_point == reference_point:
return 'undefined'
# 始点と終点からベクトルを計算
reference_point = np.array(reference_point)
from_vector = np.array(from_point) - reference_point
to_vector = np.array(to_point) - reference_point
# 外積を計算
cross_product = np.cross(from_vector, to_vector)
# 外積の符号に応じて角度を調整
return 'anti-clockwise' if cross_product >= 0 else 'clockwise'
get_rotation_direction
関数は3点の座標から回転方向を求めます。
引数は次の3つです。
from_point
: 回転の始点の座標(リスト or タプル)to_point
: 回転の終点の座標(リスト or タプル)reference_point
: 回転の中心点の座標(リスト or タプル)
戻り値は文字列です。
'clockwise'
: 時計回り。0^{\circ}回転も含む。'anti-clockwise'
: 反時計回り。'undefined'
: 回転を考えない。
3次元座標空間上の点には対応しておりませんのでご注意ください。
角度と回転方向を求める
def get_signed_angle(from_point, to_point, reference_point, is_radian=False):
angle = get_angle(from_point, to_point, reference_point, is_radian)
rotation_direction = get_rotation_direction(from_point, to_point, reference_point)
return angle if rotation_direction == 'anti-clockwise' else -angle
get_signed_angle
関数は角度と回転方向を求めます。
回転方向は、戻り値の符号で判断できます。
引数は次の4つです。
from_point
: 回転の始点の座標(リスト or タプル)to_point
: 回転の終点の座標(リスト or タプル)reference_point
: 回転の中心点の座標(リスト or タプル)is_radian
: Trueだと角度を弧度法で、False(デフォルト)だと度数法で返す。
戻り値は角度です。
angle
: 角度。0以上で時計回り、0未満で半時計回り。
3次元座標空間上の点には対応しておりませんのでご注意ください。
プログラムの試験
角度を求める
3点の座標から角度を求めるget_angle
関数のテストを行います。
座標平面上の3点
ベクトル\overrightarrow{a} = (1,\ \sqrt{3})と\overrightarrow{b}=(\sqrt{3},\ -3)のなす角\thetaを求めよ。
答え
\theta = 120^{\circ}
get_angle
関数の下に、次のコード書いて実行します。
a = (1, np.sqrt(3))
b = (np.sqrt(3), -3)
o = (0, 0)
answer = get_angle(a, b, o)
print(answer)
実行結果です。
print(answer)
# 120.00000000000001
先生
座標空間上の3点
空間の3点A(1,\ −2,\ 3), B(3,\ −4,\ 2), C(−3,\ 3,\ 0)に対して,ベクトル\overrightarrow{AB}と\overrightarrow{AC}のなす角を求めよ。
[05 福井県立大]
答え
135^{\circ}
get_angle
関数の下に、次のコード書いて実行します。
A = (1, -2, 3)
B = (3, -4, 2)
C = (-3, 3, 0)
answer = get_angle(B, C, A)
print(answer)
実行結果です。
print(answer)
# 135.0
回転方向を求める
先ほどの問題を流用します。
ベクトル\overrightarrow{a} = (1,\ \sqrt{3})に対して\overrightarrow{b} = (\sqrt{3},\ -3)はどちら回りの回転か。
答え
時計回り
get_rotation_direction
関数の下に、次のコード書いて実行します。
a = (1, np.sqrt(3))
b = (np.sqrt(3), -3)
o = (0, 0)
answer = get_rotation_direction(a, b, o)
print(answer)
実行結果です。
print(answer)
# clockwise
角度と回転方向を求める
先ほどの問題を流用します。
ベクトル\overrightarrow{a} = (1,\ \sqrt{3})に対して\overrightarrow{b} =(\sqrt{3},\ -3)は反時計回りに何度回転しているか。
答え
-120^{\circ}
すずか
先生
get_signed_angle
関数の下に、次のコード書いて実行します。
a = (1, np.sqrt(3))
b = (np.sqrt(3), -3)
o = (0, 0)
answer = get_rotation_direction(a, b, o)
print(answer)
実行結果です。
print(answer)
# -120.00000000000001
角度と回転方向が同時に求まりました。
プログラムの活用例
図形をマウスドラッグで回転させるプログラムです。
PythonのGUIライブラリであるTkinterで作りました。
複雑そうですが、仕組みはシンプルです。
マウスカーソルの位置が変化するたびに、次の3点から回転角度を求めています。
- カーソルの現在位置
- カーソルの移動前の位置
- 図形の重心
次に、求めた角度を元に図形の頂点を回転移動させるだけです。
現在Tkinter搭載の関数では、図形を回転させることはできません。
しかし、今回ご紹介した点を回転させるプログラムを上手く使うと、このようにクルクル回すことが出来るのです。
Tkinterで図形を回転させるプログラムに関する記事は、近日中に公開します!
まとめ
今回は、3点の座標から角度と回転方向を求める方法について解説しました。
角度を求める方法は2通りありましたね。
上記いずれのやり方でも、余弦と逆余弦関数が重要でした。
また、回転方向は外積のz成分で判別します。
- z成分>0\Rightarrow反時計回り
- z成分<0\Rightarrow時計回り
- z成分=0\Rightarrow回転なし
本記事でご紹介した内容は、特にゲーム制作で用いられる技術です。
自分でPCゲームを作る際には是非参考にしてくださいね。
最後までお付き合いいただき、ありがとうございました。