2018-09-18 
Qiita コミュニティ 2次曲線の式から楕円パラメータを導出する 
にあった:変換計算式の追加。
 此処での変換式は、楕円の角度を求めるとき以外三角関数を使用せず、二元二次式の係数から全て求めています。
問題なく 長半径、短半径、角度、中心座標を求められます。
 
2017-08-16 新しい変換計算の確認
 二元二次式から問題なく楕円の一般式 に変換できる計算方法があったので確認をしました。
以前の計算では、楕円の角度が45°の時、半径a, bの値が正確に求めることが出来ませんでした。
 計算方法は、"楕円と楕円の衝突"その7 楕円と楕円の衝突に楕円の縦横変倍があるのですが、縦横変倍に二元二次式を利用していて、その中で二元二次式から一般式に変換を行っている計算があり、その計算で、どのような楕円の二元二次式の値でも正しく、長半径、短半径、角度、中心座標が求められことが分かりました。
これで、無駄な座標変換と、再計算は必要なくなります。

楕円の最小二乗法、ガウスジョルダン法による 二元二次方程式の係数推定値から、楕円一般式変換時の問題確認

 楕円の二元二次方程式から、楕円の一般式に変換する計算の確認をしてみました。
楕円近似のプログラムに組み込まれていますが、楕円の角度45°の時の、半径a,bの値がズレ、座標変換をして半径を求める方法を追加したので、その時の検討内容です。
最小二乗法、ガウスジョルダンにより楕円の二元二次方程式を求めた時、楕円の一般式に変換しないと、楕円の描画が面倒になります。
楕円の一般式から、二元二次方程式に変換する場合は、計算は面倒ですが、確実に変換することが可能です。
但し、Sin関数、Cos関数等による桁落ちの問題はあります。 


 座標の値から、楕円の二元二次方程式の係数を求める場合、楕円の座標(X、Y)の点データーが最低5個あれば、AX^2+BXY+CY^2+DX+EY+Fの各係数の値を求めることが出来ます。
A~F迄の値をすべて求めるのは困難なので、Aの値を1に、或いはFの値を1に固定して、求めます。
二元二次方程式にしてしまうと、A~Fの値は、比率さえ変更しなければ、どんな値でもよく無限に存在するので、どれかを最初に固定して求める必要があります。

 楕円の軸角 θ Arctan2(B, A-C) / 2 で、各値の比率が変わらなければ、簡単に求めることが出来ます。
又、中心座標も、Aの値がになる様に係数を変更すれば、できます。
今までの計算方法では、半径は、三角関数sincosを使用して、求めますが、軸角45°の時、sincosの値が同じになり、正しい値を求めることが出来ません。
しかし、新しい計算方法だと、問題なく正しい値を計算することができます。

"楕円変換V2"のボタンが、楕円と楕円の衝突にあった計算方法の実行ボタンです。
"楕円変換V3"のボタンが、2次曲線の式から楕円パラメータを導出するによる計算の実行ボタンです。

 楕円の半径a,b、中心座標Xo,Yo、角度θ を、二元二次方程式に変換し、各係数を求め、更に元の半径a,b、中心座標Xo,Yo、角度θ へ逆算するプログラムです。
上図左側は、二元二次に変換し、そのまま、逆変換した場合です。右側は、Aの値をにした場合です。
楕円の角度が45°の場合、二元二次の係数の係数から、半径を計算しようとすると、上図右の様に半径a,bが同じ値になってしまいます。
プログラムは、イメージングソリューションにある逆変換の計算式を使用した場合です。
Fの値を1にして、逆変換する計算式も組み込んでみましたが、計算結果は同じように、半径a,bが同じ値になってしまいます。
色々なパターンについて、確認をすると、イメージングソリューションの逆変換計算は、45°ジャストの時に半径a,bが同じ値になり、僅かでもズレれば、かなり正解に近い値を得られるようです。
同じ様に、最小二乗法のプログラムで、楕円変換式のAtoPの計算も45°に近い角度の場合正しい半径の値を得ることが出来ません。
最小二乗法による計算の場合、元のデーターは、X,Yの座標データーであり、楕円の角度は正しい答えが得られることから、座標変換をして45°以外の角度にすれば、正しい半径の値を得ることが出来ます。

 新しい その7 楕円と楕円の衝突 2次曲線の式から楕円パラメータを導出する の方法であれば、楕円の二元二次の値であれば、いつでも正しい結果が得られることがわかります。

 次の計算例は、最小二乗法による楕円の係数を求めるプログラムで、45°の楕円の座標から二元二次の係数を求め、半径a,b、中心座標Xo,Yo、角度θイメージングソリューションの計算で求めている場合です。

ここのプログラムは、最小二乗法のプログラムで、楕円近似の古いプログラムです。


 上図左は、演算精度Doubleで、右はEXtendedで計算しています。
計算のデーターは、長半径が50で、短半径が40、中心座標0,0、軸角45°です。
不思議な事に、Doubleと、Extendedでは、半径a.bの値が逆になり、軸角+-逆になっています。
結果的には同じですが、やはり、Extendedの方が正解に近いです。
ジャスト45だと、半径a.bが同じになってしまう筈ですが、誤差が小さいので、45°表示されていますが、計算値はジャスト45°に成ってないようです。 
試しにプログラムの中のArctan2(A, 1-B) / 2の値を if B=1 then **** とプログラムしてみると、1になっていないことが確認できます。
角度も、pi/4と比較すると、ジャスト45°ではないようです。

 左図は、デバッグモードでArctan2(A, 1-B) / 2のBの値を1に強制変更した場合です。
上図は、デバッグ時の評価変更の画面で、評価時Bの値は1と表示されます。
しかし、新しい値に1といれ、新しい値に変更すると、左図の様に、半径a,bが同じ値になり円になります。
要するに、45°近辺の時、二進浮動小数点の値を、十進の値に変更して表示するとき、丸められて表示されない桁の値で計算されていることになります。


 次は0°に座標変換して、半径a.bの値を求めたものです。
新しい計算方法であれば座標変換をしなくても正しい値を得ることができます。

 計算精度は、Doubleですが、半径は、かなり精度の高い結果となっています。
元のデーターが小数点以下5桁に対して、半径も小数点以下5桁の精度になっていてかなり良い結果となっています。


 ガウスジョルダン法によるプログラム例

ガウスジョルダン法で、楕円の5点のデーターを入力して、楕円の値を求めるプログラムを検討してみました。
"新しい変換計算"にチェックを入れると、"座標変換"のチェックの有無に関わらず、座標変換計算は行われません。

 新しい計算方法であればいつでも正しい結果が得られます。

変換確認プログラム

unit Main;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls, math;

type
  TForm1 = class(TForm)
    Image1: TImage;
    Panel1: TPanel;
    Ellipse1Box: TGroupBox;
    radius_a1_Edit: TLabeledEdit;
    radius_b1_Edit: TLabeledEdit;
    Center_x1_Edit: TLabeledEdit;
    Center_y1_Edit: TLabeledEdit;
    angle_q1_Edit: TLabeledEdit;
    Button2: TButton;
    CheckBox1: TCheckBox;
    RadioButton1: TRadioButton;
    RadioButton2: TRadioButton;
    GroupBox1: TGroupBox;
    LabeledEdit7: TLabeledEdit;
    LabeledEdit8: TLabeledEdit;
    LabeledEdit9: TLabeledEdit;
    LabeledEdit10: TLabeledEdit;
    LabeledEdit11: TLabeledEdit;
    Button4: TButton;
    LabeledEdit12: TLabeledEdit;
    Label1: TLabel;
    CheckBox2: TCheckBox;
    GroupBox2: TGroupBox;
    LabeledEdit1: TLabeledEdit;
    LabeledEdit2: TLabeledEdit;
    LabeledEdit3: TLabeledEdit;
    LabeledEdit4: TLabeledEdit;
    LabeledEdit5: TLabeledEdit;
    LabeledEdit6: TLabeledEdit;
    Button1: TButton;
    Button3: TButton;
    Label2: TLabel;
    Button5: TButton;
    Label3: TLabel;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
    function  datainput(var A, B, C, D, E, F: double): boolean;
    procedure elementsCalc;                     // 各要素計算
    procedure elementCalc;
    procedure quadratic_transform(a, b, x, y, qdeg: double; var A0, B0, C0, D0, E0, F0: double);
    procedure imageClear;
    procedure PtoA;                             // 係数変換 P to A
    procedure AtoP;                             // 係数変換 A to P
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
var
  A1, B1, C1, D1, E1, F1 : double;
  LL            : string;
  Q             : double;                         // 軸角
  Xo, Yo        : double;                         // 原点
  Ar, Br        : double;

// 画面の消去
procedure TForm1.imageClear;
begin
  image1.Canvas.Brush.Style := bsSolid;
  image1.Canvas.Brush.Color := clWhite;
  image1.Canvas.FillRect(Rect(0, 0, image1.Width, image1.Height));
end;

//-------------------------------------
// AX^2 + BXY + CY^2 + DX + EY + F = 0
// 二元二次楕円式の値の入力処理
//-------------------------------------
function TForm1.datainput(var A, B, C, D, E, F: double): boolean;
var
  ch : integer;
begin
  result := false;
  val(LabeledEdit1.Text, A, ch);
  if ch <> 0 then begin
    application.MessageBox('Aの入力に誤りがあります', '注意', 0);
    exit;
  end;
  val(LabeledEdit2.Text, B, ch);
  if ch <> 0 then begin
    application.MessageBox('Bの入力に誤りがあります', '注意', 0);
    exit;
  end;
  val(LabeledEdit3.Text, C, ch);
  if ch <> 0 then begin
    application.MessageBox('Cの入力に誤りがあります', '注意', 0);
    exit;
  end;
  val(LabeledEdit4.Text, D, ch);
  if ch <> 0 then begin
    application.MessageBox('Dの入力に誤りがあります', '注意', 0);
    exit;
  end;
  val(LabeledEdit5.Text, E, ch);
  if ch <> 0 then begin
    application.MessageBox('Eの入力に誤りがあります', '注意', 0);
    exit;
  end;
  val(LabeledEdit6.Text, F, ch);
  if ch <> 0 then begin
    application.MessageBox('Fの入力に誤りがあります', '注意', 0);
    exit;
  end;
  result := true;
end;

//-----------------------------------------------------------------------
// 此処での計算は、楕円の全ての二元二次式から 楕円の係数を計算できます
//  AX^2 + BXY + CY^2 + DX + EY + F
//  h,v           中心座標
//  Q:            楕円の傾き角
//  ar,br         楕円の半径
//-----------------------------------------------------------------------
procedure TForm1.Button3Click(Sender: TObject);
var
  xy, cosQ, sinQ    : double;
  h, v              : double;
  At, Bt            : double;
  kd                : double;
  ar, br            : double;
  A, B, C, D, E, F  : double;
begin
  if not datainput(A, B, C, D, E, F) then exit;
 // 楕円の各係数計算
  xy := (B * B - 4 * A * C);
  h  := (2 * D * C - E * B) / xy;   // 原点からのX方向移動量
  v  := (2 * A * E - D * B) / xy;   // 原点からのy方向移動量
  Q :=  - arctan2(B, C - A) / 2;    // 標準楕円からの回転角
  cosQ := cos(Q);
  sinQ := sin(Q);
  at := A * cosQ * cosQ + B * cosQ * sinQ + C * sinQ * sinQ;
  bt := A * sinQ * sinQ - B * cosQ * sinQ + C * cosQ * cosQ;
  kd := A * h * h + B * h * v + C * v * v + D * h + E * v + F;
  imageClear;
  image1.Canvas.TextOut(10, 0,' 楕円変換V2');
  if (at * kd > 0) or (bt * kd > 0) then begin
    image1.Canvas.TextOut(10, 120,' 半径が虚数なので双曲線です。');
    ar := sqrt(abs(Kd / at));           // 虚数半径a
    br := sqrt(abs(kd / bt));           // 虚数半径b
    image1.Canvas.TextOut(20,  20, '半径   a= ' + floatTostrF(ar, ffGeneral, 8, 3) + 'i');
    image1.Canvas.TextOut(20,  40, '半径   b= ' + floatTostrF(br, ffGeneral, 8, 3) + 'i');
  end
  else begin
    ar := sqrt(-Kd / at);           // 半径a
    br := sqrt(-kd / bt);           // 半径b
    image1.Canvas.TextOut(20,  20, '半径   a= ' + floatTostrF(ar, ffGeneral, 8, 3));
    image1.Canvas.TextOut(20,  40, '半径   b= ' + floatTostrF(br, ffGeneral, 8, 3));
  end;
  image1.Canvas.TextOut(20,  60, '中心座標 X= ' + floatTostrF(h,  ffGeneral, 8, 3));
  image1.Canvas.TextOut(20,  80, '中心座標 Y= ' + floatTostrF(v,  ffGeneral, 8, 3));
  image1.Canvas.TextOut(20, 100, '角度   θ= ' + floatTostrF(Q / pi * 180, ffGeneral, 10, 3)+'°');
end;

//--------------------------------------------------------------
// 楕円の二元二次から楕円係数変換
// AX^2 + 2BXY + CY^2 + 2DX + 2EY + F
// sinθ cosθを使用しない方法
// 参照 https://qiita.com/haniwo820/items/98670ea4abc545cf86b6
//--------------------------------------------------------------
procedure TForm1.Button5Click(Sender: TObject);
var
  x0, y0: double;
  ar, br, a2, b2 : double;
  ab: double;
  A, B, C, D, E, F  : double;
begin
  if not datainput(A, B, C, D, E, F) then exit;
  B := B / 2;
  D := D / 2;
  E := E / 2;
  x0 := 0;
  y0 := 0;
  a2 := 0;
  b2 := 0;
  if A * C - B * B <> 0 then begin
    x0 := (B * E - C * D) / (A * C - B * B);
    y0 := (B * D - A * E) / (A * C - B * B);
    ab := 2 * ((C * D - B * E) * D + (A * E - B * D) * E - (A * C - B * B) * F);
    if A + C - sqrt((A - C) * (A - C) + 4 * B * B) <> 0 then
      a2 := ab / (A * C- B * B) / (A + C - sqrt((A - C) * (A - C) + 4 * B * B));
    if A + C + sqrt((A - C) * (A - C) + 4 * B * B) <> 0 then
      b2 := ab / (A * C- B * B) / (A + C + sqrt((A - C) * (A - C) + 4 * B * B));
  end;
  ar := sqrt(abs(a2));
  br := sqrt(abs(b2));
  Q := - arctan2((2 * B) , (C - A)) / 2;
  imageClear;
  image1.Canvas.TextOut(10, 0,' 楕円変換V3');
  if (a2 < 0) or (b2 < 0) then begin
    image1.Canvas.TextOut(10, 120,' 半径が虚数なので双曲線です。');
    image1.Canvas.TextOut(20,  20, '半径   a= ' + floatTostrF(ar, ffGeneral, 8, 3) + 'i');
    image1.Canvas.TextOut(20,  40, '半径   b= ' + floatTostrF(br, ffGeneral, 8, 3) + 'i');
  end
  else begin
    image1.Canvas.TextOut(20,  20, '半径   a= ' + floatTostrF(ar, ffGeneral, 8, 3));
    image1.Canvas.TextOut(20,  40, '半径   b= ' + floatTostrF(br, ffGeneral, 8, 3));
  end;
  image1.Canvas.TextOut(20,  60, '中心座標 X= ' + floatTostrF(x0,  ffGeneral, 8, 3));
  image1.Canvas.TextOut(20,  80, '中心座標 Y= ' + floatTostrF(y0,  ffGeneral, 8, 3));
  image1.Canvas.TextOut(20, 100, '角度   θ= ' + floatTostrF(Q / pi * 180, ffGeneral, 10, 3)+'°');
end;

//----------------------------------------------------
// A(x-a)^2 + 2Bxy + C(y-c) = K
// をAx^2 + Bxy + Cy^2 + Dx + Ey + F = 0 に変換します
//----------------------------------------------------
procedure TForm1.Button4Click(Sender: TObject);
var
  ch          : integer;
  A, B, C, K  : double;
  sa, sc      : double;
begin
  val(LabeledEdit7.Text,A,ch);
  if ch <> 0 then begin
    application.MessageBox('Aは数値ではありません。', 'A', 0);
    exit;
  end;
  val(LabeledEdit8.Text,B,ch);
  if ch <> 0 then begin
    application.MessageBox('Bは数値ではありません。', 'B', 0);
    exit;
  end;
  val(LabeledEdit9.Text,C,ch);
  if ch <> 0 then begin
    application.MessageBox('Cは数値ではありません。', 'C', 0);
    exit;
  end;
  val(LabeledEdit10.Text,sa,ch);
  if ch <> 0 then begin
    application.MessageBox('aは数値ではありません。', 'a', 0);
    exit;
  end;
  val(LabeledEdit11.Text,sc,ch);
  if ch <> 0 then begin
    application.MessageBox('cは数値ではありません。', 'c', 0);
    exit;
  end;
  val(LabeledEdit12.Text,K,ch);
  if ch <> 0 then begin
    application.MessageBox('Kは数値ではありません。', 'K', 0);
    exit;
  end;
  LabeledEdit1.Text := floattostr(A);
  LabeledEdit2.Text := floattostr(2 * B);
  LabeledEdit3.Text := floattostr(C);
  LabeledEdit4.Text := floattostr(-2 * A * sa);
  LabeledEdit5.Text := floattostr(-2 * C * sc);
  LabeledEdit6.Text := floattostr(A * sa * sa  + C * sc * sc - K);
end;

//--------------------------------------------
// Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0 から
// 通常の楕円データーに変換します
// Xo,Yo     // 中心座標
// Q:        // 楕円の傾き角
// Ao,Bo     // 楕円の半径
//--------------------------------------------
procedure TForm1.Button1Click(Sender: TObject);
begin
  // Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0 の A,B,C,D,E,F の入力
  if not datainput(A1, B1, C1, D1, E1, F1) then exit;
  if RadioButton1.Checked then
    if CheckBox1.Checked then elementCalc       // 半径a, b 傾き角θ 中心座標Xo,yo 計算(画像ソリューション)
                         else elementsCalc;     // 半径a, b 傾き角θ 中心座標Xo,yo 計算(最初の楕円近似計算)
  if RadioButton2.Checked then AtoP;            // 係数変換 A to P
end;

//--------------------------------------------------------
// 楕円の方程式 二元二次方程式への変換
// AX^2 + BXY + CY^2 + DX + EY + F の A,B,C,D,E,F
// 二元二次方程式から更に
//      A(x-a)^2 + 2Bxy + C(y-c)^2 = 1  にへんかんします。
//--------------------------------------------------------
procedure TForm1.Button2Click(Sender: TObject);
var
  Ch : integer;
  a, c : double;
  A0, B0, C0, K : double;
begin
// 楕円1のデーター
  val(radius_a1_Edit.Text, ar, ch);
  if ch <> 0 then begin
    application.MessageBox('楕円の半径a数値ではありません。', '楕円の半径a', 0);
    exit;
  end;
  if ar <= 0 then begin
    application.MessageBox('楕円の半径aがゼロ 又はゼロ以下です。', '楕円の半径a', 0);
    exit;
  end;
  val(radius_b1_Edit.Text, br, ch);
  if ch <> 0 then begin
    application.MessageBox('楕円の半径b数値ではありません。', '楕円の半径b', 0);
    exit;
  end;
  if br <= 0 then begin
    application.MessageBox('楕円の半径bがゼロ 又はゼロ以下です。', '楕円の半径b', 0);
    exit;
  end;
  val(Center_x1_Edit.Text, Xo, ch);
  if ch <> 0 then begin
    application.MessageBox('楕円の中心位置x数値ではありません。', '楕円の中心位置x', 0);
    exit;
  end;
  val(Center_y1_Edit.Text, Yo, ch);
  if ch <> 0 then begin
    application.MessageBox('楕円の中心位置y数値ではありません。', '楕円の中心位置y', 0);
    exit;
  end;
  val(angle_q1_Edit.Text, q, ch);
  if ch <> 0 then begin
    application.MessageBox('楕円の角度θ数値ではありません。', '楕円の角度θ', 0);
    exit;
  end;
  // 係数変換 Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0;   (F = -1)
  if RadioButton2.Checked then PtoA;
  // 係数変換 Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0;   (F <> -1)
  if RadioButton1.Checked then quadratic_transform(ar, br, Xo, Yo, q, A1, B1, C1, D1, E1, F1);
  LabeledEdit1.Text := floatTostr(A1);
  LabeledEdit2.Text := floatTostr(B1);
  LabeledEdit3.Text := floatTostr(C1);
  LabeledEdit4.Text := floatTostr(D1);
  LabeledEdit5.Text := floatTostr(E1);
  LabeledEdit6.Text := floatTostr(F1);
// A(x-a)^2 + 2Bxy + C(y-c)^2 = 1  の式へ変換します
  A0 := A1;
  B0 := B1 / 2;
  C0 := C1;
  a := -D1 / 2 / A0;
  c := -E1 / 2 / C0;
  K := A0 * a * a + C0 * c * c - F1;
  A0 := A0 / K;
  B0 := B0 / K;
  C0 := C0 / K;
  K := 1;
  LabeledEdit7.Text := floatTostr(A0);
  LabeledEdit8.Text := floatTostr(B0);
  LabeledEdit9.Text := floatTostr(C0);
  LabeledEdit10.Text := floatTostr(a);
  LabeledEdit11.Text := floatTostr(c);
  LabeledEdit12.Text := floatTostr(K);
end;

//--------------- F使用ルーチンを追加した場合 使用可------------
// 楕円近似 最小二乗法ので最初に使用した計算方法
// 計算の中にFの値が有りませんでした
// 改良しFを使用したルーチン追加してみました。
// 角度θ, 中心座標 Xo, Y0 から K=Axo^2+Bxoyo+Cyo^2+Dxo+Eyo+Fで
// 計算されたKの値を使用しないと正しい答えが出ません
//---------------------------------------------------------------
procedure TForm1.elementsCalc;                    // 各要素計算
var
  A, B, C, D, E    : double;
  K          : double;
begin
  A := A1 / 2;
  B := B1 / 2;
  C := C1 / 2;
  D := D1 / 2;
  E := E1 / 2;
  if (A1 * C1 - B * B) = 0 then        // 分母が0なのか確認する
    begin
      LL := '入力した値に誤りがあります。';
      application.MessageBox(pointer(LL),'注意',0);
      exit;
    end;
  Xo := (B * E - C1 * D) / (A1 * C1 - B * B);
  Yo := (B * D - A1 * E) / (A1 * C1 - B * B);

  Q := - arctan2(B1, C1 - A1);                      // 軸角の二倍
  Ar := (A - C) * cos(Q) + B * sin(Q) + (A + C);
  Br := (C - A) * cos(Q) - B * sin(Q) + (A + C);
  imageClear;
  if (Ar = 0) or (Br = 0) then begin
    image1.Canvas.TextOut(10, 120,' 半径がゼロで楕円ではありません。');
    exit;
  end;
  // Fの値を使用しない場合
  if not CheckBox2.Checked then begin
    image1.Canvas.TextOut(10, 0,' 楕円近似 K= 無し');
    if (Ar < 0) or (Br < 0) then begin
      image1.Canvas.TextOut(10, 120,' 半径が虚数なので双曲線です。');
      Ar := abs(Ar);
      Br := abs(Br);
    end;
    Ar := 1 / sqrt(Ar);
    Br := 1 / sqrt(Br);
    image1.Canvas.TextOut(20,  20, '半径   a= ' + floatTostrF(Ar, ffGeneral, 8, 3));
    image1.Canvas.TextOut(20,  40, '半径   b= ' + floatTostrF(Br, ffGeneral, 8, 3));
  end
  // Fの値を使用した場合 K=Ax^2+Bxy+Cy^2+Dx+Ey+F 追加
  else begin
    image1.Canvas.TextOut(10, 0,' 楕円近似 K= 追加');
    K := A1 * Xo * Xo + B1 * Xo * Yo + C1 * Yo * Yo + D1 * Xo + E1 * Yo + F1;
    if (Ar * K > 0) or (Br * K > 0) then begin
      image1.Canvas.TextOut(10, 120,' 半径が虚数なので双曲線です。');
      Ar := sqrt(abs(K / Ar));
      Br := sqrt(abs(K / Br));
      image1.Canvas.TextOut(20,  20, '半径   a= ' + floatTostrF(Ar, ffGeneral, 8, 3) + 'i');
      image1.Canvas.TextOut(20,  40, '半径   b= ' + floatTostrF(Br, ffGeneral, 8, 3) + 'i');
    end
    else begin
      Ar := sqrt(-K / Ar);
      Br := sqrt(-K / Br);
      image1.Canvas.TextOut(20,  20, '半径   a= ' + floatTostrF(Ar, ffGeneral, 8, 3));
      image1.Canvas.TextOut(20,  40, '半径   b= ' + floatTostrF(Br, ffGeneral, 8, 3));
    end;
  end;
  Q := Q / 2;                                       // 軸角
  image1.Canvas.TextOut(20,  60, '中心座標 X= ' + floatTostrF(Xo, ffGeneral, 8, 3));
  image1.Canvas.TextOut(20,  80, '中心座標 Y= ' + floatTostrF(Yo, ffGeneral, 8, 3));
  image1.Canvas.TextOut(20, 100, '角度   θ= ' + floatTostrF(q / pi * 180, ffGeneral, 10, 3)+'°');
end;

//-------------------使用不可-----------------
// 楕円二元二次方程式を 楕円の通常式に変換
// 画像ソリューションにある計算方法
// 45°の時正しく半径を変換できません
//--------------------------------------------
procedure TForm1.elementCalc;                     // 各要素計算
var
  A,B,C,D,E : double;
  XPY2, EC2, XMY2, ES2, SWS: double;
begin
//  Xo,Yo     // 中心座標
//  Q:        // 楕円の傾き角
//  Ao,Bo     // 楕円の半径
// 行列inv_a と 行列bの積の結果から、直接c1[]を使用しても良いのだか
// 計算を分かりやすくするため、A~Eに値をコピーする
  A := B1 / A1;
  B := C1 / A1;
  C := D1 / A1;
  D := E1 / A1;
  E := F1 / A1;
// 中心座標計算
  if (4 * B - A * A) = 0 then        // 分母が0なのか確認する
    begin
      LL := '入力した値に誤りがあります。';
      application.MessageBox(pointer(LL),'注意',0);
      exit;
    end;
  Xo := (A * D - 2 * B * C) / (4 * B - A * A);
  Yo := (A * C - 2 * D) / (4 * B - A * A);
// 傾き角計算
  Q := - arctan2(A ,(B - 1)) / 2;
//  if B = 1 then Q := pi / 4
//           else Q := arctan(A / (1 - B)) / 2;
//  Q  := arctan(A / (1 - B)) / 2 - pi / 2;       // 角度を90どずらすと、半径 A と B が逆になる
// 半径 A 計算
  XPY2 := (Xo * cos(Q) + Yo * sin(Q)) * (Xo * cos(Q) + Yo * sin(Q));
  EC2  := E * cos(Q) * cos(Q);
  XMY2 := (Xo * sin(Q) - Yo * cos(Q)) * (Xo * sin(Q) - Yo * cos(Q));
  ES2  := E * sin(Q) * sin(Q);
  SWS  := (sin(Q) * sin(Q) - B * cos(Q) * cos(Q)) / (cos(Q) * cos(Q) - B * sin(Q) * sin(Q));
  Ar   := sqrt(abs(XPY2 - EC2 - (XMY2 - ES2) * SWS));
// 半径 B 計算
  XPY2 := (Xo * sin(Q) - Yo * cos(Q)) * (Xo * sin(Q) - Yo * cos(Q));
  EC2  := E * sin(Q) * sin(Q);
  XMY2 := (Xo * cos(Q) + Yo * sin(Q)) * (Xo * cos(Q) + Yo * sin(Q));
  ES2  := E * cos(Q) * cos(Q);
  SWS  := (cos(Q) * cos(Q) - B * sin(Q) * sin(Q)) / (sin(Q) * sin(Q) - B * cos(Q) * cos(Q));
  Br   := sqrt(abs(XPY2 - EC2 - (XMY2 - ES2) * SWS));
  imageClear;
  image1.Canvas.TextOut(10, 0,' 画像処理 ソリューション');
  image1.Canvas.TextOut(20,  20, '半径   a= ' + floatTostrF(Ar, ffGeneral, 8, 3));
  image1.Canvas.TextOut(20,  40, '半径   b= ' + floatTostrF(Br, ffGeneral, 8, 3));
  image1.Canvas.TextOut(20,  60, '中心座標 X= ' + floatTostrF(Xo, ffGeneral, 8, 3));
  image1.Canvas.TextOut(20,  80, '中心座標 Y= ' + floatTostrF(Yo, ffGeneral, 8, 3));
  image1.Canvas.TextOut(20, 100, '角度   θ= ' + floatTostrF(q / pi * 180, ffGeneral, 10, 3)+'°');
end;

//-------------------------------------------------
// 楕円の方程式 二次方程式への変換
// AX^2 + BXY + CY^2 + DX + EY + F の A,B,C,D,E,F
// F <> -1 と A = 1
//-------------------------------------------------
procedure TForm1.quadratic_transform(a, b, x, y, qdeg: double; var A0, B0, C0, D0, E0, F0: double);
var
  Q                 : double;
  sin_Q, cos_q      : double;
  ah2, bh2          : double;
  sinh2, cosh2      : double;
  sincosQ           : double;
  xh2, yh2, xy      : double;
begin
  Q := qdeg / 180 * pi;     // ラジアン単位に変換
  sin_Q := sin(Q);
  cos_Q := cos(Q);
  ah2 := a * a;
  bh2 := b * b;
  sinh2 := sin_Q * sin_Q;
  cosh2 := cos_Q * cos_Q;
  sincosQ := sin_Q * cos_Q;
  xh2 := x * x;
  yh2 := y * y;
  xy  := x * y;
  A0 := cosh2 / ah2 + sinh2 / bh2;
  B0 := 2 * (1 / ah2 - 1 / bh2) * sincosQ;
  C0 := sinh2 / ah2 + cosh2 / bh2;
  D0 := -2 * ((x * cosh2 + y * sincosQ) / ah2 + (x * sinh2 - y * sincosQ) / bh2);
  E0 := -2 * ((x * sincosQ + y * sinh2) / ah2 + (y * cosh2 - x * sincosQ) / bh2);
  F0 :=  (xh2 * cosh2 + 2 * xy * sincosQ + yh2 * sinh2) / ah2
       + (xh2 * sinh2 - 2 * xy * sincosQ + yh2 * cosh2) / bh2
       - 1;
  // A = 1 に変換します
  if CheckBox1.Checked then begin
    B0 := B0 / A0;
    C0 := C0 / A0;
    D0 := D0 / A0;
    E0 := E0 / A0;
    F0 := F0 / A0;
    A0 := A0 / A0;
  end;
end;

//-----------------------------------------------------------
// 楕円の方程式 Ax^2 + Bxy + Cy^2 + Dx + Ey + F に変換
// F = -1 に変換
// ○/F が無ければ通常の変換ですがその時はFの符号を反転します
//-----------------------------------------------------------
procedure TForm1.PtoA;            // 係数変換 P to A
var
  C, S, F: double;
begin
  Q := q / 180 * pi;
  C  := COS(Q);
  S  := SIN(Q);
  F  := 1 - Power(((Xo * C + Yo * S) / ar), 2) - Power(((Xo * S - Yo * C) / br), 2);
//  F1  := Power(((Xo * C + Yo * S) / ar), 2) + Power(((Xo * S - Yo * C) / br), 2) - 1;
  A1 := Power((C / ar), 2) + Power((S / br), 2);
  B1 := (1 / Power(ar, 2) - 1 / Power(br, 2)) * C * S * 2;
  C1 := Power(S / ar, 2) + Power(C / br, 2);
  D1 := - (Xo * C + Yo * S) * C * 2 / Power(ar, 2) - (Xo * S - Yo * C) * S * 2 / Power(br, 2);
  E1 := - (Xo * C + Yo * S) * S * 2 / Power(ar, 2) + (Xo * S - Yo * C) * C * 2 / Power(br, 2);
  // F1 を使用した場合以下不要です
  A1 := A1 / F;
  B1 := B1 / F;
  C1 := C1 / F;
  D1 := D1 / F;
  E1 := E1 / F;
  F1 := -1;
end;

//-------------------使用不可-----------------------------------
// 楕円近似計算最小二乗法+ガウスニュートン法で使用していた計算
// Ax^2 + Bxy + Cy^2 + Dx + Ey + F から
//    半径 a, b 角度θ 座標 Xo, Yo に変換します
// F = -1 でないと正しく変換できません
// 楕円の角度が45度の時は半径a, b を正しく計算できません
// a,bの値が同じになってしまいます。
//--------------------------------------------------------------
procedure TForm1.AtoP;            // 係数変換 A to P
var
  C, S, K: double;
begin
  Q := - 1 / 2 * ARCTAN2(B1, (C1 - A1));
  C := COS(Q);
  S := SIN(Q);
// 下記に変更すれば F1 = -1 でなくてもOKです
//  K := 1 / (-F1 + (C * C - S * S) / 4
  K := 1 / (1 + (C * C - S * S) / 4
        * (Power((D1 * C + E1 * S), 2) / (A1 * C * C - C1 * S * S)
                                     - Power((D1 * S - E1 * C), 2) / (A1 * S * S - C1 * C * C)));
  // 次の計算ar,brは45°の時あきらかに同じ値になります
  ar := Sqrt(Abs( (C * C - S * S) / (A1 * C * C - C1 * S * S) / K));
  br := Sqrt(Abs(-(C * C - S * S) / (A1 * S * S - C1 * C * C) / K));

  Xo :=-(ar * ar * C * (D1 * C + E1 * S) + br * br * S * (D1 * S - E1 * C)) / 2 * K;
  Yo :=-(ar * ar * S * (D1 * C + E1 * S) - br * br * C * (D1 * S - E1 * C)) / 2 * K;
  imageClear;
  image1.Canvas.TextOut(10, 0,' 楕円近似 AtoP');
  image1.Canvas.TextOut(20,  20, '半径   a= ' + floatTostrF(Ar, ffGeneral, 8, 3));
  image1.Canvas.TextOut(20,  40, '半径   b= ' + floatTostrF(Br, ffGeneral, 8, 3));
  image1.Canvas.TextOut(20,  60, '中心座標 X= ' + floatTostrF(Xo, ffGeneral, 8, 3));
  image1.Canvas.TextOut(20,  80, '中心座標 Y= ' + floatTostrF(Yo, ffGeneral, 8, 3));
  image1.Canvas.TextOut(20, 100, '角度   θ= ' + floatTostrF(q / pi * 180, ffGeneral, 10, 3)+'°');
end;

//-----------------------
// 初期設定
//-----------------------
procedure TForm1.FormCreate(Sender: TObject);
begin
  imageClear;
  image1.Canvas.Font.Name := 'MS ゴシック';
//  image1.Canvas.Font.Style := [fsbold];
  image1.Canvas.Font.Height := 14;
end;

end.

ガウスジョルダン法のプログラム例

unit Main;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, VclTee.TeeGDIPlus, Vcl.StdCtrls, Vcl.ExtCtrls,
  VCLTee.TeEngine, VCLTee.Series, VCLTee.TeeProcs, VCLTee.Chart, Vcl.Grids, math;

type
  TForm1 = class(TForm)
    StringGrid1: TStringGrid;
    Chart1: TChart;
    Series1: TPointSeries;
    Button1: TButton;
    GroupBox2: TGroupBox;
    LabeledEdit1: TLabeledEdit;
    Button3: TButton;
    Button4: TButton;
    Image1: TImage;
    Button2: TButton;
    CheckBox1: TCheckBox;
    CheckBox2: TCheckBox;
    procedure Button1Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Chart1AfterDraw(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private 宣言 }
    procedure StrinGridset;           // データー表示
    procedure calcDsp;                // 計算表示
    procedure imageClear;
    procedure sigmaCalc;                // Σの計算
    procedure DotGraph;                 // データーグラフ表示
    procedure XYMAXScaleSet;            // スケールセット  縦横比を同じにするため
    procedure OvalGraph;                // 楕円描画
    procedure testdataset;              // テストデーターの読み込み
    procedure test1dataset;             // テストデーターの読み込み
    procedure coordinate_converter;     // 座標変換
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

type
  TGaussA = array of array of double;
  TGaussB = array of double;

const
  unknowns = 5;
var
  // 入力データーの数
  nd      : integer;
  // 入力データー
  data:  array of array of double;
  // 座標変換用
  Cdata: array of array of double;
  Ga: TGaussA; Gb: TGaussB;
  A, B, C, D, E, F: double;
  ar, br, Xo, Yo, Q : double;

  maxn              : integer;
  // テスト用楕円データー

  Testdata: array[0..4,0..1] of string =  (('-125.127','134.797'),
                                           ('-129.240',' 66.143'),
                                           (' -75.660',' 35.356'),
                                           (' -50.398','110.839'),
                                           (' -95.329','141.0157'));

  Testdata1: array[0..4,0..1] of string =  ((' 50.00000',' 50.00000'),
                                            ('-40.00000',' 40.00000'),
                                            ('-50.00000','-50.00000'),
                                            (' 40.00000','-40.00000'),
                                            (' 62.4695047554424262',' 0.00000'));

procedure TForm1.imageClear;
begin
  image1.Canvas.Brush.Style := bsSolid;
  image1.Canvas.Brush.Color := clWhite;
  image1.Canvas.FillRect(Rect(0, 0, image1.Width, image1.Height));
end;

procedure TForm1.OvalGraph;             // 楕円描画
var
  JJ: integer;
//  KK: integer;
  Xpos,Ypos: integer;
//  AoXo,BoYo,MAoXo,MBoYo: integer;
  W: double;                          // 角度ω
  leftD,rightD,topD,BottomD: integer;
begin
  Chart1.Canvas.Pen.Color := clBlack;
  Chart1.Canvas.Pen.Width := 1;
  Chart1.Canvas.Pen.Style := PsSolid;
  Chart1.Canvas.Brush.Style := Bsclear;
// 角度を1度毎に分解し計算楕円作図 推定値 楕円半径 A,B  中心位置 X0, Y0 傾き角 θ を使用します
  W := 0;
  Xpos := Series1.CalcXPosValue(Ar * cos(W) * cos(Q) - Br * sin(W) * sin(Q) + Xo);    // X座標 Ao * cos(W) * cos(Q) - Bo * sin(W) * sin(Q) + Xo
  Ypos := Series1.CalcYPosValue(Ar * cos(W) * sin(Q) + Br * sin(W) * cos(Q) + Yo);    // Y座標 Ao * cos(W) * sin(Q) + Bo * sin(W) * cos(Q) + Yo
  Chart1.Canvas.MoveTo(Xpos,Ypos);                                                    // Series1.CalcXPosValue(X座標)   Chart 上のX位置を求める
  for JJ := 0 to 360 do                                                               // Series1.CalcYPosValue(Y座標)   Chart 上のY位置を求める
    begin
      W := 2 * pi / 360 * JJ;
      Xpos := Series1.CalcXPosValue(Ar * cos(W) * cos(Q) - Br * sin(W) * sin(Q) + Xo);
      Ypos := Series1.CalcYPosValue(Ar * cos(W) * sin(Q) + Br * sin(W) * cos(Q) + Yo);
      Chart1.Canvas.LineTo(Xpos,Ypos);
    end;
// 中心線描画
  leftD   := Chart1.ChartRect.Left;
  rightD  := Chart1.ChartRect.Right;
  topD    := Chart1.ChartRect.Top;
  BottomD := Chart1.ChartRect.Bottom;
  Xpos    := Series1.CalcXPosValue(Xo);
  Ypos    := Series1.CalcYPosValue(Yo);
  Chart1.Canvas.Pen.Color := clBlue;
  Chart1.Canvas.Pen.Width := 1;
  Chart1.Canvas.Pen.Style := PsDashdot;
  Chart1.Canvas.MoveTo(Xpos, topD);
  Chart1.Canvas.LineTo(Xpos, BottomD);
  Chart1.Canvas.MoveTo(leftD, Ypos);
  Chart1.Canvas.LineTo(rightD, Ypos);
// 傾き線の描画
  Chart1.Canvas.Pen.Color := clRed;
  Xpos := Series1.CalcXPosValue(Ar * 1.2 * cos(Q) + Xo);
  Ypos := Series1.CalcYPosValue(Ar * 1.2 * sin(Q) + Yo);
  Chart1.Canvas.MoveTo(Xpos,Ypos);
  Xpos := Series1.CalcXPosValue(-Ar * 1.2 * cos(Q) + Xo);
  Ypos := Series1.CalcYPosValue(-Ar * 1.2 * sin(Q) + Yo);
  Chart1.Canvas.LineTo(Xpos,Ypos);
end;


procedure TForm1.testdataset;          // テストデーターの読み込み
var
  JJ,II: integer;
begin
  setlength(data , nd, 2);            // データー数分の配列確保
  for JJ := 0 to nd - 1 do
    begin
      for II := 0 to 1 do
        data[JJ, II] := strTofloat(Testdata[JJ, II]);   // テストデーター
    end;
  StrinGridset;
  for II := 0 to nd - 1 do
    begin
      stringGrid1.Cells[1, II + 1] := Testdata[II, 0];
      stringGrid1.Cells[2, II + 1] := Testdata[II, 1];
    end;
end;

procedure TForm1.test1dataset;          // テストデーターの読み込み
var
  JJ,II: integer;
begin
  setlength(data , nd, 2);            // データー数分の配列確保
  for JJ := 0 to nd - 1 do
    begin
      for II := 0 to 1 do
        data[JJ, II] := strTofloat(Testdata1[JJ, II]);   // テストデーター
    end;
  StrinGridset;
  for II := 0 to nd - 1 do
    begin
      stringGrid1.Cells[1, II + 1] := Testdata1[II, 0];
      stringGrid1.Cells[2, II + 1] := Testdata1[II, 1];
    end;
end;

procedure TForm1.XYMAXScaleSet;         // スケールセット  縦横比を同じにするため
var                                     // のAxis設定
  XBMin,XBMax,YLMin,YLMax: double;
  BRange, LRange, W      : double;
  Xposd, Yposd: double;
  JJ: integer;
  SCsetF  : Boolean;
begin
  XBMax := data[0, 0];
  XBMin := data[0, 0];
  YLMax := data[0, 1];
  YLMin := data[0, 1];
  for JJ := 1 to nd -1 do
    begin
      if XBMax < data[JJ, 0] then        // Xの最大値検索
          XBMax := data[JJ, 0];          // Xの最大値
      if XBMin > data[JJ, 0] then        // Xの最小値検索
          XBMin := data[JJ, 0];          // Xの最小値

      if YLMax < data[JJ, 1] then        // Yの最大値検索
          YLMax := data[JJ, 1];          // Yの最大値
      if YLMin > data[JJ, 1] then        // Yの最小値検索
          YLMin := data[JJ, 1];          // Yの最小値
    end;
  for JJ := 0 to 360 do                  // Series1.CalcYPosValue(Y座標)   Chart 上のY位置を求める
    begin
      W := 2 * pi / 360 * JJ;
      Xposd := Ar * cos(W) * cos(Q) - Br * sin(W) * sin(Q) + Xo;
      Yposd := Ar * cos(W) * sin(Q) + Br * sin(W) * cos(Q) + Yo;
      if XBMax < Xposd then        // Xの最大値検索
          XBMax := Xposd;          // Xの最大値
      if XBMin > Xposd then        // Xの最小値検索
          XBMin := Xposd;          // Xの最小値

      if YLMax < Yposd then        // Yの最大値検索
          YLMax := Yposd;          // Yの最大値
      if YLMin > Yposd then        // Yの最小値検索
          YLMin := Yposd;          // Yの最小値
    end;
  BRange := XBMax - XBMin;
  LRange := YLMax - YLMin;
  if BRange > LRange then LRange := BRange;
  if LRange = 0 then LRange := 1;  // グラフのレンジが0になるのを防止する

  XBMin := Xo - LRange * 0.6;
  XBMax := Xo + LRange * 0.6;
  YLMin := Yo - LRange * 0.6;
  YLMax := Yo + LRange * 0.6;

  Chart1.LeftAxis.Automatic := False;
  Chart1.BottomAxis.Automatic := False;
  SCsetF := False;
  if Chart1.LeftAxis.Maximum < YLMin then
    begin
      Chart1.LeftAxis.Maximum := YLMax;
      Chart1.LeftAxis.Minimum := YLMin;
      SCsetF := True;
    end;
  if Chart1.LeftAxis.Minimum > YLMax then
    begin
      Chart1.LeftAxis.Minimum := YLMIN;
      Chart1.LeftAxis.Maximum := YLMAX;
      SCsetF := True;
    end;
  if not SCsetF then
    begin
      Chart1.LeftAxis.Maximum := YLMax;
      Chart1.LeftAxis.Minimum := YLMin;
    end;
  SCsetF := False;
  if Chart1.BottomAxis.Maximum < XBMin then
    begin
      Chart1.BottomAxis.Maximum := XBMax;
      Chart1.BottomAxis.Minimum := XBMin;
      SCsetF := True;
    end;
  if Chart1.BottomAxis.Minimum > XBMax then
    begin
      Chart1.BottomAxis.Minimum := XBMin;
      Chart1.BottomAxis.Maximum := XBMax;
      SCsetF := True;
    end;
  if not SCsetF then
    begin
      Chart1.BottomAxis.Maximum := XBMax;
      Chart1.BottomAxis.Minimum := XBMin;
    end;
end;

procedure TForm1.DotGraph;              // データーグラフ表示
var
  JJ : integer;
begin
  Series1.Clear;                        // グラフクリア
  XYMAXScaleSet;                        // スケールセット
  for JJ := 0 to nd -1 do
    Series1.AddXY(data[JJ, 0], data[JJ, 1], '', clRed);
//    Series1.AddXY(data[JJ, 0], data[JJ, 1], intTostr(JJ), clRed);
//  Series1.Marks.Visible:=True;
end;

procedure TForm1.StrinGridset;          // データー表示
var
  I: integer;
begin
  stringGrid1.DefaultRowHeight := 20;
  if nd < 1 then stringGrid1.RowCount := 2            // 固定行が1行あるので少なくても2行にする
            else stringGrid1.RowCount := nd + 1;      // 固定行を1行追加する
  stringGrid1.ColCount := 3;                          // No, X, Y で3列
  stringGrid1.ClientHeight := (stringGrid1.DefaultRowHeight + 1) * 16;
  stringGrid1.ClientWidth := (stringGrid1.DefaultColWidth + 1) * 3;
  stringGrid1.Cells[1, 0] := 'X';
  stringGrid1.Cells[2, 0] := 'Y';
  if nd = 0 then exit;
  for I := 0 to nd - 1 do
    begin
      stringGrid1.Cells[0, I + 1] := intTostr(I + 1);
//      stringGrid1.Cells[1, I + 1] := '';
//      stringGrid1.Cells[2, I + 1] := '';
    end;
end;

//-----------------------------------------------------------------------------------------
// 連立方程式の解法 ガウス-ジョルダン法
// n:n次方程式 
// a:係数を表すn次元配列      入力データー
// b:非同次項を表す1次配列   解
//
// http://www.yamamo10.jp/yamamoto/lecture/2006/5E/Linear_eauations/gaussj_html/node2.html
//-----------------------------------------------------------------------------------------
function gauss_jordan(n: integer; var a: TGaussA; var b: TGaussB): boolean;
var
  ipv, i, j : integer;
  inv_pivot, temp : real;
  big : real;
  pivot_row : integer;
//  row : array of integer;
begin
  result := True;
//  setlength(row, unknowns);
  pivot_row := -1;
  for ipv := 0 to n - 1 do begin
    //最大値検索
    big := 0.0;
    for i := ipv to n - 1 do begin
      if abs(a[i, ipv]) > big then begin
        big := abs(a[i, ipv]);
        pivot_row := i;
      end;
    end;
    if big = 0 then begin
      Result := False;
      Exit;
    end;
//    row[ipv] := pivot_row;

    // 行の入れ替え
    if (ipv <> pivot_row) then begin
      for i := 0 to n - 1 do begin
        temp := a[ipv, i];
        a[ipv, i] := a[pivot_row, i];
        a[pivot_row, i] := temp;
      end;
      temp := b[ipv];
      b[ipv] := b[pivot_row];
      b[pivot_row] := temp;
    end;
    // 対角成分=1 ピボット行の処理
    inv_pivot := 1.0 / a[ipv, ipv];
    a[ipv,ipv] := 1.0;
    for j := 0 to n - 1 do begin
      a[ipv, j] := a[ipv, j] * inv_pivot;
    end;
    b[ipv] := b[ipv] * inv_pivot;

    // ピボット列=0 ピボット行以外ノ処理
    for i := 0 to n - 1 do begin
      if (i <> ipv) then begin
        temp := a[i, ipv];
        a[i, ipv] := 0.0;
        for j := 0 to n - 1 do begin
          a[i, j] := a[i,j] - temp * a[ipv, j];
        end;
        b[i] := b[i] - temp * b[ipv];
      end;
    end;
  end;
  // 列の入れ替え 逆行列
  // 逆行列は使用されません
{  for j := n - 1 downto 0 do begin
    if (j <> row[j]) then begin
      for i := 0 to n  - 1do begin
        temp := a[i, j];
        a[i, j] := a[i, row[j]];
        a[i, row[j]] := temp;
      end;
    end;
  end; }
end;

// 楕円の傾き角で座標変換をします
// 座標変換前の中心座標を計算しておきます
// 座標変換後ガウスジョルダン法で連立方程式を解きます
procedure TForm1.coordinate_converter;            // 座標変換
var
  lng, QQ     : double;
  i, j        : integer;
  Xi, Yi    : double;
begin
  setlength(Cdata , nd, 2);            // データー数分の配列確保
  // 座標変換
  for j := 0 to nd - 1 do begin
    lng := sqrt(data[j, 1] * data[j, 1] + data[j, 0] * data[j, 0]);
    QQ := arctan2(data[j, 1], data[j, 0]);
    QQ := QQ - Q;
    Cdata[j, 0] := lng * cos(QQ);
    Cdata[j, 1] := lng * sin(QQ);
  end;
  // ガウスデータークリア
  for i := 0 to maxn - 1 do begin
    Gb[i] := 0;
    for j := 0 to maxn -1 do
      Ga[i, j] := 0;
  end;
  for i := 0 to nd - 1 do begin   // x^2 xy y^2 x y の値セット 5 x 5
    Ga[i, 0] := Cdata[i, 0] * Cdata[i, 0];
    Ga[i, 1] := Cdata[i, 0] * Cdata[i, 1];
    Ga[i, 2] := Cdata[i, 1] * Cdata[i, 1];
    Ga[i, 3] := Cdata[i, 0];
    Ga[i, 4] := Cdata[i, 1];
    Gb[i] := 1;
  end;
  // 連立方程式をガウス-ジョルダン法で解く
  // 此処で計算された、半径が採用されます
  if gauss_jordan(unknowns, Ga, Gb) then begin
    A := Gb[1] / Gb[0];
    B := Gb[2] / Gb[0];
    C := Gb[3] / Gb[0];
    D := Gb[4] / Gb[0];
    E := -1 / Gb[0];

    // 中心の座標
    Xi :=  (A * D - 2 * B * C) / (4 * B - A * A);
    Yi :=  (A * C - 2 * D) / (4 * B - A * A);
    // 傾き
    QQ := ArcTan2(A ,(1 - B)) / 2.0;

    // X軸方向の長さA
    ar := sqrt(abs(sqr(Xi * cos(QQ) + Yi * sin(QQ)) - E * sqr(cos(QQ)) -
      (sqr(Xi * sin(QQ) - Yi * cos(QQ)) - E * sqr(sin(QQ))) *
      (sqr(sin(QQ)) - B * sqr(cos(QQ))) / (sqr(cos(QQ))- B * sqr(sin(QQ)))));

    // Y軸方向の長さB
    br := sqrt(abs(sqr(Xi * sin(QQ) - Yi * cos(QQ)) - E * sqr(sin(QQ)) -
      (sqr(Xi * cos(QQ) + Yi * sin(QQ)) - E * sqr(cos(QQ))) *
      (sqr(cos(QQ)) - B * sqr(sin(QQ))) / (sqr(sin(QQ)) - B * sqr(cos(QQ)))));
  end
  else
    application.MessageBox('方程式の解法が出来ませんでした。','注意',0)
end;

// 方程式の解法と表示
// ガウスジョルダン法で方程式を解きます
// 座標変換の指定がある場合は、座標変換ガウスジョルダンのルーチンを呼び出します
procedure TForm1.sigmaCalc;
var
  i, j : integer;
  sinQ, cosQ: double;
  xy, h, v, at, bt, kd: double;
begin
  maxn := unknowns;
  setlength(Ga, maxn, maxn);
  setlength(Gb, maxn);
  for i := 0 to maxn - 1 do begin
    Gb[i] := 0;
    for j := 0 to maxn -1 do
      Ga[i, j] := 0;
  end;
  for i := 0 to nd - 1 do begin   // x^2 xy y^2 x y の値セット 5 x 5
    Ga[i, 0] := data[i, 0] * data[i, 0];
    Ga[i, 1] := data[i, 0] * data[i, 1];
    Ga[i, 2] := data[i, 1] * data[i, 1];
    Ga[i, 3] := data[i, 0];
    Ga[i, 4] := data[i, 1];
    Gb[i] := 1;                   // Eの値を-1にします
  end;
  // 連立方程式をガウス-ジョルダン法で解く
  // 座標変換をする場合は、中心座標と、軸角が採用されます
  // 軸角が45°であると、半径が正しく計算されないので座標変換をして
  // 半径を計算します。
  // 角度は、短辺側の角度が計算されます。
  if gauss_jordan(unknowns, Ga, Gb) then begin
    if checkbox2.Checked = true then begin
      A := Gb[0];
      B := Gb[1];
      C := Gb[2];
      D := Gb[3];
      E := Gb[4];
      F := -1;
      // 楕円の各係数計算
      xy := (B * B - 4 * A * C);
      h  := (2 * D * C - E * B) / xy;   // 原点からのX方向移動量
      v  := (2 * A * E - D * B) / xy;   // 原点からのy方向移動量
      Q :=  - arctan2(B, C - A) / 2;    // 標準楕円からの回転角
      if Q < 0 then Q := Q + pi;
      cosQ := cos(Q);
      sinQ := sin(Q);
      at := A * cosQ * cosQ + B * cosQ * sinQ + C * sinQ * sinQ;
      bt := A * sinQ * sinQ - B * cosQ * sinQ + C * cosQ * cosQ;
      kd := A * h * h + B * h * v + C * v * v + D * h + E * v + F;
      ar := sqrt(-Kd / at);             // 半径a
      br := sqrt(-kd / bt);             // 半径b
      xo := h;
      yo := v;
    end
    else begin
      A := Gb[1] / Gb[0];
      B := Gb[2] / Gb[0];
      C := Gb[3] / Gb[0];
      D := Gb[4] / Gb[0];
      E := -1 / Gb[0];

      // 中心の座標
      Xo :=  (A * D - 2 * B * C) / (4 * B - A * A);
      Yo :=  (A * C - 2 * D) / (4 * B - A * A);
      // 傾き
      Q := ArcTan2(A ,(1 - B)) / 2.0;
      if checkbox1.Checked = true then coordinate_converter            // 座標変換
      else begin
        sinQ := sin(Q);
        cosQ := cos(Q);
        // X軸方向の長さA
        ar := sqrt(abs(sqr(Xo * cosQ + Yo * sinQ) - E * sqr(cosQ) -
          (sqr(Xo * sinQ - Yo * cosQ) - E * sqr(sinQ)) *
          (sqr(sinQ) - B * sqr(cosQ)) / (sqr(cosQ)- B * sqr(sinQ))));

        // Y軸方向の長さB
        br := sqrt(abs(sqr(Xo * sinQ - Yo * cos(Q)) - E * sqr(sinQ) -
          (sqr(Xo * cosQ + Yo * sinQ) - E * sqr(cosQ)) *
          (sqr(cosQ) - B * sqr(sinQ)) / (sqr(sinQ) - B * sqr(cosQ))));

      end;
    end;
    imageClear;
    image1.Canvas.TextOut(20,  20, '半径   a= ' + floatTostrF(Ar, ffGeneral, 8, 3));
    image1.Canvas.TextOut(20,  40, '半径   b= ' + floatTostrF(Br, ffGeneral, 8, 3));
    image1.Canvas.TextOut(20,  60, '中心座標 X= ' + floatTostrF(Xo, ffGeneral, 8, 3));
    image1.Canvas.TextOut(20,  80, '中心座標 Y= ' + floatTostrF(Yo, ffGeneral, 8, 3));
    image1.Canvas.TextOut(20, 100, '角度   θ= ' + floatTostr(Q / pi * 180) + '°');
  end
  else
    application.MessageBox('方程式の解法が出来ませんでした。','注意',0)
end;

procedure TForm1.calcDsp;   // 計算表示
begin
  sigmaCalc;                // Σの計算
  DotGraph;                 // データーグラフ表示
end;

procedure TForm1.Chart1AfterDraw(Sender: TObject);
begin
  if nd = 0 then exit;      // nd が 0 だったら 楕円描画しない
  OvalGraph;                // 楕円描画
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  nd := unknowns;                  // テストデーター数
  LabeledEdit1.Text := intTostr(nd);
  testdataset;              // テストデーターの読み込み
  calcDsp;                  // 計算表示
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  nd := unknowns;                  // テストデーター数
  LabeledEdit1.Text := intTostr(nd);
  test1dataset;             // テストデーターの読み込み
  calcDsp;                  // 計算表示
end;

procedure TForm1.Button3Click(Sender: TObject);
var
  check : integer;
begin
  val(LabeledEdit1.Text, nd, check);
  if check <> 0 then exit;
  if nd <> 5 then exit;
  setlength(data , nd, 2);              // 配列確保
{  for JJ := 0 to nd -1 do               // データークリア
    begin
      for II := 0 to 1 do
          data[JJ, II] := 0;
    end;}
  StrinGridset;          // データー表示
end;

procedure TForm1.Button4Click(Sender: TObject);
var
  Check,JJ: integer;
  Dataind: double;
begin
//  if nd <> unknowns then exit;
  for JJ := 0 to nd - 1 do
    begin
      val(stringGrid1.Cells[1,JJ + 1], Dataind, Check);
      if Check = 0 then data[JJ, 0] := Dataind
                   else data[JJ, 0] := 0;
      val(stringGrid1.Cells[2,JJ + 1], Dataind, Check);
      if Check = 0 then data[JJ, 1] := Dataind
                   else data[JJ, 1] := 0;
    end;
  calcDsp;                  // 計算表示
end;

end.

download Ellipse_gauss_jordan.zip

各種プログラム計算例に戻る