2010/09/13(月)
3点の座標 (2次元または3次元) から三角形の辺の長さ・角度, ベクトルの内積・外積などを自動的に計算する Excel ファイルを, DLmarket 様にて委託ダウンロード販売開始しました.
下図において,線分 CP が点 C を中心に角度θ (-180°< θ < +180°)
だけ回転した結果,
線分 CQ になったとする.3つの点の座標
C=(Cx, Cy), P=(Px, Py), Q=(Qx, Qy)
が与えられたとき,この回転が右回りか左回りかを判別せよ.
(別の言い方をすれば,「C → P → Q → C は右回りか左回りか?」)
Y ↑ │ Q │ / │ /θ │ C────P │ O────────→X
S (Px - Cx) * (Qy - Cy) - (Py - Cy) * (Qx - Cx)
とする.S>0 なら左回り,S<0 なら右回り,S=0 ならば C,P,Q は一直線上にある.(注)
なお,この判別方法は,CP と CQ が同じ長さである必要はない.
θを求めたい場合はこちらへ.
この問題を見て,逆三角関数 tan-1 (C言語では atan() や atan2()) を使って CP と CQ の角度をそれぞれ求め, 両者を比較しようと考えた方が多いのではないでしょうか. しかしこの問題では,角度そのものではなく角度差の符号を求めればよいので, 逆三角関数を使う方法よりも簡単で優れた,外積を使う方法を紹介します.
2つの2次元ベクトル A=(Ax, Ay), B=(Bx, By) の外積を次のように定義する.
A × B ≡ Ax * By − Ay * Bx
ここで OA から OB への回転角をθ (-180°< θ < +180°) とする (下図). ただし OA から OB に回転する方向が左回りならば θ>0 とする. このとき,A×B = |OA| * |OB| * sinθ である.したがって,
Y ↑ │ B │ / θ A │ / / │ / / │// O───────→X
このように外積を使えば,2回の乗算だけですむので,(遅い) 逆三角関数サブルーチンを使う方法に比べて次のようにいいことずくめです.
また,2つのベクトルの回転角 (上記のθ) を求める場合には, 後述するように内積と外積を併用すれば簡単です. この場合も,2つのベクトルそれぞれの角度を求め, それらの差を計算しようとすると場合分けが必要になり厄介です.
なお,外積は3次元で使われることが多く,
「外積」で検索してもヒットするのは多分ほとんどが3次元の話だと思います.
3次元ベクトル同士の外積は3次元ベクトルになりますが,
2次元ベクトル同士の外積はスカラーになります.
(3次元外積において,元の2つのベクトルがXY平面内にある場合を考えれば,
外積のX,Y成分は0になる.そのためZ成分だけを考えればよく,
それを2次元の外積と考えることもできる.)
上の解答および解説で,「外積の値が正ならば左回り」と書きましたが, それはX軸およびY軸の方向が上の図のようになっている場合です. もしY軸が下向き (あるいはX軸が左向き) になっていれば回転の向きが逆になります.(注の注)
そこで一般的な言い方をすると,次のようになります.
OX が90°回転して OY になる方向を正回転とすると, 外積が正ならば正回転,負ならば逆回転.
上の解説を読んで, 次のような疑問を持たれた方も多いでしょう.
(a) 「3次元ベクトル同士の外積は3次元ベクトルなのに, なぜ2次元ベクトル同士の外積は2次元ベクトルではなくスカラーなのか?」
あるいは,3次元外積だけを知っている人なら,次のように思われたかもしれません.
(b) 「えっ,2次元外積がスカラーってどういうこと? そもそも外積って3次元以外で定義できるの?」
(a) のようになる理由は, 3次元の外積の定義を見るとわかりますが, 外積の1つの成分を計算するのに, 元の2つのベクトルの2つの座標軸の成分を使っていることにあります. 例えば3次元外積のX成分を計算するには, 元の2つのベクトルのYおよびZ成分を使います. 2つの座標軸の組合せごとに,外積の独立成分が1つ対応します. 外積の各独立成分は,N次元空間内の回転を, 2つの座標軸が張る平面に投影したものに対応していると考えられます.
したがってN次元ベクトル同士の外積には, NC2 個の独立な成分があります. 3次元ではたまたま 3C2=3 なのでベクトルとして表現できますし, 2次元ではたまたま 2C2=1 なのでスカラーとして表現できます.
一般のN次元では,ベクトル
A=(A1, A2, …, AN)
と B=(B1, B2, …, BN) の外積は,次のような N×N 行列で表現できます.
((楔形の記号∧から) ウェッジ積,(考案者名から) グラスマン積ともいう.)
A ∧ B = (Ai * Bj − Aj * Bi) = |
|
実はこれが本来の外積の定義であり, ベクトルでもスカラーでもありません. 上の説明では「行列で表現」と書きましたが, 正確に言うと外積は2階のテンソルと呼ばれる量です. N階のテンソルとは,大雑把に言えば,「N次元の行列」のようなものと考えてください. 0階のテンソルはスカラー,1階のテンソルはベクトルです.
さらに正確に言うと,外積は2階の 反対称 擬テンソルです. 「反対称」というのは,上に書いた行列の行と列を入れ替えると (つまり転置行列にすると),符号が反転するということです. 「擬」とは,「右と左を入れ替える=座標系の向きを反転させる (奇数本の座標軸を逆向きにする,あるいは2本の座標軸を交換する) と, 向きが反転する」ことを意味します.
上の「注意」で, 座標系の向きによって外積の符号に対応する回転の向きが変わるという意味のことを書きましたが, これは2次元外積が擬スカラーだからです. 同様に3次元外積は擬ベクトル (軸性ベクトルともいう) です.
軸性ベクトルに関する詳しい説明.
高校数学Bでは2〜3次元ベクトルしか習わないが,何次元だろうと (無限次元だろうと),ベクトル A=(Ax,Ay,Az,…) と B=(Bx,By,Bz,…) の内積の定義は,
幾何学的定義:A・B ≡ |A| * |B| * cosθ 代数的定義 :A・B ≡ Ax * Bx + Ay * By + Az * Bz + …
したがって A と B のなす角θは,
A・B cosθ = ──── |A||B| Ax * Bx + Ay * By + Az * Bz + … = ───────────────────────── √((Ax2 + Ay2 + Az2 + …)(Bx2 + By2 + Bz2 + …))
あとは逆三角関数 cos-1 を使うだけでθ (0°≦ θ ≦ 180°) が求まる. 2次元の場合は Az および Bz 以後を0とすればよい.
内積を使うと,何次元のなす角でも求められるが,回転方向の情報は得られない.
■参考
■参考
また,図形の問題では,「2辺のなす角」という場合もあり, この場合は「小さい方の角」のことをいいます。
3次元以上では回転方向は無限にありますが, 角度が同じならば内積の値は回転方向にかかわらず全部同じになります.
時々,「なす角」,「角度」,「逆三角関数」,「atan2」 などで検索してくる人がいるのでおまけ.(2006/12/13(水))
2次元ベクトル A と B の内積および外積の定義を改めて書くと,
幾何学的定義:A ・ B ≡ |A| * |B| * cosθ A × B ≡ |A| * |B| * sinθ 代数的定義 :A ・ B ≡ Ax * Bx + Ay * By A × B ≡ Ax * By - Ay * Bx
なので,両者を併用すると簡単に角度および回転方向 (-180°< θ ≦ +180°) が求まる. C/C++ の場合は atan2(y, x) 関数,C# の場合は Math.Atan2(y, x) を使うと,
θ = atan2(A×B,A・B) (単位はラジアン,引数の順序にも注意)
Excel の ATAN2(x, y) 関数はCの atan2(y, x) とは引数の順序が逆なので,
θ = ATAN2(A・B,A×B)
■参考
3次元ベクトル A と B の外積 (ベクトル積,クロス積ともいう) A×B の定義は,
大学1年レベルの力学,電磁気学,物理数学の教科書にデカデカと書いてあるはず
(ただしほとんど右手系限定).
最近では,3DCG やゲームプログラミングの本 (の一部?) でも解説されている.
「外積 求め方」などで検索して来る人が多いけど, 教科書読んだことがないんだったら何で「外積」という言葉を知ってるの? (激謎)
幾何学的定義:|A × B| ≡ |A| * |B| * sinθ (0°≦ θ ≦ 180°) 回転に対する A × B の向きは,座標系が右手系ならば右ネジの進む方向, 左手系ならば左ネジの進む方向.(A:親指方向,B:人差し指方向,A×B:中指方向) 代数的定義 :A × B ≡ (Ay * Bz - Az * By, Az * Bx - Ax * Bz, Ax * By - Ay * Bx)
θを求める方法として,2次元の場合と同様に外積と内積を併用することも可能だが, 3次元では計算式が複雑になるだけでメリットが (たぶん) ないので, 6.1 の方法を使う方がいいと思う.
回転方向については,A×B 自体がその方向を表しているのでそのまま使ってもよいが, 単位ベクトルにする必要がある場合には正規化して A×B / |A×B| とすればよい.
注意:4次元以上の回転についてちゃんと考えたわけではないので, この節に書いてあることを読む前に,眉に唾をベットリとつけてほしい.(笑)
まず回転角 (なす角) は,何次元であろうが 6.1 の方法で求めることができる.(これは間違いない.)
「余談 (N次元の外積について)」 に書いたことと2〜3次元の回転から類推すれば, N次元でも次式が成立するはずである.
|A ∧ B| = |A| * |B| * sinθ (0°≦ θ ≦ 180°)
ただしN次元外積テンソル A∧B の「長さ」は次のように定義する.
|A ∧ B| ≡ √ (Σ (Ai * Bj - Aj * Bi)2) i<j = √ ((1/2) ΣΣ (Ai * Bj - Aj * Bi)2) i j
3次元と同様,A∧B 自体がその「回転方向」を表しているが, これを正規化すれば A∧B / |A∧B| となる.
3点の座標 (2次元または3次元) を入力するだけで,自動的に次の値を計算する Excel ファイルを,DLmarket 様にて委託ダウンロード販売しています.
Triangle2D.xls (2次元用) | \308 |
Triangle3D.xls (3次元用) | \514 |
2次元用 + 3次元用 (セット割引) | \617 |
/*───────────────────────────────────── 機能 :2つのベクトルの外積成分を計算する. 入力 :(1) vector1,vector2:ベクトル (構造体のアドレスまたは配列). (2) component1,component2:ベクトルの座標成分.ベクトルが構造体の 場合はメンバ名,配列の場合は成分番号 (添字). 戻り値:外積 vector1∧vector2 の (component1,component2) 成分. 2009/03/13(金) 作成 ─────────────────────────────────────*/ // ベクトルが構造体の場合 #define CrossProductComponentS(vector1, vector2, component1, component2) \ ((vector1)->component1 * (vector2)->component2 - \ (vector1)->component2 * (vector2)->component1) // ベクトルが配列の場合 #define CrossProductComponentA(vector1, vector2, component1, component2) \ ((vector1)[component1] * (vector2)[component2] - \ (vector1)[component2] * (vector2)[component1]) /*───────────────────────────────────── 機能 :2つのn次元ベクトルのウェッジ積 (外積テンソル)を計算する. 無駄な計算を避けるため,独立成分のみ求める. 入力 :(1) vector1[0 〜 n-1],vector2[0 〜 n-1]:n次元ベクトル (配列). (2) n (≧2):ベクトルの次元. 出力 :wedgeProduct[0 〜 nC2-1]:ウェッジ積 vector1∧vector2 の独立成分の配列. 注意 :n=3 の場合,wedgeProduct[] の要素の順序および符号は,通常の3次元 外積の定義とは異なる.だから「3次元外積のプログラムを作成せよ」と いう学校の課題でこれを丸写ししてもダメよ.(笑) 2009/03/13(金) 作成 2013/01/14(月) 改名 (VectorN_CrossProduct → VectorN_WedgeProduct) ─────────────────────────────────────*/ typedef double VectorComponent_t; // ベクトルの成分の型 void VectorN_WedgeProduct(VectorComponent_t wedgeProduct[/* nC2 */], const VectorComponent_t vector1[/* n */], const VectorComponent_t vector2[/* n */], unsigned n) { unsigned i, j; for(i = 0; i < n; i++) { for(j = i; ++j < n; ) { *wedgeProduct++ = (VectorComponent_t)CrossProductComponentA(vector1, vector2, i, j); } } }
#ifdef _MSC_VER #define _USE_MATH_DEFINES // Visual C/C++ では,M_PI を使うのにこれが必要. #endif /* _MSC_VER */ #include <math.h> #include <stdio.h> #include <stdlib.h> // ラジアンを度に変換する. #define RadianToDegree(radian) ((180 / M_PI) * (radian)) // 度をラジアンに変換する. #define DegreeToRadian(degree) ((M_PI / 180) * (degree)) // ベクトルの成分の型 typedef double VectorComponent_t; // 2次元ベクトル typedef struct { VectorComponent_t x, y; } Vector2_t; // diff ← ベクトル v1 - v2 void Vector2_Diff(Vector2_t *diff, const Vector2_t *v1, const Vector2_t *v2) { diff->x = v1->x - v2->x; diff->y = v1->y - v2->y; } // ベクトル v1 と v2 の内積 (v1・v2) を返す. VectorComponent_t Vector2_DotProduct(const Vector2_t *v1, const Vector2_t *v2) { return v1->x * v2->x + v1->y * v2->y; } // ベクトル v1 と v2 の外積 (v1×v2) を返す. VectorComponent_t Vector2_CrossProduct(const Vector2_t *v1, const Vector2_t *v2) { return v1->x * v2->y - v1->y * v2->x; // return CrossProductComponentS(v1, v2, x, y); でもよい. } // ベクトル C→P と C→Q のなす角θおよび回転方向を求める. int main(void) { Vector2_t c, p, q; // 入力データ Vector2_t cp; // ベクトル C→P Vector2_t cq; // ベクトル C→Q VectorComponent_t s; // 外積:(C→P) × (C→Q) VectorComponent_t t; // 内積:(C→P) ・ (C→Q) double theta; // θ (ラジアン) // c,p,q を所望の値に設定する. c.x = 1; c.y = 2; p.x = 2; p.y = 3; q.x = 3; q.y = 5; // 回転方向および角度θを計算する. Vector2_Diff(&cp, &p, &c); // cp ← p - c Vector2_Diff(&cq, &q, &c); // cq ← q - c s = Vector2_CrossProduct(&cp, &cq); // s ← cp × cq t = Vector2_DotProduct(&cp, &cq); // t ← cp ・ cq theta = atan2(s, t); // 結果を出力する. printf("C=(%G, %G)\n", c.x, c.y); printf("P=(%G, %G)\n", p.x, p.y); printf("Q=(%G, %G)\n", q.x, q.y); printf("θ=%Gラジアン (%G度)\n", theta, RadianToDegree(theta)); if(s == 0.0) { printf("3点 C,P,Q は一直線上にある。\n"); } else { printf("C→P→Qは%s回り。\n", (s > 0.0) ? "左" : "右"); } return EXIT_SUCCESS; }
実行結果:
C=(1, 2) P=(2, 3) Q=(3, 5) θ=0.197396ラジアン (11.3099度) C→P→Qは左回り。
|
Androidゲームプログラミング A to Z posted with amazlet at 12.05.20 Mario Zechner インプレスジャパン 売り上げランキング: 26792 |
|
細野真宏のベクトル〈平面図形〉が本当によくわかる本―数B (1週間集中講義シリーズ) posted with amazlet at 11.11.03 細野 真宏 小学館 売り上げランキング: 32875 |
|
細野真宏の ベクトル〈空間図形〉が本当によくわかる本 1週間集中講義シリーズ posted with amazlet at 11.11.03 細野 真宏 小学館 売り上げランキング: 8392 |
|
|
坂田アキラの三角関数・指数・対数が面白いほどわかる本 新装版―数学2対応 (数学が面白いほどわかるシリーズ) posted with amazlet at 11.11.03 坂田 アキラ 中経出版 売り上げランキング: 17311 |
山本俊郎のベクトル原則編が面白いほどわかる本 (数学が面白いほどわかるシリーズ) posted with amazlet at 11.11.03 山本 俊郎 中経出版 売り上げランキング: 142524 |
山本俊郎のベクトル実戦編が面白いほどわかる本 (数学が面白いほどわかるシリーズ) posted with amazlet at 11.11.03 山本 俊郎 中経出版 売り上げランキング: 331107 |
イラスト・図解 はじめての行列とベクトル posted with amazlet at 11.11.03 長谷川 勝也 技術評論社 売り上げランキング: 220444 |
|
なるほど高校数学 ベクトルの物語 (ブルーバックス) posted with amazlet at 11.11.03 原岡 喜重 講談社 売り上げランキング: 229182 |
|
なるほど高校数学 三角関数の物語 (ブルーバックス) posted with amazlet at 11.11.03 原岡 喜重 講談社 売り上げランキング: 158574 |
|
|
|
なっとくする行列・ベクトル (なっとくシリーズ) posted with amazlet at 11.11.03 川久保 勝夫 講談社 売り上げランキング: 172378 |
|
内積・外積・空間図形を通して ベクトルを深く理解しよう (数学のかんどころ 1) posted with amazlet at 11.11.03 飯高 茂 共立出版 売り上げランキング: 190055 |
ベクトル・行列がビジュアルにわかる線形代数と幾何 posted with amazlet at 11.11.03 江見 圭司 江見 善一 共立出版 売り上げランキング: 158714 |
|
|
|
|
理論的な説明 + サンプルプログラム.
|
微分幾何学と接続―技術者のための (One Pointテキストシリーズ 11) posted with amazlet at 10.11.29 棚橋 隆彦 三恵社 売り上げランキング: 545133 |
|
|
このページの主な更新は Blog でお知らせします.
Copyright © 2006-2016 noocyte E-mail: relipmoced (a) yahoo.co.jp (" (a) " を半角のアットマークに書き替えてください.) リンクはご自由に. 「幾何学・CG のアルゴリズム集」に戻る. 「noocyte のプログラミング研究室」トップページに戻る. |