2019/07/23
垂直線を扱えなかったので、X= **
の垂直線の値を扱えるようにしました。
双曲線と直線の交点
双曲線と直線の交点、更に弓型部、扇型部の面積、弓型部弧の長さの計算をします。
双曲線の弓型部、扇型部の面積では、双曲線 X2/a2 - Y2/b2 = 1 の双曲線でしたが、ここでは計算の都合上 X2/a2 - Y2/b2 = -1 の双曲線です。
双曲線の向きが、X軸方向からY軸方向に変わるだけで、基本的な計算は変わりません。
直線 y = αx + c の α の値がゼロ以外は、左右対称とならないので、左半分と、右半分に分けて計算します。
直線の傾きが漸近線の傾きと等しい場合は、交点が一か所となり、漸近線の傾きより大きい場合は上下の双曲線に交点が発生します。
直線の傾きが、漸近線の傾きより小さい場合は、交点が無いか、同じ方向の双曲線二か所の交点か、どちらかの双曲線の接線となります。
垂直の線を入力する場合は、垂直線にチェックを入れて、Xの値をしていします。
交点は、双曲線と直線の連立方程式を解けば容易に求める事ができます。
二次方程式なので、特に解説は必要無いでしょう。
面積と弧の長さの検算は、分割積分と比較して正しいかどうか確認しています。
計算式自体は、公式を使用していますが、プログラムミスの確認の為、分割積分を追加で行っています。
β点、原点、γ点での三角形の面積は、三点の座標による面積計算を行っています。
扇型部の面積が二つあるのは計算式を変えて計算した結果で、単に確認用です。
分割計算の場合は、値は完全には一致しません。
この弓型部の面積を計算する方法は、直線と交点 β、γ と
交点からY軸に対する垂線で出来る三角形の面積で基本式の面積を補正して計算します。
交点
β、γ のX方向位置が同じ方向になってしまう場合は、小さい方の弓型部の面積の符号に注意が必要です。
この計算は、単に:計算結果が正しいかどうかの確認の為に計算方法を変えて行っています。
γ(-x,y) 原点 γ(x,y) と β(-x,y) 原点 β(x,y) の二等辺三角形の面積からそれぞれの扇形部の面積を差し引いて計算した面積をもとにして計算しています。
扇形部の面積は二つ計算し、その後βγ間の傾きで補正します。
左図扇形部の面積は βy と γy の値で次の計算式でそれぞれ面積を計算し加算すれば簡単に求める事ができます。:
S1 = a * b * arccosh(βy / b) / 2
S2 = a * b * arccosh(γy
/ b) / 2
S = S1 + S2
弓形部の面積は、β 原点 γ 三点の座標の三角形の面積から扇形部の面積を差し引けば計算されます。
前記の弓形部の面積計算は、γ(-x,y) 原点 γ(x,y) と β(-x,y) 原点 β(x,y) の二等辺三角形の面積からそれぞれの扇形部の面積を差し引いて計算した面積をもとにして計算しています。
扇形部の面積は二つ計算し、その後βγ間の傾きで補正します。
双曲線と直線の図は、TChartの線グラフと、TChartのBackImageで表示しています。
TChartにもCanvasがあり、そこへ文字や図形を描画出来るのですが、最小化し、再度表示すると消えてしまいます、これは、TFormのCanvasに直接描画すると、最小化したり、裏表示になつたりすると消えてしまう現象と同じです。
原因は、Bitmap がなく、一旦消えてしまうと再生出来ない為です。
そこで、TChartと同じサイズのTImageを用意して、TImageのBitmapに書き込んだ後、TChartのBackImageのBitmapにコピーして、表示しています。
TImageは非表示にしておきます。
TChartを使用せず、全てをTImageに書き込んで゛も良いのですが、TChartを使用した方が、グラフの作成が簡単です。
TChartを使用する場合の注意点は、AutoSizeにしておいて、グラフデーター配列に追加する場合、追加が終了した時点で、グラフ表示の為の割り込み処理を実行させないと、グラフの情報を正しく読みだすことが出来ません。
データーの追加が終わった時点で、Application.ProcessMessagesを実行して、追加データーの表示をさせれば、データー追加後のグラフ情報を正しく得ることが出来ます。
プログラム
unit Main; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, VclTee.TeeGDIPlus, VCLTee.TeEngine, VCLTee.Series, Vcl.ExtCtrls, VCLTee.TeeProcs, VCLTee.Chart, Vcl.StdCtrls; type TForm1 = class(TForm) Button1: TButton; Chart1: TChart; Series1: TLineSeries; Series2: TLineSeries; Series3: TLineSeries; Series4: TLineSeries; Series5: TLineSeries; LabeledEdit1: TLabeledEdit; LabeledEdit2: TLabeledEdit; LabeledEdit3: TLabeledEdit; Memo1: TMemo; Series6: TLineSeries; LabeledEdit4: TLabeledEdit; Series7: TLineSeries; Image1: TImage; Image2: TImage; CheckBox1: TCheckBox; LabeledEdit5: TLabeledEdit; procedure Button1Click(Sender: TObject); procedure FormCreate(Sender: TObject); private { Private 宣言 } function fy: boolean; procedure Linedraw; procedure Textdraw; procedure area(bx, by, gx, gy: double); procedure second_imperfect_elliptic_integral(z, K: double; var F, E: Double); procedure arch(bx, gx, by, gy: double); procedure integral(betax, gammaX: double); procedure archpaint(bx, by, gx, gy: double); procedure Image2Draw; public { Public 宣言 } end; var Form1: TForm1; implementation {$R *.dfm} uses math; var a, b : double; // 双曲線系数 alpha, d : double; // 直線係数 xinp : double; // 垂線の位置 xpL, xpR : integer; xpF : boolean; procedure TForm1.Button1Click(Sender: TObject); label ENDNP; var ch : integer; chF : boolean; begin button1.Enabled := False; chF := True; val(LabeledEdit1.Text, a, ch); if ch <> 0 then begin application.MessageBox('aの値に間違いがあります。','注意',0); chF := False; goto ENDNP; end; if a = 0 then begin application.MessageBox('aがゼロです。','注意',0); chF := False; goto ENDNP; end; val(LabeledEdit2.Text, b, ch); if ch <> 0 then begin application.MessageBox('bの値に間違いがあります。','注意',0); chF := False; goto ENDNP; end; if b = 0 then begin application.MessageBox('bがゼロです。','注意',0); chF := False; goto ENDNP; end; if not checkBox1.Checked then begin val(LabeledEdit3.Text, alpha, ch); if ch <> 0 then begin application.MessageBox('αの値に間違いがあります。','注意',0); chF := False; goto ENDNP; end; val(LabeledEdit4.Text, d, ch); if ch <> 0 then begin application.MessageBox('dの値に間違いがあります。','注意',0); chF := False; end; end else begin val(LabeledEdit5.Text, Xinp, ch); if ch <> 0 then begin application.MessageBox('dの値に間違いがあります。','注意',0); chF := False; end; end; ENDNP: if not chF then begin button1.Enabled := True; exit; end; a := abs(a); b := abs(b); fy; button1.Enabled := True; end; // 弓形部,扇形部の面積、長さの分割積分確認計算 procedure TForm1.integral(betax, gammaX: double); var i, n : integer; dx, dx2 : double; b2, b2sa2 : double; s, x : double; x0, x1 : double; y0, y1 : double; L, dl : double; ds, ss : double; sl, sr, aa : double; begin memo1.Lines.Add(''); s := 0; n := 10000; // 分割数 dx := (gammaX - betaX) / n; // Δx dx2 := dx / 2; // 中間点 Δx/2 b2 := b * b; b2sa2 := b * b / a / a; for i:= 0 to n - 1 do begin // 積分計算 x := i * dx + dx2 + betaX; y0 := alpha * x + d; // 直線のy値 y1 := sqrt(b2 + b2sa2 * x * x); // 双曲線のy値 s := s + abs(abs(y0) - y1); end; s := s * dx; memo1.Lines.Add('分割弓形部の面積 Su = ' + floatTostr(s)); L := 0; // 弧の長さ x0 := betaX; // 最初のx計算 y0 := sqrt(b2 + b2sa2 * x0 * x0); // 最初のy計算 for i := 1 to n do begin // 積分計算 x1 := i * dx + betaX; // 双曲線x y1 := sqrt(b2 + b2sa2 * x1 * x1); // 双曲線y dl := sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)); // 微小長さΔL L := L + dl; // 合計値 x0 := x1; y0 := y1; end; y0 := sqrt(b2 + b2sa2 * betaX * betaX); // 双曲線のbetaXの位置のyの値 y1 := sqrt(b2 + b2sa2 * gammaX * gammaX); // 双曲線のgammaXの位置のyの値 dx := (abs(gammaX) + abs(betaX)) / n; // Δx dx2 := dx / 2; // 中間点 Δx/2 sl := 0; // 扇形部左 if abs(betaX) > dx then begin aa := abs(y0 / betaX); // 直線左の傾き for i := 0 to round(abs(betaX / dx)) - 1 do begin x := i * dx + dx2; ds := (sqrt(b2 + b2sa2 * x * x) - aa * x) * dx; // Δs sl := sl + ds; end; end; sr := 0; // 扇形部左 if abs(gammaX) > dx then begin aa := abs(y1 / gammaX); // 直線右の傾き for i := 0 to round(abs(gammaX / dx)) - 1 do begin x := i * dx + dx2; ds := (sqrt(b2 + b2sa2 * x * x) - aa * x) * dx; // Δs sr := sr + ds; end; end; if gammaX * betaX <= 0 then aa := sl + sr else aa := abs(sl - sr); ds := abs(betaX * y1 - gammaX * y0) / 2; // 三角形面積 ss := ds - s; // 扇形部面積 memo1.Lines.Add('分割扇形部の面積 So = ' + floatTostr(aa)); memo1.Lines.Add('分割扇形部の面積 So''= ' + floatTostr(ss)); memo1.Lines.Add('分割弓形部弧の長さ L = ' + floatTostr(L)); end; //------------------------------- // 第1種第2種楕円積分 // FBnKn[0] 第1種楕円積分解 // EBnKn[0] 第2種楕円積分解 // ランデン変換 // Q 角度 deg // K 離心率 //------------------------------- procedure TForm1.second_imperfect_elliptic_integral(z, K: double; var F, E: Double); var I, MI : integer; Kn : array of double; // Kn knd : array of double; // k'n N2SKn : array of double; // 2/(1+Kn) T2SKnd : array of double; // П2/(1+Kn') BRad : array of double; // β(rad) SinBn : array of double; // sinβn FBnKn : array of double; // F(βn,kn) EBnKn : array of double; // E(βn,kn) LnD : double; Q : double; begin // K = 0 の時は円なのでpiの角度値radをかえします。 Q := arcsin(z); if K = 0 then begin E := Q; F := E; exit; end; // この時は1をかえします 演算エラー防止の為、値は正しくありません。 if (K = 1) and (Q >= pi / 2) then begin E := 1; F := 1; exit; end; // (1 > K > 0 and Q <= 90°) or (K = 1 and Q < 90°)の時 MI := 0; LND := 0; setlength(kn, MI + 1); // kn kn[0] := K; while LND <> Kn[MI] do begin LnD := Kn[MI]; inc(MI); setlength(kn, MI + 1); // kn kn[MI] := 2 * sqrt(Kn[MI - 1])/(1 + Kn[MI - 1]); end; Dec(MI); setlength(kn, MI + 1); // kn setlength(knd, MI + 1); // k'n setlength(N2SKn, MI + 1); // 2/(1+Kn) setlength(T2SKnd, MI + 1); // П2/(1+Kn') setlength(BRad, MI + 1); // β(rad) setlength(SinBn, MI + 1); // sinβn setlength(FBnKn, MI + 1); // F(βn,kn) setlength(EBnKn, MI + 1); // E(βn,kn) knd[0] := 1; for I := 1 to MI do knd[I] := (1 - kn[I - 1]) / (1 + Kn[I - 1]); for I := 0 to MI do N2SKn[I] := 2 / (1 + kn[I]); T2SKnd[MI] := N2SKn[MI]; for I := MI - 1 downto 0 do T2SKnd[I] := T2SKnd[I + 1] * N2SKn[I]; BRad[0] := Q; for I := 1 to MI do BRad[I] := (arcsin(kn[I - 1] * sin(Brad[I - 1])) + Brad[I - 1]) / 2; for I := 0 to MI do SinBn[I] := sin(Brad[I]); LnD := LN((1 + sin(Brad[MI])) / cos(Brad[MI])); for I := 0 to MI do FBnKn[I] := T2SKnd[I] * LnD; F := FBnKn[0]; EBnKn[MI] := sin(Brad[MI]); for I := MI - 1 downto 0 do EBnKn[I] := (2 * EBnKn[I + 1] + 2 * knd[I + 1] * FBnKn[I + 1]) / (1 + knd[I + 1]) - kn[I] * SinBn[I]; E := EBnKn[0]; end; // 双曲弧の長さ計算 procedure TForm1.arch(bx, gx, by, gy: double); var e, k, z : double; Fzk, Ezk : double; L, Ll, Lr, x : double; begin e := sqrt(b * b + a * a) / b; // 離心率e k := 1 / e; // k x := abs(by); z := sqrt((x * x - b * b) / (x * x - k * k * b * b)); // z second_imperfect_elliptic_integral(z, k, Fzk, Ezk); // 第一種第二種楕円積分 Ll := (b * (1 - k * k) * Fzk - b * Ezk + x * z) / k; // Ll if bx > 0 then Ll := -Ll; // 交点βがγと同じ側の場合 x := abs(gy); z := sqrt((x * x - b * b) / (x * x - k * k * b * b)); // z second_imperfect_elliptic_integral(z, k, Fzk, Ezk); // 第一種第二種楕円積分 Lr := (b * (1 - k * k) * Fzk - b * Ezk + x * z) / k; // Ll if gx < 0 then Lr := -Lr; // 交点γがβと同じ側の場合 L := Ll + Lr; memo1.Lines.Add('弓形部弧の長さ L = ' + floatTostr(L)); memo1.Lines.Add('離心率 e = ' + floatTostr(e)); end; // 双曲線 弓形部と扇形部の面積 procedure TForm1.area(bx, by, gx, gy: double); var x4, s4 : double; v4 : double; s1, sl, sr : double; b2, b2sa2 : double; y0, y1 : double; ds, ss, s : double; begin memo1.Lines.Add(''); by := abs(by); gy := abs(gy); // 左側の計算 二倍の値が計算されます sbl := a * b * arccosh(by / b); // 扇型面積 x4 := a / b * sqrt(by * by - b * b); // x = by , y = x4 s4 := by * x4 - sbl; // 弓形面積 = 三角面積 - 扇形面積 if bx > 0 then begin // 交点βがγと同じ側の場合 s4 := -s4; sbl := -sbl; end; v4 := (gy - by) * bx / (gx - bx); // 補正三角部高さ s1 := bx * v4; // 補正三角部面積 sl := (s4 + s1); // 左側の値 // 右側の計算 二倍の値が計算されます sbr := a * b * arccosh(gy / b); // 扇型面積 x4 := a / b * sqrt(gy * gy - b * b); // x = gy , y = x4 s4 := gy * x4 - sbr; // 弓形面積 = 三角面積 - 扇形面積 if gx < 0 then begin // 交点γがβと同じ側の場合 s4 := -s4; sbr := -sbr; end; v4 := (gy - by) * gx / (gx - bx); // 補正三角部高さ s1 := gx * v4; // 補正三角部面積 sr := (s4 - s1); // 右側の値 sb := (sbl + sbr) / 2; // 左右加算の二分の一 s := (sl + sr) / 2; // 左右加算の二分の一 memo1.Lines.Add('弓形部の面積 Su = ' + floatTostr(s)); memo1.Lines.Add('扇型部の面積 So = ' + floatTostr(sb)); // 三点の値 bx,y, 0,0 gx,gy の面積 - 弓形面積 b2 := b * b; b2sa2 := b * b / a / a; y0 := sqrt(b2 + b2sa2 * bx * bx); // 双曲線のbxの位置のyの値 y1 := sqrt(b2 + b2sa2 * gx * gx); // 双曲線のgxの位置のyの値 ds := abs(bx * y1 - gx * y0) / 2; // 三角形面積 ss := ds - s; // 扇形部面積 memo1.Lines.Add('扇形部の面積 So'= ' + floatTostr(ss)); end; // 扇型部塗りつぶし procedure TForm1.archpaint(bx, by, gx, gy: double); var i, n : integer; dx : double; dpx, dpy : double; xmin, ymin : double; xmax, ymax : double; px0, py0 : integer; pxmin, pymin : integer; pxmax, pymax : integer; xp1, yp1 : integer; xp2, yp2 : integer; xf1, yf1 : double; b2, b2sa2 : double; xf2 : double; begin ymin := Chart1.LeftAxis.Minimum; ymax := Chart1.LeftAxis.Maximum; xmin := Chart1.BottomAxis.Minimum; xmax := Chart1.BottomAxis.Maximum; if d > 0 then begin px0 := Series7.CalcXPos(0); py0 := Series7.CalcYPos(0); end else begin px0 := Series7.CalcXPos(2); py0 := Series7.CalcYPos(2); end; pxmin := Series4.CalcXPos(1); pymin := Series4.CalcYPos(1); pxmax := Series4.CalcXPos(2); pymax := Series4.CalcYPos(0); dpy := (pymax - pymin) / (ymax - ymin); // pymax < pymin に注意 dpx := (pxmax - pxmin) / (xmax - xmin); b2 := b * b; b2sa2 := b * b / a / a; xf1 := dpx * bx; xf2 := dpx * gx; dx := (gx - bx) / (xf2 - xf1) * 5; n := round(abs(xf2 - xf1) / 5); Image1.Canvas.Pen.Color := clLime; Image1.Canvas.Pen.Style := psSolid; Image1.Canvas.Pen.Width := 1; for i:= 0 to n - 1 do begin xf1 := i * dx + bx; yf1 := sqrt(b2 + b2sa2 * xf1 * xf1); // 双曲線のy値 xp1 := round(dpx * (xf1)) + px0; xp2 := px0; if d > 0 then begin // 双曲線+-側で計算変更 yp1 := py0 + round(dpy * yf1); yp2 := py0; end else begin yp1 := py0 + round(dpy * -yf1); yp2 := py0; end; Image1.Canvas.MoveTo(xp1, yp1); Image1.Canvas.LineTo(xp2, yp2); end; end; // TImageに文字出力 procedure TForm1.Textdraw; var xpp, ypp: integer; begin Image1.Canvas.Font.Size := 17; // 文字の高さ Image1.Canvas.Font.Color := clRed; Image1.Canvas.Font.Name := 'Tahoma'; xpp := (Series7.CalcXPos(0) + Series7.CalcXPos(2)) div 2 - 7; // 文字aのx位置計算 ypp := Series7.CalcyPos(1) - 17; // 文字aのy位置計算 Image1.Canvas.TextOut(xpp, ypp, 'a'); // aの描画 xpp := Series7.CalcXPos(1) - 15; // 文字bのx位置計算 ypp := Series7.CalcYPos(0) - ((Series7.CalcYPos(0) - Series7.CalcYPos(2)) div 2) - 15; // 文字bのy位置計算 Image1.Canvas.TextOut(xpp, ypp, 'b'); // bの描画 xpp := (Series7.CalcXPos(0) + Series7.CalcXPos(2)) div 2 - 12; // 文字cのx位置計算 ypp := Series7.CalcYPos(0) - (Series7.CalcYPos(0) - Series7.CalcYPos(2)) div 2; if d > 0 then begin xpp := xpp + 12; ypp := ypp - 12; // 文字cのy位置計算 end else begin xpp := xpp - 12; ypp := ypp - 12; // 文字cのy位置計算 end; Image1.Canvas.TextOut(xpp, ypp, 'c'); // cの描画 if d > 0 then begin xpp := Series1.CalcXPos(xpL) - 20; ypp := Series1.CalcYPos(xpL) - 20; Image1.Canvas.TextOut(xpp, ypp, 'β'); // βの描画 end else begin xpp := Series3.CalcXPos(xpL) - 20; ypp := Series3.CalcYPos(xpL) - 20; Image1.Canvas.TextOut(xpp, ypp, 'β'); // βの描画 end; if d > 0 then begin xpp := Series1.CalcXPos(xpR); ypp := Series1.CalcYPos(xpR) - 20; Image1.Canvas.TextOut(xpp, ypp, 'γ'); // βの描画 end else begin xpp := Series3.CalcXPos(xpR); ypp := Series3.CalcYPos(xpR) - 20; Image1.Canvas.TextOut(xpp, ypp, 'γ'); // βの描画 end; end; // 直線の描画 procedure TForm1.Linedraw; var i : integer; xp1, yp1: integer; xp2, yp2: integer; begin xp1 := Series7.CalcXPos(0); // 双曲線原点X yp1 := Series7.CalcYPos(0); // 双曲線原点y xp2 := Series7.CalcXPos(2); // グラフ原点 x0 yp2 := Series7.CalcYPos(2); // グラフ原点 y0 Image1.Canvas.Pen.Color := clBlue; Image1.Canvas.Pen.Width := 2; Image1.Canvas.Pen.Style := psSolid; Image1.Canvas.MoveTo(xp1,yp1); // c の線 Image1.Canvas.LineTo(xp2,yp2); application.ProcessMessages; Image1.Canvas.Pen.Color := clskyblue; Image1.Canvas.Pen.Width := 1; Image1.Canvas.Pen.Style := psSolid; for i := xpL to xpR do begin // 双曲線弓形部塗りつぶし if i mod 2 = 0 then begin if d < 0 then begin xp1 := Series3.CalcXPos(i); // 双曲線-y側座標 yp1 := Series3.CalcYPos(i); end; if d > 0 then begin xp1 := Series1.CalcXPos(i); // 双曲線+y側座標 yp1 := Series1.CalcYPos(i); end; xp2 := Series2.CalcXPos(i); // 直線座標 yp2 := Series2.CalcYPos(i); Image1.Canvas.MoveTo(xp1,yp1); Image1.Canvas.LineTo(xp2,yp2); end; end; Image1.Canvas.Pen.Color := clblack; Image1.Canvas.Pen.Width := 1; Image1.Canvas.Pen.Style := psDashdotdot; xp1 := Series4.CalcXPos(0); xp2 := Series4.CalcXPos(2); if d > 0 then yp1 := Series7.CalcYPos(0) // y = 0 の座標 else yp1 := Series7.CalcYPos(2); // y = 0 の座標 yp2 := yp1; Image1.Canvas.MoveTo(xp1,yp1); // X軸線 Image1.Canvas.LineTo(xp2,yp2); if d > 0 then xp1 := Series7.CalcXPos(0) // x = 0 の座標 else xp1 := Series7.CalcXPos(2); // x = 0 の座標 xp2 := xp1; yp1 := Series4.CalcYPos(0); yp2 := Series4.CalcYPos(1); Image1.Canvas.MoveTo(xp1,yp1); // y軸線 Image1.Canvas.LineTo(xp2,yp2); if d > 0 then begin xp1 := Series1.CalcXPos(xpl); // beta x yp1 := Series1.CalcYPos(xpl); // beta y end else begin xp1 := Series3.CalcXPos(xpl); // beta x yp1 := Series3.CalcYPos(xpl); // beta y end; if d > 0 then begin xp2 := Series7.CalcXPos(0); // x 0 yp2 := Series7.CalcYPos(0); // y 0 end else begin xp2 := Series7.CalcXPos(2); // x 0 yp2 := Series7.CalcYPos(2); // y 0 end; Image1.Canvas.Pen.Style := psSolid; Image1.Canvas.MoveTo(xp1,yp1); // beta x,y -> 0,0 Image1.Canvas.LineTo(xp2,yp2); if d > 0 then begin xp1 := Series1.CalcXPos(xpR); // gamma x yp1 := Series1.CalcyPos(xpR); // gamma y end else begin xp1 := Series3.CalcXPos(xpR); // gamma x yp1 := Series3.CalcyPos(xpR); // gamma y end; Image1.Canvas.LineTo(xp1,yp1); // 0,0 -> gamma x,y の線 end; // 双曲線の描画 function TForm1.fy: boolean; label XPOS; const nk100 = 100; // 双曲線分割数 var betaX, betaY : double; // 双曲線と直線の交点β gammaX, gammaY : double; // 双曲線と直線の交点γ a0, b0, c0 : double; // a0x^2 + b0x + c0 = 0 e0, f0 : double; i, n, k : integer; dx, y, x : double; Xmax, Xmin, Ymax, Ymin : double; asb : double; // 漸近線傾斜係数 sbF : integer; // 漸近線フラグ bx, gx : double; begin result := false; xpF := false; Image1.Canvas.Brush.Style := bsSolid; Image1.Canvas.FillRect(rect(0,0,Image1.Width,Image1.Height)); Image1.Canvas.Brush.Style := bsClear; chart1.BackImage.Bitmap := Image1.Picture.Bitmap; memo1.Clear; Series1.Clear; // 双曲線+側 Series2.Clear; // y = a/b * x Series3.Clear; // 双曲線-側 Series4.Clear; // スケール設定 Series5.Clear; // 漸近線 Series6.Clear; // 漸近線 Series7.Clear; // abc 三角 sbF := 0; // 漸近線フラグ betaX := 0; gammaX := 0; betaY := 0; gammaY := 0; bx := 0; gx := 0; a0 := 0; e0 := 0; if checkbox1.Checked then goto XPOS; // 交点計算 連立方程式の解法 a0 := 1 / a / a - alpha * alpha / b / b; b0 := - 2 * alpha * d / b / b; c0 := - d * d / b / b + 1; e0 := b0 * b0 - 4 * a0 * c0; if e0 < 0 then begin memo1.Lines.Add('交点無し'); betaX := -(a + b) / 2 * 5; gammaX := (a + b) / 2 * 5; sbF := 1; end else begin // 同じ双曲線に交点二か所あり if a0 <> 0 then begin xpF := true; f0 := sqrt(e0); betaX := (- b0 - f0) / 2 / a0; betaY := alpha * betax + d; gammaX := (- b0 + f0) / 2 / a0; gammaY := alpha * gammax + d; memo1.Lines.Add('β x, y = ' + floatTostr(betax) + ', ' + floatTostr(betay)); memo1.Lines.Add('γ x, y = ' + floatTostr(gammax) + ', ' + floatTostr(gammay)); end; end; application.ProcessMessages; // memo1出力を確定させます if e0 = 0 then begin if d <> 0 then memo1.Lines.Add('接線'); betaX := -(a + b); gammaX := (a + b); sbf := 2; end; if betaY * gammaY < 0 then begin memo1.Lines.Add('上下双曲線に対する直線'); x := abs(betaX); y := abs(gammaX); if x > y then begin betaX := -x; gammaX := x; end else begin betaX := -y; gammaX := y; end; sbF := 3; end; if a0 = 0 then begin if b0 <> 0 then begin memo1.Lines.Add('交点一か所 漸近線と平行'); betaX := c0 / b0; betaY := alpha * betax + d; memo1.Lines.Add('β x, y = ' + floatTostr(betax) + ', ' + floatTostr(betay)); betaX := -(a + b); gammaX := (a + b); end else begin memo1.Lines.Add('交点無し 漸近線と平行'); end; sbF := 4; end; XPOS: if checkbox1.Checked then begin bx := xinp; gx := xinp; betax := xinp; gammax := xinp; if xinp < 5 then begin betax := 5; gammax := 5; end; dx := 1 + xinp * xinp / a / a; y := sqrt(dx * b * b); betay := y; gammay := -y; sbF := 5; memo1.Lines.Add('上下双曲線に対する直線'); memo1.Lines.Add('β x, y = ' + floatTostr(bx) + ', ' + floatTostr(betay)); memo1.Lines.Add('γ x, y = ' + floatTostr(gx) + ', ' + floatTostr(gammay)); end; application.ProcessMessages; // memo1出力を確定させます dx := abs((gammaX - betaX) / nk100); xmax := gammaX * 1.5; xmin := betaX * 1.5; if abs(betaX) > abs(gammaX) then begin xmax := abs(betaX); xmin := - abs(betaX) * 1.5; end; if abs(betaX) < abs(gammaX) then begin xmax := abs(gammaX) * 1.5; xmin := - abs(gammaX); end; if abs(betaX) = abs(gammaX) then begin xmax := abs(gammaX) * 1.5; xmin := - abs(betaX) * 1.5; end; xpl := 0; xpR := 0; if dx <> 0 then n := round((xmax - xmin) / dx) else begin n := nk100; dx := (xmax - xmin) / n end; asb := b / a; for i := 0 to n do begin x := i * dx + xmin; if x < betaX then xpL := i; if x < gammaX then xpR := i; y := sqrt((1+ x * x / a / a) * b * b); // 双曲線y計算 if a0 <> 0 then begin if (betay > 0) and (gammay > 0) then Series1.AddXY(x, y); // +双曲線 if (betay < 0) and (gammay < 0) then Series3.AddXY(x, -y); // -双曲線 if (betay * gammay < 0) then begin Series1.AddXY(x, y); // +双曲線 Series3.AddXY(x, -y); // -双曲線 end; end; if (a0 = 0) or (e0 < 0) then begin Series1.AddXY(x, y); // +双曲線 Series3.AddXY(x, -y); // -双曲線 end; y := asb * x; if sbF = 0 then begin if d > 0 then begin if x > -a then Series5.AddXY(x, y); // +漸近線 end else if x < a then Series5.AddXY(x, y); end else Series5.AddXY(x, y); y := -asb * x; if sbF = 0 then begin if d > 0 then begin if x < a then Series6.AddXY(x, y); // -漸近線 end else if x > -a then Series6.AddXY(x, y); end else Series6.AddXY(x, y); end; Chart1.Update; application.ProcessMessages; // グラフ出力を確定させます ymin := Chart1.LeftAxis.Minimum; ymax := Chart1.LeftAxis.Maximum; k := 0; if not checkbox1.Checked then begin for i := 0 to n do begin x := i * dx + xmin; y := alpha * x + d; // 直線y計算 if (y <= ymax * 1.5) and (y >= ymin * 1.5) then begin Series2.AddXY(x, y); // 直線 inc(k); end; end; end; if k < 3 then begin Series2.Clear; Series2.AddXY(bx, ymin * 1.5); // 直線 Series2.AddXY(gx, ymax * 1.5); // 直線 end; Chart1.Update; application.ProcessMessages; // グラフ出力を確定させます xmin := Chart1.BottomAxis.Minimum; xmax := Chart1.BottomAxis.Maximum; ymin := Chart1.LeftAxis.Minimum; ymax := Chart1.LeftAxis.Maximum; if (xmax - xmin) > (ymax - ymin) then begin dx := ((xmax - xmin) - (ymax - ymin)) / 2; ymin := ymin - dx; ymax := ymax + dx; end else begin dx := ((ymax - ymin) - (xmax - xmin)) / 2; xmin := xmin - dx; xmax := xmax + dx; end; Series4.AddXY(xmin, ymax); // スケール設定 Series4.AddXY(xmin, ymin); Series4.AddXY(xmax, ymin); Chart1.Update; application.ProcessMessages; // グラフ出力を確定させます if sbF <> 0 then exit; if d > 0 then begin Series7.AddXY(0, 0); // 一番小さい値から Series7.AddXY(0, b); // bの線 Series7.AddXY(a, b); // aの線 end else begin Series7.AddXY(-a, -b); // 一番小さい値から Series7.AddXY(0, -b); // aの線 Series7.AddXY(0, 0); // bの線 end; application.ProcessMessages; // グラフ出力を確定させます archpaint(betaX, betaY, gammaX, gammaY); Linedraw; Textdraw; integral(betaX, gammaX); area(betaX, betaY, gammaX, gammaY); arch(betaX, gammaX, betaY, gammaY); chart1.BackImage.Bitmap := Image1.Picture.Bitmap; end; // 双曲線直線式作図 procedure TForm1.Image2Draw; begin Image2.Canvas.Brush.Color := clBtnFace; Image2.Canvas.Brush.Style := bsSolid; Image2.Canvas.FillRect(rect(0, 0, Image2.Width, Image2.Height)); Image2.Canvas.Font.Name := 'MS UI Gothic'; Image2.Canvas.Font.Style := [fsItalic, fsBold]; Image2.Canvas.Font.Size := 12; Image2.Canvas.Pen.Color := clBlack; Image2.Canvas.Pen.Width := 2; Image2.Canvas.Brush.Style := bsClear; Image2.Canvas.Pen.Style := psSolid; Image2.Canvas.MoveTo(20, 37); Image2.Canvas.LineTo(45, 37); Image2.Canvas.TextOut(25, 16, 'x'); Image2.Canvas.TextOut(25, 38, 'a'); Image2.Canvas.MoveTo(20 + 45, 37); Image2.Canvas.LineTo(45 + 45, 37); Image2.Canvas.TextOut(25 + 45, 16, 'y'); Image2.Canvas.TextOut(25 + 45, 38, 'b'); Image2.Canvas.Font.Size := 10; Image2.Canvas.TextOut(34, 16, '2'); Image2.Canvas.TextOut(34, 38, '2'); Image2.Canvas.TextOut(34 + 45, 16, '2'); Image2.Canvas.TextOut(34 + 45, 38, '2'); Image2.Canvas.Font.Size := 13; Image2.Canvas.TextOut(50, 29, '-'); Image2.Canvas.TextOut(100, 29, '= -1'); Image2.Canvas.TextOut(240, 29, 'y = αx + c'); Image2.Canvas.Font.Size := 11; Image2.Canvas.Font.Style := [fsBold]; Image2.Canvas.TextOut(50, 2, '双曲線'); Image2.Canvas.TextOut(250, 2, '直線'); end; procedure TForm1.FormCreate(Sender: TObject); var bm : Tbitmap; begin bm := Tbitmap.Create; bm.Width := Chart1.Width; bm.Height := Chart1.Height; Image1.Width := Chart1.Width; Image1.Height := Chart1.Height; Image1.Visible := False; Image1.Picture.Bitmap := bm; Image1.Canvas.Brush.Color := clWhite; bm.Width := Image2.Width; bm.height := Image2.Height; Image2.Picture.Bitmap := bm; bm.Free; Image2Draw; end; end.