楕円と直線の接線

 楕円と直線の交点に続いて、楕円と直線の接点 又は、接線を追加しました。

 接線の種類は、三種類で、楕円外の指定点を通る接線、楕円上の指定点を通る接線、傾きmの直線の接線です。
 楕円に対して垂直な接線は計算出来ません、これは交点の場合と同じです。
楕円に対して垂直な線の接点は、計算の必要はなく明らかです。
 此処でのプログラムでは、垂直になるかどうかの判別はしていませんので、垂直になる場合、演算エラーとなります。

 接線の方程式は、左図に示してあります。
楕円の中心は、原点にあり、楕円の回転は無いものとしています。
楕円の中心が、原点にない場合、楕円が回転して斜めの場合は、原点移動、楕円の傾きが無いように、座標変換をしてから計算し、計算後元の座標系に戻す必要があります。
 楕円外の点を通る接線の場合は、円の接線を利用して、計算します。

 プログラムは、単に、決められた条件で計算するようになっています。
接点の位置や、条件を変えたい場合は、値の入力が出来る様に追加が必要です。
計算結果も、数値表示はなく、計算結果に基づいて作図をしているだけです。
描画時、Y座標が逆なので、方向の変換をする為、yの値の計算が、理論式と符号が逆になっている場合があります。

プログラム

unit Main;

interface

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

type
  TForm1 = class(TForm)
    BitBtn1: TBitBtn;
    BitBtn2: TBitBtn;
    BitBtn3: TBitBtn;
    BitBtn4: TBitBtn;
    BitBtn5: TBitBtn;
    BitBtn6: TBitBtn;
    procedure BitBtn1Click(Sender: TObject);
    procedure BitBtn2Click(Sender: TObject);
    procedure BitBtn3Click(Sender: TObject);
    procedure BitBtn4Click(Sender: TObject);
    procedure BitBtn5Click(Sender: TObject);
    procedure BitBtn6Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
//------------------------------------
// 楕円外の点を通る直線との接点を求めます
// a 楕円の半径
// b 楕円の半径
// o 楕円の中心X
// p 楕円の中心Y
//------------------------------------
procedure Ovaltangent(a, b, o, p, xp, yp: double);
const
  bx = 350;      // 作図中心位置 X
  by = 150;      // 作図中心位置 Y
var
  r             : double;  // 円半径
  Al, Dl        : double;
  xs, ys        : double;  // 円座標直線の通過点座標 X Y
  xa1, ya1      : double;  // 円座標接点1
  xa2, ya2      : double;  // 円座標接点2

  x1, y1        : double;  // 楕円座標接点
  x2, y2        : double;  // 楕円座標接点

  cex,cey       : integer;
  csx,csy       : integer;

  m1, m2        : double;  // 直線傾斜係数
  c1, c2        : double;  // y直線切片

  xl1, yl1      : double;
  xl2, yl2      : double;

begin
// 円原点座標に変換
  r   := a;                  // 半径
  ys  := (yp - p) * a / b;   // 始点座標
  xs  := xp - o;
// 円との接点計算
  Dl  := abs(xs * xs + ys * ys - r * r);

  Dl  := sqrt(Dl);
  Al  := xs * xs + ys * ys;

  xa1 := r * (xs * r + ys * Dl) / Al;
  xa2 := r * (xs * r - ys * Dl) / Al;
  ya1 := r * (ys * r - xs * Dl) / Al;
  ya2 := r * (ys * r + xs * Dl) / Al;

// 楕円の座標に変換
  x1 := xa1 + o;
  x2 := xa2 + o;
  y1 := ya1 * b / a + p;
  y2 := ya2 * b / a + p;

// 直線係数
  m1 := (y1 - yp) / (x1 - xp);    // 傾斜係数
  m2 := (y2 - yp) / (x2 - xp);
  c1 := y1 - m1 * x1;             // y切片
  c2 := y2 - m2 * x2;
  csx := 50;
  // 楕円中心と通過指定点の位置でXの値設定
  if o < xp then csx := - csx;
  xl1 := x1 + csx;          // 直線の通過指定点と反対側の端点x
  xl2 := x2 + csx;

  yl1 := m1 * xl1 + c1;     // 直線のy計算
  yl2 := m2 * xl2 + c2;

// 作図 作図の為 横 bx 縦 by 位置補正
  with Form1.Canvas do begin
    // 画像消去
    Brush.Style := bsSolid;
    Brush.Color := clBtnface;
    FillRect(rect(0, 0, Form1.ClientWidth, Form1.ClientHeight));
    // 線種設定
    Brush.Style := bsClear;
    Pen.Color := clblack;
    Pen.Style := psSolid;
    Pen.Width := 1;
    // 接点が有ったらその場所に丸作図
    xa1 := x1 + bx;
    xa2 := x2 + bx;
    ya1 := y1 + by;
    ya2 := y2 + by;
    Ellipse(round(xa1 - 5), round(ya1 - 5), round(xa1 + 6), round(ya1 + 6));
    Ellipse(round(xa2 - 5), round(ya2 - 5), round(xa2 + 6), round(ya2 + 6));
    // 通過指定点 丸表示
    Brush.Color := clBlack;
    Brush.Style := bsSolid;
    xs := xp + bx;
    ys := yp + by;
    Ellipse(round(xs  - 5), round(ys  - 5), round(xs  + 6), round(ys  + 6));
    // 楕円の作図
    cex := round(a + o + bx + 1);
    cey := round(b + p + by + 1);
    csx := round(o - a + bx);
    csy := round(p - b + by);
    Pen.Color := clRed;
    Pen.Width := 1;
    Brush.Style := bsClear;
    Ellipse(csx, csy, cex, cey);
    // 直線の描画
    Pen.Width := 1;
    Pen.Color := clBlue;
    MoveTo(round(xl1 + bx), round(yl1 + by));
    LineTo(round(xp + bx), round(yp + by));
    LineTo(round(xl2 + bx), round(yl2 + by));
    // 中心線の描画
    Pen.Color := clblack;
    Pen.Width := 1;
    Pen.Style := psdashDot;
    MoveTo(round(o + bx - 45), round(p + by));
    LineTo(round(o + bx + 45), round(p + by));
    MoveTo(round(o + bx), round(p + by - 45));
    LineTo(round(o + bx), round(p + by + 45));
  end;
end;

//---------------------------------------------
// 楕円上の点X,y を通る接線
// a 楕円半径 a
// b 楕円半径 b
// o 楕円中心座標 x
// p 楕円中心座標 y
// x, y 楕円上の点座標
//---------------------------------------------
procedure Ovaltangentm(a, b, o, p, x, y: double);
const
  bx = 350;      // 作図中心位置 X
  by = 150;      // 作図中心位置 Y
var
  x1, y1    : double;
  m,  c     : double;
  xs1, ys1  : double;
  xs2, ys2  : double;

  xs,  ys   : integer;
  xe,  ye   : integer;

  cex,cey   : integer;
  csx,csy   : integer;
begin
  x1 := x - o;                    // 楕円の中心に対する値に変換
  y1 := y - p;
  m := -x1 * b * b / y1 / a / a;  // 直線の傾斜係数計算
  c := b * b / y1;                // 切片計算

  // 直線座標の計算
  xs1 := -a;
  xs2 :=  a;
  ys1 := m * xs1 + c;
  ys2 := m * xs2 + c;

// 作図 作図の為 横 bx 縦 by 位置補正
  with Form1.Canvas do begin
    // 画像消去
    Brush.Style := bsSolid;
    Brush.Color := clBtnface;
    FillRect(rect(0, 0, Form1.ClientWidth, Form1.ClientHeight));
    // 通過指定点 丸表示
    Pen.Color := clblack;
    Pen.Style := psSolid;
    Pen.Width := 1;
    Brush.Color := clBlack;
    xs := round(x) + bx;
    ys := round(y) + by;
    Ellipse(round(xs  - 5), round(ys  - 5), round(xs  + 6), round(ys  + 6));

    // 直線の描画
    xs := round(xs1 + o) + bx;
    ys := round(ys1 + p) + by;
    xe := round(xs2 + o) + bx;
    ye := round(ys2 + p) + by;

    Pen.Width := 1;
    Pen.Color := clBlue;
    MoveTo(xs, ys);
    LineTo(xe, ye);

    // 楕円の作図
    cex := round(a + o + bx + 1);
    cey := round(b + p + by + 1);
    csx := round(o - a + bx);
    csy := round(p - b + by);
    Pen.Color := clRed;
    Pen.Style := psSolid;
    Pen.Width := 1;
    Brush.Style := bsClear;
    Ellipse(csx, csy, cex, cey);
    // 中心線の描画
    Pen.Color := clblack;
    Pen.Width := 1;
    Pen.Style := psdashDot;
    MoveTo(round(o + bx - 45), round(p + by));
    LineTo(round(o + bx + 45), round(p + by));
    MoveTo(round(o + bx), round(p + by - 45));
    LineTo(round(o + bx), round(p + by + 45));
  end;
end;

//---------------------------------------------
// 楕円と傾きmの直線の接線
// a 楕円半径 a
// b 楕円半径 b
// o 楕円中心座標 x
// p 楕円中心座標 y
// m 直線の傾き
//---------------------------------------------
procedure Ovaltangentmx(a, b, o, p, m: double);
const
  bx = 350;      // 作図中心位置 X
  by = 150;      // 作図中心位置 Y
var
  c         : double;
  xs1, ys1  : double;
  xe1, ye1  : double;

  ys2, ye2  : double;

  xs,  ys   : integer;
  xe,  ye   : integer;

  cex, cey  : integer;
  csx, csy  : integer;

  x, y      : double;
begin
  // 切片
  c := sqrt(a * a * m * m + b * b);   // y切片の計算

  xs1 := -a;                          // 直線x座標
  xe1 :=  a;
  ys1 := -a * -m - c;                 // 直線y座標
  ye1 :=  a * -m - c;
  ys2 := -a * -m + c;
  ye2 :=  a * -m + c;

  // 接点
  x := - 2 * a * a * m * c / (b * b + a * a * m * m) / 2;
  y := - (x * m + c);
// 作図 作図の為 横 bx 縦 by 位置補正
  with Form1.Canvas do begin
    // 画像消去
    Brush.Style := bsSolid;
    Brush.Color := clBtnface;
    FillRect(rect(0, 0, Form1.ClientWidth, Form1.ClientHeight));
    // 通過指定点 丸表示
    Pen.Color := clblack;
    Pen.Style := psSolid;
    Pen.Width := 1;
    Brush.Color := clBlack;

    // 接点の表示
    Brush.Style := bsClear;
    xs := round(x + o) + bx;
    ys := round(y + p) + by;
    Ellipse(round(xs  - 5), round(ys  - 5), round(xs  + 6), round(ys  + 6));

    xs := round(-x + o) + bx;
    ys := round(-y + p) + by;
    Ellipse(round(xs  - 5), round(ys  - 5), round(xs  + 6), round(ys  + 6));

    // 直線の描画
    xs := round(xs1 + o) + bx;
    ys := round(ys1 + p) + by;
    xe := round(xe1 + o) + bx;
    ye := round(ye1 + p) + by;

    Pen.Width := 1;
    Pen.Color := clBlue;
    MoveTo(xs, ys);
    LineTo(xe, ye);

    ys := round(ys2 + p) + by;
    ye := round(ye2 + p) + by;

    MoveTo(xs, ys);
    LineTo(xe, ye);

    // 楕円の作図
    cex := round(a + o + bx + 1);
    cey := round(b + p + by + 1);
    csx := round(o - a + bx);
    csy := round(p - b + by);
    Pen.Color := clRed;
    Pen.Style := psSolid;
    Pen.Width := 1;
    Brush.Style := bsClear;
    Ellipse(csx, csy, cex, cey);
    // 中心線の描画
    Pen.Color := clblack;
    Pen.Width := 1;
    Pen.Style := psdashDot;
    MoveTo(round(o + bx - 45), round(p + by));
    LineTo(round(o + bx + 45), round(p + by));
    MoveTo(round(o + bx), round(p + by - 45));
    LineTo(round(o + bx), round(p + by + 45));
  end;
end;


procedure TForm1.BitBtn1Click(Sender: TObject);
begin
  OvalTangent(
            175,      // 楕円の半径 a
            100,      // 楕円の半径 b
            -80,      // 楕円の中心座標 X
            150,      // 楕円の中心座標 Y
            200,      // 直線の始点 X
              0       // 直線の始点 Y
               );
end;

procedure TForm1.BitBtn2Click(Sender: TObject);
begin
  OvalTangent(
            100,      // 楕円の半径 a
            180,      // 楕円の半径 b
             80,      // 楕円の中心座標 X
            150,      // 楕円の中心座標 Y
           -300,      // 直線の始点 X
            250       // 直線の始点 Y
               );
end;

procedure TForm1.BitBtn3Click(Sender: TObject);
const
  ox =    0;
  oy =  150;
  a  =  180;
  b  =  100;
var
  x, y : double;
begin
  x := 50;
  y := sqrt(1 - x * x / a / a) * b;
  x := x + ox;
  y := oy - y;
  OvalTangentm(
              a,      // 楕円の半径 a
              b,      // 楕円の半径 b
             ox,      // 楕円の中心座標 X
             oy,      // 楕円の中心座標 Y
              x,      // 楕円上の点 X
              y       // 楕円上の点 Y
               );
end;

procedure TForm1.BitBtn4Click(Sender: TObject);
const
  ox =  -30;
  oy =   30;
  a  =  120;
  b  =  140;
var
  x, y : double;
begin
  x := -80;
  y := - sqrt(1 - x * x / a / a) * b;
  x := x + ox;
  y := oy - y;
  OvalTangentm(
              a,      // 楕円の半径 a
              b,      // 楕円の半径 b
             ox,      // 楕円の中心座標 X
             oy,      // 楕円の中心座標 Y
              x,      // 楕円上の点 X
              y       // 楕円上の点 Y
               );
end;

procedure TForm1.BitBtn5Click(Sender: TObject);
begin
  OvalTangentmx(
            180,      // 楕円の半径 a
            100,      // 楕円の半径 b
            -80,      // 楕円の中心座標 X
            150,      // 楕円の中心座標 Y
           0.25       // 傾斜 m
               );
end;

procedure TForm1.BitBtn6Click(Sender: TObject);
begin
  OvalTangentmx(
            120,      // 楕円の半径 a
            180,      // 楕円の半径 b
            -80,      // 楕円の中心座標 X
            120,      // 楕円の中心座標 Y
           -0.3       // 傾きm
               );
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  bitbtn1.Caption := '楕円外の点を' + #13#10 + '通る接線1';
  bitbtn2.Caption := '楕円外の点を' + #13#10 + '通る接線2';
  bitbtn3.Caption := '楕円上の点を' + #13#10 + '通る接線1';
  bitbtn4.Caption := '楕円上の点を' + #13#10 + '通る接線2';
  bitbtn5.Caption := '傾きmの直線と' + #13#10 + '楕円の接線1';
  bitbtn6.Caption := '傾きmの直線と' + #13#10 + '楕円の接線2';
end;

end.

    download EllipseTangent.zip

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

      最初に戻る