« ◇「対等でない」は説明にならない:双子のパラドクス | トップページ | ■光と縦波、横波;光の振幅とは »

↑矢印をプログラムで描く;単純ベクトル加算法

 矢印・矢尻の数学、直交するベクトルの加算

矢印の矢尻図形は基本となる線ベクトルに対し左右90°方向のベクトル と逆方向のベクトルを加えることにより得ることができます。 矢尻生成には三角関数も向きによる場合分けも不要です。

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

矢尻の設計図 : 単純な3角形と"かえし"のある実用形
プログラムによる描画(A点、B点を動かし色々な向きで同じ式で計算しています)

 矢尻端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分

コメントを書く



(ウェブ上には掲載しません)




トラックバック


この記事へのトラックバック一覧です: ↑矢印をプログラムで描く;単純ベクトル加算法:

« ◇「対等でない」は説明にならない:双子のパラドクス | トップページ | ■光と縦波、横波;光の振幅とは »