楕円同士の接線

 楕円同士の接線を求めるのには、二つの楕円と、楕円の接線の連立方程式が解ければ良いのですが、半径比が異なり、角度を持っている楕円同士の場合難しいので、楕円外の点と楕円の接線の計算を、二つの楕円で交互に行う事により実現します。

 最初に、楕円1と楕円2に、仮の接点の位置、Xs,Ys Xe,Yeをそれぞれ与えます。
次に、楕円1に対する、座標Xe,Yeからの接線の接点Xs,Ysの座標を計算しなおします。
更に、今度は、楕円2に対する、座標Xs,Ysからの接線の接点Xe,Yeを計算します。
この計算を繰り返し、接点の位置が変化しなくなるまで行うことにより、接点と接線を求めます。
数回の繰り返しで、計算は収束します。
 楕円は、傾きを持っているので、それぞれの楕円の計算時、原点への移動と、傾きの無いように座標変換を行い計算を行います。



 実際の計算

 左図、角度θの楕円上座標Xθ、Yθを求めるのは、接線の計算とは直接は関係ありません。
マウスで、楕円上の仮の接点の位置を与えるのに利用しています。
 接点の計算に使用しているのは、左図下側の楕円外の点Xp,Ypを通る接線です。
 楕円外の指定点を通る接線を求める時、楕円のまゝで計算するのは困難なので、円に変換をしてから、接点を求め、楕円の座標に戻して計算します。
 ここでは、直線の方程式は求めていません。
2点が与えられるので、簡単に直線の方程式は求めることができます。



 左図は、接点を求めるプログラムの実行例です。
新たに、専用のプログラムを作成するのは面倒なので、楕円同士の交点を計算するプログラムに接線の接点を求める計算を追加しました。
 交点の位置と、接線の計算は無関係です。
 最初に、楕円の作図を行い、次に、楕円1と楕円2の仮の接点の位置を指定します。
仮の位置の指定は、マウスで楕円の線上の近辺をクリックすることにより行います。
指定が終わったら、計算ボタンをクリックすることにより、接点と接線が表示されます。
指定された位置によっては、計算が出来ないことがあります、その場合は改めて指定をし直します。
 接点の計算時、接点は2個計算されますが、指定された位置と近い方の接点の位置を採用しています。



プログラム

unit Tangent_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, Vcl.Buttons, math;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Ellipse1Box: TGroupBox;
    radius_a1_Edit: TLabeledEdit;
    radius_b1_Edit: TLabeledEdit;
    Center_x1_Edit: TLabeledEdit;
    Center_y1_Edit: TLabeledEdit;
    angle_q1_Edit: TLabeledEdit;
    Ellipse2Box: TGroupBox;
    radius_a2_Edit: TLabeledEdit;
    radius_b2_Edit: TLabeledEdit;
    Center_x2_Edit: TLabeledEdit;
    Center_y2_Edit: TLabeledEdit;
    angle_q2_Edit: TLabeledEdit;
    Panel2: TPanel;
    SelectBtn: TBitBtn;
    Image1: TImage;
    Image2: TImage;
    GroupBox1: TGroupBox;
    CalcBtn: TButton;
    StartPosBtn: TBitBtn;
    EndPosBtn: TBitBtn;
    procedure FormCreate(Sender: TObject);
    procedure SelectBtnClick(Sender: TObject);
    procedure Image1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X,
      Y: Integer);
    procedure StartPosBtnClick(Sender: TObject);
    procedure EndPosBtnClick(Sender: TObject);
    procedure CalcBtnClick(Sender: TObject);
  private
    { Private 宣言 }
    procedure imageClear;
    function  Dist(x1, y1, x2, y2 : double): double;
    function  Angle(dx, dy: double): double;
    procedure LineSub(Tcv: TCanvas; px1, py1, px2, py2: double);
    procedure pitchControl(NLine, LW: integer);
    procedure LineSubPitch(Tcv : TCanvas; Ln: integer; var i: integer; var d1: double; px1, py1, px2, py2:double);
    function  angleSe(a, b, rad: double): double;
    procedure EllipseEx(Tcv: TCanvas; h, lf, lw: integer; lc: Tcolor; a, b, x, y, rq, sq, eq: double);
    procedure quadratic_transform(a, b, x, y, qdeg: double; var A0, B0, C0, D0, E0, F0: double);
    procedure inputdata_drawing;
    function cross_calc: boolean;
    function cross_quadratic(a, b, c, d, e: double; var x1, x2, x3, x4: double): byte;
    procedure trance(Xn, Yn, defQ, xo1, yo1: double; var x, y: double);
    procedure StraightLineDraw(Tcv: TCanvas; h, lf, lw: integer; lc: Tcolor; xs, ys, xe, ye: double; sf: boolean);
    procedure TaninputDraw;
    procedure Position_Calc;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

const
  PitchN = 5;                                 // 線ピッチ設定数
  LineN  = 3;                                 // 線種数
  LIMIT8 = 0.00000001;

  MINIMAM = 1E-13;

type
  TLinePitch = Record                         // 線種用レコード
    Pitch   : array[0..PitchN] of double;     // ピッチ 線長さ 空白の繰り返し
    segment : integer;                        // ピッチ数
  End;

var
  LPitch    : array of TLinePitch;            // 線種用 (線ピッチ)
  VPitch    : array[0..PitchN] of double;     // 線幅による線ピッチ設定用

  magnification : double;
  ar1, ar2      : double;
  br1, br2      : double;
  xo1, xo2      : double;
  yo1, yo2      : double;
  q1, q2        : double;
  scale         : double;

  shift_x       : double;
  shift_y       : double;
  d01           : double;
  d0p           : integer;
  posXs         : double;                     // 接線始点指定位置X
  posYs         : double;                     // 接線始点指定位置y
  posXe         : double;                     // 接線終点指定位置X
  posYe         : double;                     // 接線終点指定位置y
  TanmodF       : byte;     // bit0 始点指定  bit1 終点位置  bit2 計算
  TandrwF       : byte;     // bit0 始点指定  bit1 終点位置  bit2 交点計算済み 描画

const
  draw_margin = 30;                           // 左右上下作図マージン

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

//-----------------------------------------------
// 線幅によるピッチの補正
// 線幅を広くするとスペースが狭くなるので広げます
// NLine 線種
// LW    線幅
//-----------------------------------------------
procedure TForm1.pitchControl(NLine, LW: integer);
var
  i : integer;
begin
  // 線幅によるピッチの補正
  for i := 0 to pitchN do begin
    if i mod 2 <> 0 then                              // 奇数配列noセグメントがスペース
      Vpitch[i] := LPitch[NLine].Pitch[i] + LW        // スペースに線幅加算
    else
      Vpitch[i] := LPitch[NLine].Pitch[i];
  end;
end;

//----------------------------
// 作図用 二点間線引き
//----------------------------
procedure TForm1.LineSub(Tcv: TCanvas; px1, py1, px2, py2: double);
var
  x1, y1, x2, y2 : integer ;
begin
  x1 := Round(px1);
  y1 := Round(py1);
  x2 := Round(px2);
  y2 := Round(py2);
  Tcv.MoveTo(x1, y1);
  Tcv.LineTo(x2, y2);
end;

//----------------------
// 距離(長さ)を計算
//  x1, y1 : 始点
//  x2, y2 : 終点
//  out : 距離(長さ)
//----------------------
function TForm1.Dist(x1, y1, x2, y2 : double): double;
begin
  Result := Sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)) ;
end;

//--------------------------------
// 角度を計算[rad]
//  dx : X方向差分
//  dy : Y方向差分
//  out: 角度 (0~2 * Pi)
//--------------------------------
function TForm1.Angle(dx, dy: double): double ;
var
  r : double ;
begin
  Result := 0.0;
  if (Abs(dx) < LIMIT8) and (Abs(dy) < LIMIT8) then exit;
  r := ArcTan2(dy, dx);
  if r < 0.0 then r := r + 2.0 * Pi;
  Result := r;
end;

//--------------------------------------
// 線分の表示・サブ2:線種指定
//    Ln : 線種番号(1-)
//    i  : 開始セグメント番号(終了時更新)
//    d1 : 開始ピッチ長さ  (終了時更新)
//    px1, py1 : 線分始点[mm]
//    px2, py2 : 線分終点[mm]
//--------------------------------------
procedure TForm1.LineSubPitch(Tcv: TCanvas; Ln: integer; var i: integer; var d1: double; px1, py1, px2, py2:double);
var
  x1, y1, x2, y2  : double;
  a, sa, ca, d, p : double;
  PenStyle        : TPenstyle;
begin
  PenStyle := Tcv.Pen.Style;                    // 線種バックアップ
  Tcv.Pen.Style := psSolid;
  if (Ln < 0) or (Ln >= LineN) then begin       // 無い線種は実線
    LineSub(Tcv, px1, py1, px2, py2);
    Tcv.Pen.Style := PenStyle;                  // 線種戻し
    exit;
  end;
  a := Angle(px2 - px1, py2 - py1);             // 角度計算
  d := Dist(px1, py1, px2, py2);                // 距離計算
  ca:= Cos(a);                                  // コサイン
  sa:= Sin(a);                                  // サイン
  x1:= px1;                                     // 始点x
  y1:= py1 ;                                    // 始点y
  repeat
    p := Vpitch[i] - d1;                        // ピッチと開始ピッチ長さ差分 残り分
    if (p > d) then p := d;                     // 距離より残り分が大きい場合 距離
    x2 := x1 + p * ca ;                         // 新しい位置計算
    y2 := y1 + p * sa ;
    if (i mod 2) = 0 then                       // セグメント偶数毎に線引き 空白部線引きしない
         LineSub(Tcv, x1, y1, x2, y2);
    x1 := x2;                                   // 終点を始点にセット
    y1 := y2;
    d := d - p ;                                // 残り長さ計算
    if d > LIMIT8 then begin                    // 残り長さがあったら
      d1 := 0.0;                                // 開始ピッチ長さクリア
      Inc(i);                                   // セグメントカウンターインクリメント
      if i >= LPitch[Ln].segment then i := 0;   // セグメント数を超えたらゼロに戻し
    end;
  until d <= LIMIT8;
  d1 := d1 + p;                                 // 開始ピッチ長さ
  Tcv.Pen.Style := PenStyle;                    // 線種戻し
end;

//------------------------------------------
// 楕円の開始角を 基礎円の開始角に変換
//------------------------------------------
function TForm1.angleSe(a, b, rad: double): double;
const
  KMIN = 0.00000001;
var
  l : double;
begin
  // 90°, 270°, 0°, 180° 360°近傍は計算しない
  if (abs(rad - pi / 2) < KMIN) or (abs(rad - pi - pi / 2) < KMIN)
      or (abs(rad) < KMIN) or (abs(rad - pi) < KMIN) or
      (abs(rad - pi * 2) < KMIN)
  then begin
    result := rad;
    exit;
  end;
  // 底辺の長さ計算 ( b / a の値がいつも正の値なので元の角度によって角度を補正)
  // 分母がゼロに近くなる時もあるので、判別が不要なarctan2を使用します
  l := b / a / tan(rad);
  // 180°以下だったら
  if rad < pi then
    result := arctan2(1, l)
  else
    // 360°以下だったら
    if rad < pi * 2 then
      result := arctan2(1, l) + pi
    // 360°を超えていたら
    else
      result := arctan2(1, l) + pi * 2;
end;

//-------------------------------------------------------------
// 直線の描画
// 直線の開始はsfをTrueにします。
// つながった直線、折れ線を描画する場合は、sfをFalseに
//-------------------------------------------------------------
procedure TForm1.StraightLineDraw(Tcv: TCanvas; h, lf, lw: integer; lc: Tcolor; xs, ys, xe, ye: double; sf: boolean);
begin
  Tcv.Pen.Width := lw;
  Tcv.Pen.Color := lc;
  // 線幅によるピッチの補正
  pitchControl(lf, lw);
  // y方向変換
  ys := h - ys;
  ye := h - ye;
  if sf then begin
    d01 := 0;
    d0p := 0;
  end;
  LineSubPitch(                               // ピッチ線描画
                tcv,                          // TCanvas
                 lf,                          // 線種
                d0p,                          // セグメント位置
                d01,                          // 長さ
                 xs,                          // 始点X
                 ys,                          // 始点Y
                 xe,                          // 終点X
                 ye                           // 終点Y
                   );
end;


//---------------------------------------
// 楕円の描画
// Windows為ののy方向変換は,このルーチンで行います。
// tcv   描画キャンバスの指定
// h     キャンバスの高さ
// lf    線の種類
// lw    線の幅
// lc    線の色
// a     半径a
// b     半径b
// x     中心位置 x
// y     中心位置 y
// rq    楕円の角度
// sq    描画開始角
// eq    描画終了
//---------------------------------------
procedure TForm1.EllipseEx(Tcv : TCanvas; h, lf, lw: integer; lc: Tcolor; a, b, x, y, rq, sq, eq: double);
var
  sqrad, eqrad    : double;
  qrad            : double;
  Ssqrad, Seqrad  : double;
  sp              : integer;
  dq              : double;
  d1              : double;
  dp              : integer;
  xf, yf          : double;
  rdist, nrad     : double;
  px1, py1        : double;
  px2, py2        : double;
  i               : integer;
begin
  Tcv.Pen.Width := lw;
  Tcv.Pen.Color := lc;
  // y方向変換
  y := h - y;
  // 線幅によるピッチの補正
  pitchControl(lf, lw);

  // 終了角と開始角が一致したら全周
  if abs(sq - eq) < LIMIT8 then begin
    sq := 0;
    eq := 360;
  end;

  // 分割数 半径に応じて調整
  sp := round(sqrt(a + b) * 4);               // 分割数

  // 楕円の計算と描画
  sqrad := sq / 180 * pi;
  eqrad := eq / 180 * pi;

  qrad  := pi / 180 * rq;                     // 回転角 ラジアン

  Ssqrad := angleSe(a, b, sqrad);             // 基本円角度に変換
  Seqrad := angleSe(a, b, eqrad);             // 基本円角度に変換
  // 分割微小角計算  分割数角度で補正
  if Seqrad >= Ssqrad then begin
    sp := round(sp * (Seqrad - Ssqrad) / pi);
    if sp < 1 then sp := 1;
    dq := (Seqrad - Ssqrad) / sp              // 分割微小角 ラジアン
  end
  else begin
    sp := round(sp * (Pi * 2 - Ssqrad + Seqrad) / pi);
    if sp < 1 then sp := 1;
    dq := (Pi * 2 - Ssqrad + Seqrad) / sp;    // 分割微小角 ラジアン
  end;
  // 開始点の計算
  d1 := 0;                                    // 長さ
  dp := 0;                                    // セグメント位置
  xf := cos(Ssqrad) *  a;                     // 基本楕円座標X スタート座標
  yf := sin(Ssqrad) *  b;                     // 基本楕円座標y スタート座標
  nrad := arctan2(yf, xf) + qrad;             // 新しい角度 回転角加算
  rdist := sqrt(xf * xf + yf * yf);           // 中心と楕円座標の距離
  px1 := cos(nrad) * rdist + x;               // 開始座標X
  py1 := sin(nrad) * -rdist + y;              // 開始座標Y -rdistはY座標反転の為
                                              // WindowはY座標方向が逆のため
  // 分割数分計算繰り返し描画
  for i := 1 to sp do begin
    xf := cos(Ssqrad + dq * i) * a;
    yf := sin(Ssqrad + dq * i) * b;
    nrad := arctan2(yf , xf) + qrad;
    rdist := sqrt(xf * xf + yf * yf);
    px2 := round(cos(nrad) * rdist + x);      // 終点座標X
    py2 := round(sin(nrad) * -rdist + y);     // 終点座標Y
    // ピッチ線描画
    LineSubPitch(                             // ピッチ線描画
                tcv,                          // TCanvas
                 lf,                          // 線種
                 dp,                          // セグメント位置
                 d1,                          // 長さ
                px1,                          // 始点X
                py1,                          // 始点Y
                px2,                          // 終点X
                py2                           // 終点Y
                   );
    px1 := px2;                               // 終点Xを始点Xに
    py1 := py2;                               // 終点Yを始点Yに
  end;
end;

//-------------------------------------------------
// 楕円の方程式 二次方程式への変換
// AX^2 + BXY + CY^2 + DX + EY + F の A,B,C,D,E,F 
//-------------------------------------------------
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;
end;

//-----------------------------------------------------
// 四次 二次方程式の解法  デカルトの方法
// ax^4 + bx^3 + cx^2 + dx + e = 0
// x の 値は 4個
// 虚数にならない X を フラグにして byteで返します
// x1 bit0 x2 bit1 x3 bit2  x4 bit3
//-----------------------------------------------------
function TForm1.cross_quadratic(a, b, c, d, e: double; var x1, x2, x3, x4: double): byte;
const
  IMAGIMIN = 1E-8;
var
  A3, A2, A1, A0  : double;
  b2, b1, b0      : double;
  f0              : double;
  e1, e0, e1s3, e0s2, e02s4, e13s27 : double;
  e02s4pe13s27                      : double;
  r, rh1s3, cosQ, Q, cosQs3, sinQs3 : double;
  u, v, wiupv, t, c1, c0            : double;
  up, vm                            : string;
  d1, d0, c1h2s4mc0, d1h2s4md0      : double;
  xim1, xim2, xim3, xim4            : string;

  b2h2mb0                           : double;
  ru, rv, ruh1s3, rvh1s3            : double;
  Qu, Qv, Qus3, Qvs3                : double;
  u1, u2, u3, v1, v2, v3            : double;
  u1pv1, u2pv3, u3pv2               : double;

  u1S, u2S, u3S, v1S, v2S, v3S      : double;
  u1pv1S, u2pv3S, u3pv2S            : double;

  rs1, rs2, rs3, rs4                : byte;
  ii    : integer;

begin
  Result := 0;

  u := 0;
  v := 0;
  if (abs(a) > MINIMAM) then begin
// a=0 の場合は四次方程式でないので解法不可
// x^4 + A3x^3 + A2x^2 + A1x + A0 = 0 に変換
    A3 := b / a;
    A2 := c / a;
    A1 := d / a;
    A0 := e / a;
// 四次式の解法
    b2 := -3 / 8 * A3 * A3 + A2;
    b1 := A3 * A3 * A3 / 8 - A3 * A2 / 2 + A1;
    b0 := -3 / 256 * A3 * A3 * A3 * A3 + A3 * A3 * A2 / 16 - A3 * A1 / 4 + A0;

    e1 := -b2 * b2 / 3 - 4 * b0;
    e0 := -2 / 27 * b2 * b2 * b2 + 8 / 3 * b2 * b0 - b1 * b1;
    e1s3 := e1 / 3;
    e0s2 := e0 / 2;
    e02s4 := e0s2 * e0s2;
    e13s27 := e1s3 * e1s3 * e1s3;
    e02s4pe13s27 := e02s4 + e13s27;                 // 判別式
    t := b2 * b2 - 4 * b0;                          // 判別式2
    if (b1 <> 0) or (t < 0) then begin
      if e02s4pe13s27 >= 0 then begin
        u := -e0 / 2 + sqrt(e02s4pe13s27);
        if u < 0 then begin                       // 負数の立方根対策
          u := power(-u, 1/3);
          u := -u;
        end
        else u := power(u, 1/3);
        v := -e0 / 2 - sqrt(e02s4pe13s27);
        if v < 0 then begin                       // 負数の立方根対策
          v := power(-v, 1/3);
          v := -v;
        end
        else v := power(v, 1/3);
        wiupv := u + v;
      end
      else begin
        r := sqrt(e02s4 + ABS(e02s4pe13s27));           // r > 0 e02s4 は必ずプラスかゼロなのでゼロ以下は無し
        rh1s3 := power(r , 1/3);
        if r <> 0 then begin
          cosQ := -e0s2 / r;
          Q := arccos(cosQ);
          cosQs3 := cos(Q / 3);
          sinQs3 := sin(Q / 3);
          wiupv := 2 * rh1s3 * cosQs3;
          u := rh1s3 * cosQs3;
          v := u;
        end
        else
          wiupv := 0;
      end;

      t := wiupv - 2 / 3 * b2;                      // t > 0
      if t < 0 then t := 0;                        // 基本的に t >= 0  t < 0 時は桁落ちの影響
      c1 := sqrt(t);
      if c1 <> 0 then c0 := (c1 * c1 * c1 + b2 * c1 - b1) / 2 / c1
                 else
                   if b2 * b2 - 4 * b0 > 0 then
                     c0 := (b2 + sqrt(b2 * b2 - 4 * b0)) / 2
                   else
                     c0 := b2 / 2;
      d1 := - c1;
      d0 := b0 / c0;
    end
    else begin
      c1 := 0;
      c0 := (b2 + sqrt(t)) / 2;
      d1 := 0;
      d0 := (b2 - sqrt(t)) / 2;
    end;
    if e02s4pe13s27 < 0 then begin
      up := floatTostr(rh1s3 * sinQs3) + ' i';    // 複素数処理 textに変換
      vm := floatTostr(rh1s3 * sinQs3) + ' i';    // 複素数処理 textに変換
    end
    else begin
      up := '…';
      vm := '…';
    end;

    c1h2s4mc0 := c1 * c1 / 4 - c0;
    d1h2s4md0 := d1 * d1 / 4 - d0;
    if abs(c1h2s4mc0) < IMAGIMIN then c1h2s4mc0 := 0;          // 計算誤差修正
    if abs(d1h2s4md0) < IMAGIMIN then d1h2s4md0 := 0;          // 微小な複素数は許容

    if c1h2s4mc0 >=0 then begin
      x1 := -c1 / 2 - A3 / 4 + sqrt(c1h2s4mc0);                 // x1
      xim1 := '';
      Result := Result or 1;
      x2 := -c1 / 2 - A3 / 4 - sqrt(c1h2s4mc0);                 // x2
      xim2 := '';
      Result := Result or 2;
    end
    else begin
      x1 := -c1 / 2 - A3 / 4;                                   // x1
      xim1 := ' + ' + floatTostr(sqrt(-c1h2s4mc0)) + ' i';      // 複素数処理 textに変換
      x2 := -c1 / 2 - A3 / 4;                                   // x2
      xim2 := ' - ' + floatTostr(sqrt(-c1h2s4mc0)) + ' i';      // 複素数処理 textに変換
    end;

    if d1h2s4md0 >=0 then begin
      x3 := -d1 / 2 - A3 / 4 + sqrt(d1h2s4md0);                 // x3
      xim3 := '';
      Result := Result or 4;
      x4 := -d1 / 2 - A3 / 4 - sqrt(d1h2s4md0);                 // x4
      xim4 := '';
      Result := Result or 8;
    end
    else begin
      x3 := -d1 / 2 - A3 / 4;                                   // x3
      xim3 := ' + ' + floatTostr(sqrt(-d1h2s4md0)) + ' i';      // 複素数処理 textに変換
      x4 := -d1 / 2 - A3 / 4;                                   // x4
      xim4 := ' - ' + floatTostr(sqrt(-d1h2s4md0)) + ' i';      // 複素数処理 textに変換
    end;

    image2.Canvas.TextOut(10, 150,'四次方程式の解法結果');
    image2.Canvas.TextOut(20, 165,'x1= ' + floattostr(x1) + ' ' + xim1);
    image2.Canvas.TextOut(20, 180,'x2= ' + floattostr(x2) + ' ' + xim2);
    image2.Canvas.TextOut(20, 195,'x3= ' + floattostr(x3) + ' ' + xim3);
    image2.Canvas.TextOut(20, 210,'x4= ' + floattostr(x4) + ' ' + xim4);
    image2.Canvas.TextOut(20, 225, 'u= ' + floattostr(u) + ' + ' + up);
    image2.Canvas.TextOut(20, 240, 'v= ' + floattostr(v) + ' - ' + vm);

  end;
  if (abs(a) <= MINIMAM) and (abs(c) > MINIMAM) then begin
    // 二次方程式の解法
    a0 := C;
    b0 := D;
    c0 := E;
    f0 := b0 * b0 - 4 * a0 * c0;
    if abs(f0) < 1E-8 then f0 := 0;
    if f0 >= 0 then begin
      X1 := (-b0 + sqrt(f0)) / 2 / a0;
      X2 := (-b0 - sqrt(f0)) / 2 / a0;
      result := 1 + 2;
    end
    else begin
      x1 := -b0 / a0 / 2;
      x2 := x1;
      xim1 := floattostr(sqrt(abs(f0))) + ' i';
      xim2 := floattostr(- sqrt(abs(f0))) + ' i';
    end;
    with image2.Canvas do begin
      TextOut(10, 150,'二次方程式の解法結果');
      TextOut(20, 165,'x1= ' + floattostr(x1) + ' ' + xim1);
      TextOut(20, 180,'x2= ' + floattostr(x2) + ' ' + xim2);
    end;
  end;
end;

// 座標変換されているので元の座標にも戻します
procedure TForm1.trance(Xn, Yn, defQ, xo1, yo1: double; var x, y: double);
var
  xh, yh        : double;
  stn           : double;
  Q, Q1         : double;
begin
  Xn := Xn / scale;
  Yn := Yn / scale;
  Q := arctan2(Yn, Xn);
  Q1 := Q - defQ;
  stn := sqrt(xn * xn + yn * yn);
  xh := cos(Q1) * stn;
  yh := sin(Q1) * stn;
  x := xh + xo1;
  y := yh + yo1;
end;

//------------------------------------
// 二つの楕円の交点計算
//------------------------------------
function TForm1.cross_calc: boolean;
const
  Ftop = 300;
  trnQ = 30;
  k180 = 180;
  k90  = 90;
var
  x1, x2, x3, x4         : double;
  y1, y2, y3, y4         : double;

  A1, B1, C1, D1, E1, F1 : double;
  A2, B2, C2, D2, E2, F2 : double;
  A3, B3, D3, E3, F3     : double;
  A,  B,  C,  D,  E      : double;
  xflag                  : byte;
  dfq, qb, qn            : double;
  nx, ny                 : double;
  stn                    : double;
  sq, tmq                : double;
  df1, df2               : double;
  xob, yob               : double;
  q1b, q2b               : double;

begin
// 座標変換 楕円1又は 楕円2の角度が30度になるようにして計算します。
// 片方が円でもう一方が楕円の時、楕円の角度が 0, 90, 180, 270 度にある時
// 方程式の解法が正しくできません。
// その時は、楕円の角度が30°になるように座標変換をして計算します

  if ar1 > br1 then df1 := br1 / ar1
               else df1 := ar1 / br1;
  if ar2 > br2 then df2 := br2 / ar2
               else df2 := ar2 / br2;
  if df1 <= df2 then tmq := q1
                else tmq := q2;
  // q1又はq2の角度から、0~90どの範囲の角度にします
  // 補正角度を小さくするためです
  repeat
    if tmq >= k90 then tmq := tmq - k90;
    if tmq < 0    then tmq := tmq + k90;
  until (tmq >= 0) and (tmq < k90);
  // 30度との角度差
  sq := trnQ - tmq;

  nx := xo2 - xo1;
  ny := yo2 - yo1;
  // 楕円1と2の位置の角度補正
  dfq := sq /180 * pi;
  qb := arctan2(ny, nx);
  qn := qb + dfq;
  // 楕円1と2の角度補正
  q2b  := q2 + sq;
  q1b  := q1 + sq;
  // 0~180°の範囲になるように修正
  repeat
    if q2b >= k180 then q2b := q2b - k180;
    if q2b <  0    then q2b := q2b + k180;
  until (q2b < k180) and (q2b >= 0);

  repeat
    if q1b >= k180 then q1b := q1b - k180;
    if q1b <  0    then q1b := q1b + k180;
  until (q1b < k180) and (q1b >= 0);
  // 楕円2の新しい座標計算
  stn := sqrt(nx * nx + ny * ny);
  xob := cos(qn) * stn * scale;
  yob := sin(qn) * stn * scale;

// 楕円式の変換
  quadratic_transform(ar1 * scale, br1 * scale,   0,   0, q1b, A1, B1, C1, D1, E1, F1);
  quadratic_transform(ar2 * scale, br2 * scale, xob, yob, q2b, A2, B2, C2, D2, E2, F2);

// y^2 の消去
  A3 := A1 / C1 - A2 / C2;
  B3 := B1 / C1 - B2 / C2;
  D3 := D1 / C1 - D2 / C2;
  E3 := E1 / C1 - E2 / C2;
  F3 := F1 / C1 - F2 / C2;
// 四次式の係数を求めます
// Ax^4 + Bx^3 + Cx^2 + Dx + E = 0;
  A := A1*B3*B3 - A3*B1*B3 + A3*A3*C1;
  B := 2*A1*B3*E3 + 2*A3*C1*D3 + B3*B3*D1 - A3*B1*E3 - B1*B3*D3 - A3*B3*E1;
  C := A1*E3*E3 + 2*A3*C1*F3 + D3*D3*C1 + 2*B3*D1*E3 + B3*B3*F1 - B1*B3*F3
       - B1*D3*E3 - A3*E1*E3 - B3*D3*E1;
  D := 2*C1*D3*F3 + D1*E3*E3 + 2*B3*E3*F1 - B3*E1*F3 - D3*E1*E3 - B1*E3*F3;
  E := C1*F3*F3 + E3*E3*F1 - E1*E3*F3;

  image2.Canvas.Brush.Style := bsClear;
// 連立方程式の係数表示
  image2.Canvas.TextOut(10,  5, '連立方程式の係数');
  image2.Canvas.TextOut(20, 20, 'A= ' + floatTostr(A));
  image2.Canvas.TextOut(20, 35, 'B= ' + floatTostr(B));
  image2.Canvas.TextOut(20, 50, 'C= ' + floatTostr(C));
  image2.Canvas.TextOut(20, 65, 'D= ' + floatTostr(D));
  image2.Canvas.TextOut(20, 80, 'E= ' + floatTostr(E));

  image2.Canvas.TextOut(20, 100, 'B3= ' + floatTostr(B3));
  image2.Canvas.TextOut(20, 115, 'E3= ' + floatTostr(E3));

  image2.Canvas.TextOut(10, 500, '交点計算時楕円の角度');
  image2.Canvas.TextOut(20, 515, 'q1b= ' + floatTostr(q1b));
  image2.Canvas.TextOut(20, 530, 'q2b= ' + floatTostr(q2b));
  result := false;
  xflag := 0;

// 連立方程式の解放を行い x の値を計算 x の値から
// y の値を求めます 分母がゼロ場合は計算しません 分母 B3x + E3
// 四次 二次式の解法
  if (b3 <> 0) or (E3 <> 0) then
    xflag := cross_quadratic(a, b, c, d, e, x1, x2, x3, x4);

  // y の値を求めます x が虚数の場合は計算しません
  // B3x + E3 がゼロより大きい場合は四次方程式作成に利用した
  // yの値を求める式でyの値を計算します
  // 楕円の角度を30°にすることにより分母が0になることを防止しています
  if xflag <> 0 then begin
    if xflag and 1 = 1 then begin
      if abs(B3 * x1 + E3) > 0 then
        y1 := - (A3 * x1 * x1 + D3 * x1 + F3) / (B3 * x1 + E3)
      else xflag := xflag and ($FF - 1);
    end;
    if xflag and 2 = 2 then begin
      if abs(B3 * x2 + E3) > 0 then
        y2 := - (A3 * x2 * x2 + D3 * x2 + F3) / (B3 * x2 + E3)
      else xflag := xflag and ($FF - 2);
    end;
    if xflag and 4 = 4 then begin
      if abs(B3 * x3 + E3) > 0 then
        y3 := - (A3 * x3 * x3 + D3 * x3 + F3) / (B3 * x3 + E3)
      else xflag := xflag and ($FF - 4);
    end;
    if xflag and 8 = 8 then begin
      if abs(B3 * x4 + E3) > 0 then
        y4 := - (A3 * x4 * x4 + D3 * x4 + F3) / (B3 * x4 + E3)
      else xflag := xflag and ($FF - 8);
    end;
  end
  else result := true;

// 計算結果の表示 座標は元の座標に戻します
  image2.Canvas.TextOut( 10,Ftop - 15  ,'交点の座標');
  if xflag and 1 = 1 then begin
    trance(X1, Y1, dfQ, xo1, yo1, x1, y1);      // 座標変換 元の座標に戻します
    image2.Canvas.TextOut( 20,Ftop   ,'x1 =' + floatTostrF(x1,ffFixed,10,4));
    image2.Canvas.TextOut(220,Ftop   ,'y1 =' + floatTostrF(y1,ffFixed,10,4));
  end;
  if xflag and 2 = 2 then begin
    trance(X2, Y2, dfQ, xo1, yo1, x2, y2);
    image2.Canvas.TextOut( 20,Ftop+15,'x2 =' + floatTostrF(x2,ffFixed,10,4));
    image2.Canvas.TextOut(220,Ftop+15,'y2 =' + floatTostrF(y2,ffFixed,10,4));
  end;
  if xflag and 4 = 4 then begin
    trance(X3, Y3, dfQ, xo1, yo1, x3, y3);
    image2.Canvas.TextOut( 20,Ftop+30,'x3 =' + floatTostrF(x3,ffFixed,10,4));
    image2.Canvas.TextOut(220,Ftop+30,'y3 =' + floatTostrF(y3,ffFixed,10,4));
  end;
  if xflag and 8 = 8 then begin
    trance(X4, Y4, dfQ, xo1, yo1, x4, y4);
    image2.Canvas.TextOut( 20,Ftop+45,'x4 =' + floatTostrF(x4,ffFixed,10,4));
    image2.Canvas.TextOut(220,Ftop+45,'y4 =' + floatTostrF(y4,ffFixed,10,4));
  end;
// 交点丸表示
  if xflag and 1 = 1 then
    EllipseEx(image1.Canvas,
            image1.Height,
            3,
            1,
            clblack,
            5,
            5,
            (x1 - shift_x) * magnification + draw_margin,
            (y1 - shift_y) * magnification + draw_margin,
            0,
            0,
            360);
  if xflag and 2 = 2 then
    EllipseEx(image1.Canvas,
            image1.Height,
            3,
            1,
            clblack,
            5,
            5,
            (x2 - shift_x) * magnification + draw_margin,
            (y2 - shift_y) * magnification + draw_margin,
            0,
            0,
            360);
  if xflag and 4 = 4 then
    EllipseEx(image1.Canvas,
            image1.Height,
            3,
            1,
            clblack,
            5,
            5,
            (x3 - shift_x) * magnification + draw_margin,
            (y3 - shift_y) * magnification + draw_margin,
            0,
            0,
            360);
  if xflag and 8 = 8 then
    EllipseEx(image1.Canvas,
            image1.Height,
            3,
            1,
            clblack,
            5,
            5,
            (x4 - shift_x) * magnification + draw_margin,
            (y4 - shift_y) * magnification + draw_margin,
            0,
            0,
            360);
end;

//--------------------------------------------------------
// 入力された楕円の描画
// 楕円作図のy方向は、楕円作図ルーチンで修正されます。
//--------------------------------------------------------
procedure TForm1.inputdata_drawing;
var
  left_min, left_max    :  double;
  top_min,  top_max     :  double;

  all_width, all_height :  double;
  ac1, ac2              :  double;
  x1, x2, y1, y2        :  double;
begin
// 楕円の半径の大きい方選択
  ac1 := ar1;
  if ar1 < br1 then begin
     ac1 := br1;
  end;
  ac2 := ar2;
  if ar2 < br2 then begin
     ac2 := br2;
  end;
// 半径を一定の値で計算する為のスケール設定
// CONTRの値を変更した場合は、四次二次連立方程式の判別値 MINIMAM4, MINIMAM2 も変更する必要があります。
  if ac2 > ac1 then scale := CONTR / ac2
               else scale := CONTR / ac1;
// 作図範囲の設定
  left_min := xo1 - ac1;
  if xo2 - ac2 < left_min then left_min := xo2 - ac2;
  left_max := xo1 + ac1;
  if xo2 + ac2 > left_max then left_max := xo2 + ac2;
  all_width := left_max - left_min;
  top_min := yo1 - ac1;
  if yo2 - ac2 < top_min then top_min := yo2 - ac2;
  top_max := yo1 + ac1;
  if yo2 + ac2 > top_max then top_max := yo2 + ac2;
  all_height := top_max - top_min;
  top_max := (image1.Height - draw_margin * 2) / all_Height;
  magnification := (image1.Width - draw_margin * 2) / all_width;
  if magnification > top_max then magnification := top_max;
  shift_x := left_min;
  shift_y := top_min;

// イメージクリア
  imageClear;

// 楕円作図
  x1 := (xo1 - left_min) * magnification + draw_margin;
  y1 := (yo1 - top_min) * magnification + draw_margin;
  x2 := (xo2 - left_min) * magnification + draw_margin;
  y2 := (yo2 - top_min) * magnification + draw_margin;

  EllipseEx(image1.Canvas,
            image1.Height,                                    // canvas高さ
            3,                                                // 線種
            1,                                                // 線幅
            clRed,                                            // 色
            ar1 * magnification,                              // 半径a
            br1 * magnification,                              // 半径b
            x1,                                               // 中心位置x
            y1,                                               // 中心位置y
            q1,                                               // 回転角 deg
            0,                                                // 作図開始角
            360);                                             // 作図終了角
// 楕円2作図
  EllipseEx(image1.Canvas,
            image1.Height,
            3,
            1,
            clBlue,
            ar2 * magnification,
            br2 * magnification,
            x2,
            y2,
            q2,
            0,
            360);
  StraightLineDraw(image1.Canvas, image1.Height, 1, 1,  clRed, x1 - 30, y1, x1 + 30, y1, true);
  StraightLineDraw(image1.Canvas, image1.Height, 1, 1,  clRed, x1, y1 - 30, x1, y1 + 30, true);
  StraightLineDraw(image1.Canvas, image1.Height, 1, 1, clBlue, x2 - 30, y2, x2 + 30, y2, true);
  StraightLineDraw(image1.Canvas, image1.Height, 1, 1, clBlue, x2, y2 - 30, x2, y2 + 30, true);
end;

procedure TForm1.SelectBtnClick(Sender: TObject);
const
  k180 = 180;
var
  ch:       integer;
begin
// 楕円1のデーター
  val(radius_a1_Edit.Text, ar1, ch);
  if ch <> 0 then begin
    application.MessageBox('楕円1の半径a1数値ではありません。', '楕円1の半径a1', 0);
    exit;
  end;
  if ar1 <= 0 then begin
    application.MessageBox('楕円1の半径a1がゼロ 又はゼロ以下です。', '楕円1の半径a1', 0);
    exit;
  end;
  val(radius_b1_Edit.Text, br1, ch);
  if ch <> 0 then begin
    application.MessageBox('楕円1の半径b1数値ではありません。', '楕円1の半径b1', 0);
    exit;
  end;
  if br1 <= 0 then begin
    application.MessageBox('楕円1の半径b1がゼロ 又はゼロ以下です。', '楕円1の半径b1', 0);
    exit;
  end;
  val(Center_x1_Edit.Text, xo1, ch);
  if ch <> 0 then begin
    application.MessageBox('楕円1の中心位置x1数値ではありません。', '楕円1の中心位置x1', 0);
    exit;
  end;
  val(Center_y1_Edit.Text, yo1, ch);
  if ch <> 0 then begin
    application.MessageBox('楕円1の中心位置y1数値ではありません。', '楕円1の中心位置y1', 0);
    exit;
  end;
  val(angle_q1_Edit.Text, q1, ch);
  if ch <> 0 then begin
    application.MessageBox('楕円1の角度θ1数値ではありません。', '楕円1の角度θ1', 0);
    exit;
  end;
// 楕円2のデーター
  val(radius_a2_Edit.Text, ar2, ch);
  if ch <> 0 then begin
    application.MessageBox('楕円2の半径a2数値ではありません。', '楕円2の半径a2', 0);
    exit;
  end;
  if ar2 <= 0 then begin
    application.MessageBox('楕円2の半径a2がゼロ 又はゼロ以下です。', '楕円2の半径a2', 0);
    exit;
  end;
  val(radius_b2_Edit.Text, br2, ch);
  if ch <> 0 then begin
    application.MessageBox('楕円2の半径b2数値ではありません。', '楕円2の半径b2', 0);
    exit;
  end;
  val(Center_x2_Edit.Text, xo2, ch);
  if br2 <= 0 then begin
    application.MessageBox('楕円2の半径b2がゼロ 又はゼロ以下です。', '楕円2の半径b2', 0);
    exit;
  end;
  if ch <> 0 then begin
    application.MessageBox('楕円2の中心位置x2数値ではありません。', '楕円2の中心位置x2', 0);
    exit;
  end;
  val(Center_y2_Edit.Text, yo2, ch);
  if ch <> 0 then begin
    application.MessageBox('楕円2の中心位置y2数値ではありません。', '楕円2の中心位置y2', 0);
    exit;
  end;
  val(angle_q2_Edit.Text, q2, ch);
  if ch <> 0 then begin
    application.MessageBox('楕円2の角度θ2数値ではありません。', '楕円2の角度θ2', 0);
    exit;
  end;
// 楕円の角度は、一回転180°なので、180°以下にします
  repeat
    if q1 >= k180 then q1 := q1 - k180;
    if q1 < 0 then q1 := q1 + k180;
  until (q1 < k180) and (q1 >= 0);

  repeat
    if q2 >= k180 then q2 := q2 - k180;
    if q2 < 0 then q2 := q2 + k180;
  until (q2 < k180) and (q2 >= 0);

  inputdata_drawing;
// 交点が正しく計算できなかったらエラー表示
  if cross_calc then begin
    image1.Canvas.TextOut(20, 530,'交点を正しく計算できませんでした。');
  end;
  TandrwF := $04;
  StartPosBtn.Enabled := true;
  EndPosBtn.Enabled   := true;
  CalcBtn.Enabled     := False;
end;

//----------------------------楕円の接線計算------------------------------
// a, b 半径
// q    角度
// xo, yo 楕円中心座標
// x,  y  接線楕円外始点
// xt, yt 接線位置
//------------------------------------------------------------------------
function Tangent_Calc(a, b, q, xo, yo, x, y: double; var xt, yt: double): boolean;
var
  r, qo, qs: double;
  xs, ys: double;     // 始点座標
  xp, yp: double;
  Dl, Al: double;
  xa1, xa2, ya1, ya2: double;
  x1, x2, y1, y2    : double;
begin
// 楕円の位置を原点に角度を0に座標変換
  // 楕円中心から始点までの距離
  r := sqrt((x - xo) * (x - xo) + (y - yo) * (y - yo));
  // 始点の角度
  qo := arctan2(y - yo, x- xo);
  // 座標変換
  qs := qo - q;                   // 楕円の角度分戻し
  xp := cos(qs) * r;              // 接線始点の位置x
  yp := sin(qs) * r;              // 接線始点の位置y
  // 現在の接線位置座標変換
  r := sqrt((xt - xo) * (xt - xo) + (yt - yo) * (yt - yo));
  // 現在の接線位置の角度
  qo := arctan2(yt - yo, xt- xo);
  // 座標変換
  qs := qo - q;
  xt := cos(qs) * r;              // 現在の接点位置X
  yt := sin(qs) * r;              // 現在の接点位置y
  // 始点位置円座標に変換
  ys  := yp * a / b;
  xs  := xp;
// 円との接点計算
  // 始点の座標が円内にある場合計算不可
  Dl  := xs * xs + ys * ys - a * a;
  if Dl <= 0 then begin
    result := False;
    exit;
  end;
  result := true;
  Dl  := sqrt(Dl);
  Al  := xs * xs + ys * ys;
// 円上の座標
  xa1 := a * (xs * a + ys * Dl) / Al;
  xa2 := a * (xs * a - ys * Dl) / Al;
  ya1 := a * (ys * a - xs * Dl) / Al;
  ya2 := a * (ys * a + xs * Dl) / Al;

// 楕円の座標に変換
  x1 := xa1;
  x2 := xa2;
  y1 := ya1 * b / a;
  y2 := ya2 * b / a;
// 指定点と近いほうの接点選択
  Al := sqrt((x1 - xt) * (x1 - xt) + (y1 - yt) * (y1 - yt));
  Dl := sqrt((x2 - xt) * (x2 - xt) + (y2 - yt) * (y2 - yt));
  if Al < Dl then begin
    xt := x1;
    yt := y1;
  end
  else begin
    xt := x2;
    yt := y2;
  end;
// 元の座標系に変換
  r := sqrt(xt * xt + yt * yt);
  qo := arctan2(yt, xt);
  qs := qo + q;
  xt := cos(qs) * r + xo;
  yt := sin(qs) * r + yo;
end;

// マウスで指定された位置を楕円上の位置に変換
procedure TForm1.Position_Calc;
var
  q11, q12, qep: double;
  qx, qy: double;
  Dis0 : double;
//  Dis1 : double;
begin
  inputdata_drawing;
  cross_calc;
  // 楕円1の座標計算
  if TanmodF = 1 then begin
    qep := q1 / 180 * pi;                       // 楕円の角度
    q11 := arctan2(posYs - yo1, posXs - xo1);   // マウス指定点の楕円中心に対する角度
    q12 := q11 - qep;                           // 楕円に対するマウス指定点の角度
    if q12 >= 2 * Pi then q12 := q12 - 2 * pi;
    if q12 < 0 then q12 :=  q12 + 2 * pi;
    // 楕円上の座標計算 X座標
    if (abs(q12 - pi / 2) < 1E-7) or (abs(q12 - pi / 2 - pi) < 1E-7) then qx := 0
      else
        qx := 1 / sqrt((1 / ar1) * (1 / ar1) + (tan(q12) / br1) * (tan(q12) / br1));
    if cos(q12) < 0 then qx := - qx;
    // 楕円上の座標計算 Y座標
    qy := qx * tan(q12);
    Dis0 := sqrt(qx * qx + qy * qy);      // 楕円中心からの距離
{
    qx := cos(q11) * Dis0 + xo1 - posXs;
    qy := sin(q11) * Dis0 + yo1 - posYs;
    Dis1 := sqrt(qx * qx + qy * qy);
    image1.Canvas.TextOut(20,02, floatTostr(Dis1));
}
    posYs := sin(q11) * Dis0 + yo1;       // 全体Y座標
    posXs := cos(q11) * Dis0 + xo1;       // 全体X座標
  end;
  // 楕円2の座標計算
  if TanmodF = 2 then begin
    qep := q2 / 180 * pi;                       // 楕円の角度
    q11 := arctan2(posYe - yo2, posXe - xo2);   // マウス指定点の楕円中心に対する角度
    q12 := q11 - qep;                           // 楕円に対するマウス指定点の角度
    if q12 >= 2 * Pi then q12 := q12 - 2 * pi;
    if q12 < 0 then q12 :=  q12 + 2 * pi;
    // 楕円上の座標計算
    if (abs(q12 - pi / 2) < 1E-7) or (abs(q12 - pi / 2 - pi) < 1E-7) then qx := 0
      else
        qx := 1 / sqrt((1 / ar2) * (1 / ar2) + (tan(q12) / br2) * (tan(q12) / br2));
    if cos(q12) < 0 then qx := - qx;
    // 楕円上の座標計算 Y座標
    qy := qx * tan(q12);
    Dis0 := sqrt(qx * qx + qy * qy);      // 楕円中心からの距離
{
    qx := cos(q11) * Dis0 + xo2 - posXe;
    qy := sin(q11) * Dis0 + yo2 - posYe;
    Dis1 := sqrt(qx * qx + qy * qy);
    image1.Canvas.TextOut(20,02, floatTostr(Dis1));
}
    posYe := sin(q11) * Dis0 + yo2;       // 全体Y座標
    posXe := cos(q11) * Dis0 + xo2;       // 全体X座標
  end;
  TaninputDraw;   // 接線指定点の表示
end;

// 接線指定点の表示
procedure TForm1.TaninputDraw;
begin
  if TandrwF and 1 = 1 then begin
      EllipseEx(image1.Canvas, image1.Height, 3, 2, clFuchsia, 5, 5,
     (posXs - shift_x) * magnification + draw_margin, (posYs - shift_y) * magnification + draw_margin,
      0, 0, 360);
  end;
  if TandrwF and 2 = 2 then begin
      EllipseEx(image1.Canvas, image1.Height, 3, 2, clGreen, 5, 5,
     (posXe - shift_x) * magnification + draw_margin, (posYe - shift_y) * magnification + draw_margin,
      0, 0, 360);
  end;
  if TandrwF and 7 = 7 then CalcBtn.Enabled := true;
end;

// マウス割り込み
procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X,
  Y: Integer);
begin
  if TandrwF and $04 = $00 then exit;
  // 楕円1の処理
  if TanmodF = 1 then begin
    TandrwF := TandrwF or 1;
    posXs := (X - draw_margin) / magnification + shift_x;
    posYs := (Image1.Height - y - draw_margin) / magnification + shift_y;
  end;
  // 楕円2の処理
  if TanmodF = 2 then begin
    TandrwF := TandrwF or 2;
    posXe := (X - draw_margin) / magnification + shift_x;
    posYe := (Image1.Height - y - draw_margin) / magnification + shift_y;
  end;
  if (TanmodF = 2) or (TanmodF = 1) then Position_Calc;
end;

// 始点位置指定
procedure TForm1.StartPosBtnClick(Sender: TObject);
begin
  TanmodF := 1;
end;

// 終点位置指定
procedure TForm1.EndPosBtnClick(Sender: TObject);
begin
  TanmodF := 2;
end;

// 接線の計算
procedure TForm1.CalcBtnClick(Sender: TObject);
var
  qo1, qo2 : double;
  x1, y1, x2, y2: double;
  xb1, yb1, xb2, yb2: double;
  ckf: boolean;
  posBXs        : double;         // 接線始点指定位置X
  posBYs        : double;         // 接線始点指定位置y
  posBXe        : double;         // 接線終点指定位置X
  posBYe        : double;         // 接線終点指定位置y
begin
  TanmodF := 0;
  qo1 := q1 / 180 * pi;
  qo2 := q2 / 180 * pi;
  x1 := 0;
  y1 := 0;
  x2 := 0;
  y2 := 0;
  // 初期値バックアップ
  posBXs := posXs;
  posBYs := posYs;
  posBXe := posXe;
  posBYe := posYe;
  // 接点の座標が変化しなくなるまで繰り返し計算
  repeat
    // 経過値バックアップ
    xb1 := posXs;
    yb1 := posYs;
    xb2 := posXe;
    yb2 := posYe;
    // 楕円1の接線計算
    ckf := Tangent_Calc(ar1, br1, qo1 , xo1, yo1, posXe, posYe, posXs, posYs);
    if not ckf then break;  // 接線が計算できなかったらブレーク
    // 楕円2の接線計算
    ckf := Tangent_Calc(ar2, br2, qo2 , xo2, yo2, posXs, posYs, posXe, posYe);
    if not ckf then break;  // 接線が計算できなかったらブレーク
    // 直前の接線座標との差分計算
    x1 := abs(xb1 - posXs);
    y1 := abs(yb1 - posYs);
    x2 := abs(xb2 - posXe);
    y2 := abs(yb2 - posYe);
  // 全ての差分がゼロになったら終了
  until (x1 < 1E-13) and (y1 < 1E-13) and (x2 < 1E-13) and (y2 < 1E-13);
  // 接線が計算出来なかったら
  if not ckf then begin
    application.MessageBox('接線指定点の位置が適正ではありません','注意',0);
    CalcBtn.Enabled := False;
    // 座標値バックアップから戻し
    posXs := posBXs;
    posYs := posBYs;
    posXe := posBXe;
    posYe := posBYe;
    exit;
  end;
  // 表示用座標に変換
  x1 := (posXs - shift_x) * magnification + draw_margin;
  y1 := (posYs - shift_y) * magnification + draw_margin;
  x2 := (posXe - shift_x) * magnification + draw_margin;
  y2 := (posYe - shift_y) * magnification + draw_margin;
  inputdata_drawing;
  cross_calc;
  TaninputDraw;   // 接線指定点の表示
  // 接線の作図
  StraightLineDraw(image1.Canvas, image1.Height, 3, 1, clblack, x1, y1, x2, y2, False);

  image2.Canvas.TextOut( 10, 380,'接線の始点と終点');
  image2.Canvas.TextOut( 20, 395,'xps =' + floatTostrF(posXs,ffFixed,10,4));
  image2.Canvas.TextOut(220, 395,'yps =' + floatTostrF(posYs,ffFixed,10,4));
  image2.Canvas.TextOut( 20, 410,'xpe =' + floatTostrF(posXe,ffFixed,10,4));
  image2.Canvas.TextOut(220, 410,'ype =' + floatTostrF(posYe,ffFixed,10,4));
end;

// 初期設定
procedure TForm1.FormCreate(Sender: TObject);
begin
  top  := (screen.Height - height) div 2;
  left := (screen.Width - width)   div 2;
// 線種配列确保
  setlength(LPitch, LineN);
// 線種データーセット
  LPitch[0].segment := 2;         // 破線
  LPitch[0].Pitch[0] := 6;
  LPitch[0].Pitch[1] := 3;

  LPitch[1].segment := 4;         // 1点鎖線
  LPitch[1].Pitch[0] := 15;
  LPitch[1].Pitch[1] := 3;
  LPitch[1].Pitch[2] := 3;
  LPitch[1].Pitch[3] := 3;

  LPitch[2].segment := 6;         // 2点鎖線
  LPitch[2].Pitch[0] := 18;
  LPitch[2].Pitch[1] := 3;
  LPitch[2].Pitch[2] := 3;
  LPitch[2].Pitch[3] := 3;
  LPitch[2].Pitch[4] := 3;
  LPitch[2].Pitch[5] := 3;

  CalcBtn.Enabled     := False;
  StartPosBtn.Enabled := False;
  EndPosBtn.Enabled   := False;

// イメージクリア
  imageClear;
  image2.Canvas.Font.Name := 'MS ゴシック';
//  image2.Canvas.Font.Style := [fsbold];
  image2.Canvas.Font.Height := 13;
end;

end.

download Ellipses_Tangent.zip


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