↑矢印をプログラムで描く;単純ベクトル加算法
矢印・矢尻の数学、直交するベクトルの加算
矢印の矢尻図形は基本となる線ベクトルに対し左右90°方向のベクトル と逆方向のベクトルを加えることにより得ることができます。 矢尻生成には三角関数も向きによる場合分けも不要です。

簡単なことなのですが、2点"(Ax,Ay)-(Bx,By)"間に矢印を書きたい場合 の計算式が瞬時で出るかというとそうでもないので、ここにメモして おきます。実際に動く計算プログラム・ソースも載せます。
![]() |
![]() |
矢尻端2点を得る基本計算

点A(Ax,Ay)から点B(Bx,By)に矢印線を引きたいとします。
矢尻の幅を2w、長さをhとします。
この条件で矢尻の左端L(Lx,Ly)と右端R(Rx,Ry)を算出します。
左方向、右方向、逆方向の単位ベクトルを得ます。
基本ベクトル : Vx = Bx - Ax Vy = By - Ay 基本ベクトル長 : v = sqrt(Vx×Vx + Vy×Vy) 基本単位ベクトル : Ux = Vx÷v Uy = Vy÷v 左方向単位ベクトル : Lux = -Uy Luy = Ux 右方向単位ベクトル : Rux = Uy Ruy = -Ux 逆方向単位ベクトル : Nux = -Ux Nuy = -Uy
従って左端、右端は次のようになります。
左端 : Lx = Bx + Lux×w + Nux×h = Bx - Uy×w - Ux×h Ly = By + Luy×w + Nuy×h = By + Ux×w - Uy×h 右端 : Rx = Bx + Rux×w + Nux×h = Bx + Uy×w - Ux×h Ry = By + Ruy×w + Nuy×h = By - Ux×w - Uy×h
矢尻端2点を得るプログラム例(Javascript+canvas)
実際に計算するプログラムを示します。javascriptを使いcanvasに出力しています。
左の2本の計算プログラムは次のようになっています。 右の2本は次項で説明
function arrowPos(A,B,w,h,L,R){ //A,B,L,Rは[0]:x [1]:y var Vx= B[0]-A[0]; var Vy= B[1]-A[1]; var v = Math.sqrt(Vx*Vx+Vy*Vy); var Ux= Vx/v; var Uy= Vy/v; L[0]= B[0] - Uy*w - Ux*h; L[1]= B[1] + Ux*w - Uy*h; R[0]= B[0] + Uy*w - Ux*h; R[1]= B[1] - Ux*w - Uy*h; }ここに示したのはJavascriptですが他の言語での実現も容易な はずです。
■裸眼立体視3Dアニメート数式プロッタ ではJavaで全く同等の計算を行いGraphics2Dで矢印を書いています。
本記事の主眼ではありませんが、参考までに本記事でのcanvas制御 プログラムも載せます。
少し凝った矢印(かえしのある矢尻)

LとRの間にもう一点置けばより矢印らしくなります。
上の回転矢印表示の右のものです。
中点M(Mx,My)は単純に逆方向ベクトルをh2の長さ分足したものです。
実際の計算プログラムを示します。
function arrowPos2(A,B,w,h,h2,L,R,M){ //A,B,L,R,Mは[0]:x [1]:y var Vx= B[0]-A[0]; var Vy= B[1]-A[1]; var v = Math.sqrt(Vx*Vx+Vy*Vy); var Ux= Vx/v; var Uy= Vy/v; L[0]= B[0] - Uy*w - Ux*h; L[1]= B[1] + Ux*w - Uy*h; R[0]= B[0] + Uy*w - Ux*h; R[1]= B[1] - Ux*w - Uy*h; M[0]= B[0] - Ux*h2; M[1]= B[1] - Uy*h2; }なお、基本線をAからMで描くと太い線でも線端が矢尻を突き抜けることが ありません。
白抜きの矢印

点を増やして白抜きの矢印にします。
基本点Aは描画しません。
AR-MR-R-B-L-ML-AL-AR一周で矢印図形となります。
実際の計算プログラムを示します。
function arrowPos3(A,B,w,w2,h,h2,L,R,ML,MR,AL,AR){ var Vx= B[0]-A[0]; var Vy= B[1]-A[1]; var v = Math.sqrt(Vx*Vx+Vy*Vy); var Ux= Vx/v; var Uy= Vy/v; L[0] = B[0] - Uy*w - Ux*h; L[1] = B[1] + Ux*w - Uy*h; R[0] = B[0] + Uy*w - Ux*h; R[1] = B[1] - Ux*w - Uy*h; ML[0] = B[0] - Uy*w2 - Ux*h2; // L[0]のwがw2,hがh2 ML[1] = B[1] + Ux*w2 - Uy*h2; MR[0] = B[0] + Uy*w2 - Ux*h2; // R[0]のwがw2,hがh2 MR[1] = B[1] - Ux*w2 - Uy*h2; AL[0] = A[0] - Uy*w2; AL[1] = A[1] + Ux*w2; AR[0] = A[0] + Uy*w2; AR[1] = A[1] - Ux*w2; }
余談:放物線上の矢の動き
白抜きの矢印は当初、頭と尻がそれぞれ同じ放物線上を
少しずれて動くようにしていました。
放物線上の速度は頂上が遅く、左右の峰は下ほど速いので
それをたどる2つの点の距離、即ち矢印の長さは、一定
ではありません。
それはそれで本記事の主眼から外れるものではなかったのですが、
一定の長さの矢印の方が見る人に違和感が少ないだろうと、
一定長になるよう変更しました。
すると、なんだか出来の悪い映画で見るCGの矢の動きのような、 確かにスムーズなんだけど、何かをなぞっているだけで 重み感がないものになってしまいました。
単なる線図に過ぎないので重み感や実物感など全く不要な はずなのに、なんだか気持が悪いのです。
そこで、矢の傾きを放物線の接線から少しずらすようにしました。 基本式を「矢の中心が放物線の接線をたどる」とした上で傾きが少し 遅延し接線からずれる形をとりました。
加えてx方向の速度を徐々に遅くしました。
本記事の目的から外れすぎますので、いずれ別記事で色々検討します。
###
2017/05/18
・ブラウザによっては式の一部でカラムずれが起こることに対処
しかし、うまくいっていない。本当に英米のものは空白が難しい
・余計な雑談を削除
| 固定リンク
コメント
素晴らしい内容をありがとうございます。
投稿: yoh | 2013年6月21日 (金) 16時15分
わかりやすくて助かりました。ありがとうございます。
投稿: | 2014年1月20日 (月) 01時15分
非常に明快で解りやすく助かりました
投稿: 野口雅史 | 2015年5月 7日 (木) 10時49分
nice!
投稿: kotaro | 2015年9月 3日 (木) 11時48分
楽しく拝見いたしました。
ありがとうございます!
投稿: 師子乃 | 2017年11月28日 (火) 13時29分