線分に矢印を描画する

ある線分(正確には,始点と終点の座標)が与えられた場合に,その線分を下図のように矢印で描画したいと言う要求に遭遇しました.ただし,今回の例では始点は常に原点 O(0,0) です.

今回は,与えられた線分を描画する際に,終点を重心 G(x0,y0) とする正三角形を同時に描画することによって矢印のように見せると言う方法を取りました.したがって,問題は“終点を重心とする正三角形の3点 ABC の座標を求める事”になります.

まず,点A を求める事から始めます.点A は,“線分OG の延長線上にあり,点G との距離が r である点”となります.r は定数とし,描画したい矢印の大きさによって適当に設定します.線分OG の直線の傾きをa,点G と点A の x軸方向の距離をx,点A の座標を (x1,y1) と置くと以下の式で求められます.ただし,今回は x0 = 0 の場合は考慮していません.

a = y0 / x0

x^2 + (a * x)^2 = r^2
  • > x = sqrt(r^2 / (a^2 + 1))
x1 = x0 + sqrt(r^2 / (a^2 + 1)) y1 = a * x1

ここまで計算できれば,残りの 2点 BC はベクトル GA をそれぞれ 120°,-120°回転したベクトルから容易に導出することができます.点B,点C の座標をそれぞれ (x2,y2), (x3,y3) と置くと

GA = (x1 - x0) + i * (y1 - y0)
GB = {(x1 - x0) + i * (y1 - y0)} * (cos120°+ i * sin120°)
   = -1/2 * (x1 - x0) - sqrt(3)/2 * (y1 - y0)
     + i * {sqrt(3)/2 * (x1 - x0) - 1/2 * (y1 - y0)}

したがって,
x2 = -1/2 * (x1 - x0) - sqrt(3)/2 * (y1 - y0) + x0
y2 = sqrt(3)/2 * (x1 - x0) - 1/2 * (y1 - y0) + y0

同様に,
GC = {(x1 - x0) + i * (y1 - y0)} * {cos(-120°) + i * sin(-120°)}
   = -1/2 * (x1 - x0) + sqrt(3)/2 * (y1 - y0)
     + i * {-sqrt(3)/2 * (x1 - x0) - 1/2 * (y1 - y0)}

したがって,
x3 = -1/2 * (x1 - x0) + sqrt(3)/2 * (y1 - y0) + x0
y3 = -sqrt(3)/2 * (x1 - x0) - 1/2 * (y1 - y0) + y0

これをプログラムで書くと次のようになります.

double x0 = ...;
double y0 = ...;
double r  = ...;

double a = (x0 == 0) ? 0 : y0 / x0;
double dist = std::sqrt(r * r / (a * a + 1));

double x1 = (x0 == 0) ? 0 : ((x0 > 0) ? x0 + dist : x0 - dist);
double y1 = (x1 != 0) ? a * x1 : ((y0 > 0) ? y0 + dist : y0 - dist);

double x2 = -((x1 - x0) + std::sqrt(3) * (y1 - y0)) / 2.0 + x0;
double y2 =  (std::sqrt(3) * (x1 - x0) - (y1 - y0)) / 2.0 + y0;

double x3 = -((x1 - x0) - std::sqrt(3) * (y1 - y0)) / 2.0 + x0;
double y3 = -(std::sqrt(3) * (x1 - x0) + (y1 - y0)) / 2.0 + y0;

中高レベルの数学でもパっとは解法を思いつかない事があり,やはり数学や物理はあまり得意ではないのだなぁと痛感します.