楕円と直線の接線
楕円と直線の交点に続いて、楕円と直線の接点 又は、接線を追加しました。
接線の種類は、三種類で、楕円外の指定点を通る接線、楕円上の指定点を通る接線、傾き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.