三角形の2点の座標と各辺の長さから、残る1点の座標を求める

下図△ABCの、AとBの座標、そして各辺の長さが分かっているときに、点Cの座標を求めるには。


点Aと点Bからの距離がそれぞれわかっているので、円の方程式を使って解くことはできる。
A(X_a, Y_a), B(X_b, Y_b),\\ a = BC, b = ACとすると
\left\{\begin{align}(x-X_a)^2+(y-Y_a)^2&=b^2\\(x-X_b)^2+(y-Y_b)^2&=a^2\end{align}\right.


これを解けば座標は出るだろうけど、なんともめんどくさい。
他に方法はないだろうか?と探してみて、見つけたのがコレ。
座標計算の公式 -土地家屋調査士の勉強してます。座標計算で、1秒でも- 数学 | 教えて!goo
回答番号:No.4 のものがプログラム的にも求めやすい。
ちょっと改造しつつ自分なりに書いてみる。

  • まず点Aが原点にくるよう点Aと点Bを平行移動


  • 点Bがx軸上にくるよう、原点(点A')を中心に回転移動


このときの角度\alphaは、arctanを使って求められる。
\begin{align}\tan\alpha&=\frac{(Y_b-Y_a)}{(X_b-X_a)}\\\alpha&=\arctan\frac{(Y_b-Y_a)}{(X_b-X_a)}\end{align}
ここでx軸上に移動してきたB'のx座標をdとすると、これは点Aと点Bの距離なのでピタゴラスの定理を使って求めることができるし、
\begin{align}d\cos\alpha&=X_b-X_a\\d&=\frac{X_b-X_a}{\cos\alpha}\end{align}
というように求めることもできる。

  • A'から距離b, B'から距離aの点を求める

x軸上にある2点から、それぞれの距離を使って点C'の座標を求める。

C'(x', y')とすると、
ピタゴラスの定理から
\left\{\begin{align}x'^2+y'^2&=b^2\\(d-x')^2+y'^2&=a^2\end{align}\right.
これを整理すると
x'=\frac{b^2-a^2+d^2}{2d}
となる。

また、△A'B'C'の面積Sは
S=\frac{1}{2}dy'で表され、
またヘロンの公式を使って
\begin{align}s&=\frac{a+b+d}{2}\\S&=\sqrt{s(s-a)(s-b)(s-d)}\end{align}
と表されるので、この2つから
y'=\frac{2\sqrt{s(s-a)(s-b)(s-d)}}{d}
が導きだされる。
y'は正負両方が存在することに注意。

  • 回転を元に戻し、平行移動も元に戻す

こうして求めた点C'のx, y座標を、元通りに角\alphaだけ回転させて\(X_a,Y_a\)だけ平行移動させることで、求めたかった点Cの座標を求めることができる。
\begin{align}x&=x'\cos\alpha\mp y'\sin\alpha+X_a\\y&=x'\sin\alpha\pm y'\cos\alpha+Y_a\end{align}

Perlで書くと

こんなカンジで求めることができる。

use Math::Trig 'atan';

sub calc_coordinate {
    my ($a, $b, $dist_from_a, $dist_from_b) = @_;

    my $alpha = atan(($b->{y} - $a->{y}) / ($b->{x} - $a->{x}));
    my $dist  = ($b->{x} - $a->{x}) / cos($alpha);
    my $s = ($dist_from_a + $dist_from_b + $dist) / 2;
    my $tempX = ($dist_from_a ** 2 - $dist_from_b ** 2 + $dist ** 2) / ($dist * 2);
    my $tempY = 2 * sqrt($s * ($s - $dist_from_a) * ($s - $dist_from_b) * ($s - $dist)) / $dist;

    return [{
        x => $tempX * cos($alpha) - $tempY * sin($alpha) + $a->{x},
        y => $tempX * sin($alpha) + $tempY * cos($alpha) + $a->{y},
    },
            {
        x => $tempX * cos($alpha) + $tempY * sin($alpha) + $a->{x},
        y => $tempX * sin($alpha) - $tempY * cos($alpha) + $a->{y},
    }];
}