放物線と焦点

 放物線のプログラムに、光線の角度と、焦点の関係を作図するルーチンを追加してみました。
光線の角度のズレに対する焦点の変化が良く理解できます。

 放物線二次式と一次式の交点を求めるプログラムに入射光の角度と焦点の関係を作図するルーチンを追加した場合の実行例です。
光線の角度を固定して、放物線を移動するのは大変なので、光線の角度を変更しています。
二次式と直線の値、光の角度を決め計算ボタンをクリックすることで最初の作図がされます。
光線の角度は90度のまゝで変更しなくても、下のスライドバーを動かすことにより、光線の角度と、反射光の長さを変えることが出来ます。
僅かな角度のズレで、一点に焦点が集まらなくなることがわかります。
天体望遠鏡の場合、星はかなり遠く完全に平行光線でありますが、放物面は目的とする星に対して精度よく向ける必要があります。
焦点距離を長くすれば、計算上のズレは小さくなるようですが、放物面の精度を高くする必要があるようです。


プログラム
 条件によっては、光線が放物面で実際には複数回反射する場合も場合もありますが、此処の計算では一度の反射しか計算しません。

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.ComCtrls;

type
  TForm1 = class(TForm)
    Chart1: TChart;
    Series1: TLineSeries;
    Series2: TLineSeries;
    Memo1: TMemo;
    Series3: TLineSeries;
    Series4: TLineSeries;
    Series6: TLineSeries;
    Image1: TImage;
    Panel1: TPanel;
    Label1: TLabel;
    Label3: TLabel;
    LabeledEdit1: TLabeledEdit;
    LabeledEdit3: TLabeledEdit;
    LabeledEdit4: TLabeledEdit;
    LabeledEdit5: TLabeledEdit;
    Button1: TButton;
    LabeledEdit2: TLabeledEdit;
    Label4: TLabel;
    Label2: TLabel;
    LabeledEdit6: TLabeledEdit;
    Label5: TLabel;
    Series5: TPointSeries;
    Label6: TLabel;
    ScrollBar1: TScrollBar;
    Label7: TLabel;
    ScrollBar2: TScrollBar;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure ScrollBar1Scroll(Sender: TObject; ScrollCode: TScrollCode;
      var ScrollPos: Integer);
    procedure ScrollBar2Scroll(Sender: TObject; ScrollCode: TScrollCode;
      var ScrollPos: Integer);
    procedure LabeledEdit1Change(Sender: TObject);
    procedure LabeledEdit4Change(Sender: TObject);
  private
    { Private 宣言 }
    function datainput: boolean;
    function calc_point_of_intersection: boolean;
    procedure graphDraw;
    function calc_arc: boolean;
    procedure fpoint(x, y, ql, qs, xl : double; var xf, yf: double);
    procedure division;
    procedure focusPoint;
    procedure ImageClear;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses math;

var
  x1, x2          : double;       // 放物線と直線の交点
  y1, y2          : double;
  xc0, yc0        : double;       // 放物線原点
  xcc, ycc        : double;       // 接線の交点
  a1, b1, c1      : double;       // 放物線二次式の値
  b2, c2          : double;       // 直線の値
  alpha, beta     : double;       // 交点の位置面積計算用
  alpy, bety      : double;
  CF              : boolean;      // 交点の有無フラグ
  Q0              : double;       // 入射光線角度仮
  focusY          : double;       // 焦点のY位置
  focusL          : double;       // 焦点の距離
  ImgF            : boolean;      // Imageの作図のみのフラグ
  sclpos          : double;       // 反射光線の長さ係数
  SCF             : Boolean;      // 反射光線の長さ調整有無フラグ
  STF             : Boolean;      // Button1により計算開始フラグ

// 入力値処理
function TForm1.datainput: boolean;
var
  ch: integer;
  Q, Qmin, Qmax: double;
  Qstr : string;
begin
  result := false;
  SCF := False;                     // 反射光線の長さ調整有無フラグ
  STF := True;                      // Button1により計算開始フラグ
  val(LabeledEdit1.Text, a1, ch);
  if ch <> 0 then begin
    application.MessageBox('α1の値に間違いがあります。','',0);
    exit;
  end;
  if a1 = 0 then begin
    application.MessageBox('α1の値はゼロではいけません。','',0);
    exit;
  end;
  val(LabeledEdit2.Text, b1, ch);
  if ch <> 0 then begin
    application.MessageBox('β1の値に間違いがあります。','',0);
    exit;
  end;
  val(LabeledEdit3.Text, c1, ch);
  if ch <> 0 then begin
    application.MessageBox('C1の値に間違いがあります。','',0);
    exit;
  end;
  val(LabeledEdit4.Text, b2, ch);
  if ch <> 0 then begin
    application.MessageBox('β2の値に間違いがあります。','',0);
    exit;
  end;
  val(LabeledEdit5.Text, c2, ch);
  if ch <> 0 then begin
    application.MessageBox('C2の値に間違いがあります。','',0);
    exit;
  end;
  val(LabeledEdit6.Text, Q0, ch);
  if ch <> 0 then begin
    application.MessageBox('光線角度の値に間違いがあります。','',0);
    exit;
  end;
  Q := arctan(b2) / pi * 180;
  Qstr := floatTostrF(Q, ffFixed, 5,3);
  Qmin := 0;
  Qmax := 180;
  // 計算出来る光角度範囲の設定
  if a1 > 0 then begin
    if Q > 0 then begin
      Qmin := Q;
      Qmax := 180;
    end;
    if Q < 0 then begin
       Qmin := 0;
       Qmax := Q + 180
    end;
  end
  else
  begin
    if Q > 0 then begin
      Qmin := 0;
      Qmax := 180 - Q;
    end;
    if Q < 0 then begin
      Qmin := - Q;
      Qmax := 180;
    end;
  end;
  scrollbar2.Max := round(Qmax - 0.5);
  scrollbar2.Min := round(Qmin + 0.5);
  scrollbar1.Position := 50;
  Qstr := FloatTostrF(Qmin, ffFixed, 4, 1) + '°<角度<' + FloatTostrF(Qmax, ffFixed, 4, 1)+ '°';
  LabeledEdit6.EditLabel.Caption := Qstr;
  if Q0 < Qmin then begin
    application.MessageBox('光線の角度が小さすぎます。','',0);
    exit;
  end;
  if Q0 > Qmax then begin
    application.MessageBox('光線の角度が大きすぎます。','',0);
    exit;
  end;
  if Q0 = 0 then begin
    application.MessageBox('光線の角度がゼロでは計算出来ません。','',0);
    exit;
  end;
  if Q0 = 180 then begin
    application.MessageBox('光線の角度が180°では計算出来ません。','',0);
    exit;
  end;
  scrollbar2.Position := round(Q0);
  SCF := True;                          // 反射光線の長さ調整有無フラグ
  // 条件により入力角度を計算用角度に変更
  if a1 < 0 then Q0 := - Q0;
  if a1 > 0 then begin
    if Q0 >   90 then Q0 := Q0 - 180;
  end
  else begin
    if Q0 <= -90 then Q0 := 180 + Q0;
  end;
  result := true;
end;

// 面積 弧の長さΔx分割計算
procedure TForm1.division;
var
  i, n        : integer;
  dx, dx2     : double;
  Xmax, Xmin  : double;
  x0, y0      : double;
  y3          : double;
  L, ls       : double;
  x, y        : double;
begin
  n := 10000;
  dx := (x2 - x1) / n;
  L := 0;
  x0 := x1;
  y0 := x0 * x0 * a1 + x0 * b1 + c1;
  for i := 1 to n do begin
    x := i * dx + x1;
    y := x * x * a1 + x * b1 + c1;
    Xmin := (x - x0);
    Xmax := (y - y0);
    ls := sqrt(Xmin * Xmin + Xmax * Xmax);
    L := L + Ls;
    x0 := x;
    y0 := y;
  end;
  memo1.Lines.Add('分割積分長さ = ' + floatTostr(L));

  dx2 := dx / 2;
  L := 0;
  for i := 0 to n - 1 do begin
    x := i * dx + dx2 + x1;
    y3 := x * b2 + c2;
    y := x * x * a1 + x * b1 + c1 - y3;
    ls := y * dx;
    L := L + Ls;
  end;
  L := abs(L);
  memo1.Lines.Add('分割積分面積= ' + floatTostr(L));
end;

// 弓形部長さ計算
// 右と左に分けて計算
function TForm1.calc_arc: boolean;
var
  L, L1, L2 : double;
  s1, s2    : double;
  a3, b3    : double;
  a4, b4    : double;
begin
  xc0 := -b1 / a1 / 2;                               // 放物線 変曲点 原点X
  yc0 := xc0 * xc0 * a1 + xc0 * b1 + c1;             //        変曲点 原点y
  memo1.Lines.Add('O x,y = ' + floatTostr(xc0) + ', ' + floatTostr(yc0));
  b3 := abs(x1 - xc0) * 2;
  a3 := abs(y1 - yc0);                               // 放物線のx1の高さ
  if a3 <> 0 then begin
    s1 := sqrt(b3 * b3 + 16 * a3 * a3);
    L1 := s1 / 2 + b3 * b3 / 8 / a3 * ln((4 * a3 + s1) / b3);
  end
  else L1 := 0;
  b4 := abs(x2 - xc0) * 2;
  a4 := abs(y2 - yc0);                              // 放物線のx2の高さ
  if a4 <> 0 then begin
    s2 := sqrt(b4 * b4 + 16 * a4 * a4);
    L2 := s2 / 2 + b4 * b4 / 8 / a4 * ln((4 * a4 + s2) / b4);
  end
  else L2 := 0;
  if (x1 - xc0) * (x2 - xc0) > 0 then L := abs(L2 - L1) / 2
                               else L := (L1 + L2) / 2;
  memo1.Lines.Add('');
  memo1.Lines.Add('弓形部弧長さ =  ' + floatTostr(L));
  result := true;
end;

// 放物線弓形部の面積
// 1/6公式で計算
function TForm1.calc_point_of_intersection: boolean;
var
  i , n: integer;
  dx              : double;
  Xmax, Xmin      : double;
  x, y            : double;
  a0, b0, c0      : double;
  b3, c3, b4, c4  : double;
  d, e, s         : double;
  ycp             : double;
begin
  result := False;
  a0 := a1;                             // 放物線と直線の交点計算
  b0 := b1 - b2;
  c0 := c1 - c2;
  d := b0 * b0 - 4 * a0 * c0;
  if d < 0 then exit;                   // ルートの中がマイナスなら交点なし
  e := sqrt(d);
  x1 := (-b0 - e) / 2 / a0;             // 交点x1座標
  x2 := (-b0 + e) / 2 / a0;             // 交点x2座標
  alpha := x1;
  beta  := x2;
  y1 := x1 * b2 + c2;                   // 交点y1座標
  y2 := x2 * b2 + c2;                   // 交点y2座標
  alpy := y1;
  bety := y2;
  if beta < alpha then begin            // 値の大きさで入れ替え β > αにします、面積計算の為
    alpha := x2;
    beta := x1;
    alpy := y2;
    bety := y1;
  end;

  focusPoint;                           // 光軸による焦点位置の計算

  memo1.Lines.Add('α x,y = ' + floatTostr(alpha) + ', ' + floatTostr(alpy));
  memo1.Lines.Add('β x,y = ' + floatTostr(beta) + ', ' + floatTostr(bety));
  b3 := 2 * a1 * alpha + b1;                // y1=m1x+c1 のm1の値
  c3 := - b3 * alpha + alpy;                // y1=m1x+c1 のc1の値
  b4 := 2 * a1 * beta + b1;                 // y2=m2x+c2 のm2の値
  c4 := - b4 * beta + bety;                 // y2=m2x+c2 のc2の値
  if b3 <> b4 then                          // 二つの直線の傾きに差があったら交点あり
    xcc := (c4 - c3) / (b3 - b4)            // y1=m1x+c1 と y2=m2x+c2 の交点xの値
  else xcc := 0;                            // 差が無かったら交点無し
  ycc := b3 * xcc + c3;                     // y1=m1x+c1 と y2=m2x+c2 の交点yの値
  memo1.Lines.Add('C x,y = ' + floatTostr(xcc) + ', ' + floatTostr(ycc));
  if not calc_arc then exit;
  result := true;
  s := abs(a1) * (beta - alpha)  * (beta - alpha) * (beta - alpha) / 6;   // 弓形部面積計算 1/6公式
  memo1.Lines.Add('弓形部面積   = ' + floatTostr(s));
  if (b3 = b4) then exit;

  division;                                 // 面積 弧の長さΔx分割計算

  if a1 > 0 then begin
    if yc0 > ycc then ycp := ycc - (yc0 - ycc) / 2  // 直線のyの描画範囲
                 else ycp := yc0 - 0.1 / a1;
  end
  else begin
    if yc0 < ycc then ycp := ycc + (ycc - yc0) / 2  // 放物線a1x^2のa1の符号±で方向変更
                 else ycp := yc0 - 0.1 / a1;
  end;
  n := 1000;
  Xmin := x1 - (x2 - X1) / 4;
  Xmax := x2 + (X2 - X1) / 4;
  dx := (Xmax - Xmin) / n;
  for i := 0 to n do begin                // 接線1の描画
    x := i * dx + Xmin;
    y := b3 * x + c3;
    if a1 > 0 then if y > ycp then Series3.AddXY(x,y);
    if a1 < 0 then if y < ycp then Series3.AddXY(x,y);
  end;
  for i := 0 to n do begin                // 接線2の描画
    x := i * dx + Xmin;
    y := b4 * x + c4;
    if a1 > 0 then if y > ycp then Series4.AddXY(x,y);
    if a1 < 0 then if y < ycp then Series4.AddXY(x,y);
  end;
end;

// x, y 放物線と光線の交点
// ql   光線の角度
// qs   接線の角度
// xl x1,y1 とx2,y2 の距離
// 光線の終端座標
procedure TForm1.fpoint(x, y, ql, qs, xl : double; var xf, yf: double);
var
  qm, qb    : double;   // qsをゼロにした場合の反射角qm  戻し角qb
  x1, y1, l : double;
  x2, y2    : double;
  dx, dy    : double;
begin
//  memo1.Lines.Add('光角度 ' + floatTostr(ql / pi * 180));
//  memo1.Lines.Add('接線角度 ' + floatTostr(qs / pi * 180));
  qm := (qs - ql);                                            // 接線に対する光線角度計算
  if qm < pi then qm := qm + pi;
//  memo1.Lines.Add('反射角度 ' + floatTostr(qm / pi * 180));
  x1 := cos(qm) * xl;                                         // 光線端点x位置計算
  y1 := sin(qm) * xl;                                         // 光線端点y位置計算
  l  := sqrt(x1 * x1 + y1 * y1);                              // 座標変換用距離計算
  qb := qm + qs;                                              // 接線の角度に戻し
//  memo1.Lines.Add('回転後角度 ' + floatTostr(qb / pi * 180));
  x2 := cos(qb) * l;                                          // 接線の角度に戻し後x座標
  y2 := sin(qb) * l;                                          // 接線の角度に戻し後y座標
  if ql * a1 > 0 then begin                 // 光線の角度が+だったら x,y標値加算
    xf := x + x2;
    yf := y + y2;
  end
  else begin                                // 光線の角度が-だったら x,y標値減算
    xf := x - x2;
    yf := y - y2;
  end;
  // 光端座標がグラフ外になるのを防止します
  dy := yf - y;
  dx := xf - x;
  if yf > Chart1.LeftAxis.Maximum then begin
    yf := Chart1.LeftAxis.Maximum;
    xf := dx * ((Chart1.LeftAxis.Maximum - y) / dy) + x;
  end;
  if yf < Chart1.LeftAxis.Minimum then begin
    yf := Chart1.LeftAxis.Minimum;
    xf := dx * ((Chart1.LeftAxis.Minimum - y) / dy) + x;
  end;
  if xf < Chart1.BottomAxis.Minimum then begin
    xf := Chart1.BottomAxis.Minimum;
    yf := dy * ((Chart1.BottomAxis.Minimum - x) / dx) + y;
  end;
  if xf > Chart1.BottomAxis.Maximum then begin
    xf := Chart1.BottomAxis.Maximum;
    yf := dy * ((Chart1.BottomAxis.Maximum - x) / dx) + y;
  end;
end;

// 光軸による焦点位置の計算
procedure TForm1.focusPoint;
var
  x, y, m   : double;
  xo        : double;                // 放物線原点x
//  yo        : double;                // 放物線原点y
  q, qp, mq : double;
  mfc       : double;
begin
  xo := - b1 / a1 / 2;
//  yo := xo / 2 * b1;
//  memo1.Lines.Add('o x,y = ' + floatTostr(xo) + ', ' + floatTostr(yo));
  x := xo + 2;
  y := a1 * x * x + b1 * x + c1;                    // X=1の放物線のyの値
  m := 2 * a1 * x + b1;                             // 接線y=mx+c のm
  q := arctan(m);                                   // 接線の角度
  qp := 2 *  q + pi / 2;                            // 反射角
  mq := tan(qp);                                    // 反射光傾き

  mfc := -mq * x + y;                               // 反射光直線式 y := mqx + mfc のmfc
  focusY := xo * mq + mfc;                          // 中心位置の 光線通過点 焦点位置
end;

// 放物線と直線の描画
// 光線作図
procedure TForm1.graphDraw;
label
  IMG;
var
  i, n        : integer;
  dx, dx2     : double;
  Xmax, Xmin  : double;
  Ymax, Ymin  : double;
  Hmax, Hmin  : double;
  Vmax, Vmin  : double;
  L, ls       : double;
  x, y        : double;

  x3, y3      : double;
  x4, y4      : double;
  x0m, y0m    : double;
  x1m, y1m    : double;
  Vx, Vy      : double;
  ypl, ypm    : integer;
  xpl, xpm    : integer;
  ax, ay      : double;
  ysp, ypp    : integer;
  xsp, xpp    : integer;
  fys         : integer;
  ma, mc      : double;
  b0, c0      : double;
  d, e        : double;
  b3, c3      : double;
  Q1, Q4, Q5  : double;
  Q2          : double;
  mf, mfc     : double;
  yback       : double;
  xl          : double;
  foc         : double;
begin
  if ImgF then goto IMG;
  focusL := 1 / 4 / a1;                          // 焦点距離計算
  // 放物線と交わる直線の描画
  n := 1000;
  Xmin := x1 - (x2 - x1) / 4;         // 描画範囲
  Xmax := x2 + (x2 - x1) / 4;
  if (Xmax - Xmin) = 0 then begin     // 接線となるときの描画範囲
    Xmin := x1 - abs(1 / a1);
    Xmax := x1 + abs(1 / a1);
    CF := False;
  end;
  if CF then begin                   // 交点があったら
    dx := abs(Xmax - Xmin) / 10;
    if a1 > 0 then begin
      if Xmin + dx > xc0 then Xmin := xc0 - dx;
      if Xmax - dx < xc0 then Xmax := xc0 + dx;
    end
    else begin
      if Xmax + dx > xc0 Then Xmax := xc0 - dx;
      if Xmin - dx < xc0 then Xmin := xc0 + dx;
    end;
  end;
  dx := abs(Xmax - Xmin) / n;
  if Xmax < Xmin then Xmin := Xmax;

  for i := 0 to n do begin
    x := i * dx + Xmin;
    y := x * x * a1 + x * b1 + c1;      // 放物線
    Series1.AddXY(x, y);                // 放物線のグラフ
    y := x * b2 + c2;                   // 直線
    Series2.AddXY(x, y);                // 直線のグラフ
  end;
  if CF then begin                   // 交点があったら
    dx := (x2 - x1) / n;
    for i := 0 to n do begin
      x := i * dx + x1;
      y := x * x * a1 + x * b1 + c1;    // 放物線
      Series6.AddXY(x, y);              // 放物線x1,x2間のグラフ
    end;
  end;

  Chart1.Update;
  application.ProcessMessages;          // 描画待ち
  // Chartの縦横比を同じに設定します
  Hmax := Chart1.BottomAxis.Maximum;
  Hmin := Chart1.BottomAxis.Minimum;
  Vmax := Chart1.LeftAxis.Maximum;
  Vmin := Chart1.LeftAxis.Minimum;
  if (focusy * 0.95) > Chart1.LeftAxis.Maximum then begin
    Vmax := focusy / 0.95;
    if (90 > Q0) and (Q0 > 0) then Vmax := Vmax + focusL / 1.5;
  end;
  if (focusy * 0.95) < Chart1.LeftAxis.Minimum then begin
    Vmin := focusy / 0.95;
    if (0 > Q0) and (Q0 > -90) then Vmin := Vmin + focusL / 1.5;
  end;
  if (Hmax - Hmin) > (Vmax - Vmin) then begin
    dx2 := ((Hmax - Hmin) - (Vmax - Vmin)) / 2;
    Vmax := Vmax + dx2;
    Vmin := Vmin - dx2;
  end
  else begin
    dx2 := ((Vmax - Vmin) - (Hmax - Hmin)) / 2;
    Hmax := Hmax + dx2;
    Hmin := Hmin - dx2;
  end;
  Series5.AddXY(Hmin, Vmax);            // Hmax,Hmin,Vmax,Vmin 反映の為の Series
  Series5.AddXY(Hmin, Vmin);
  Series5.AddXY(Hmax, Vmin);
  Chart1.Update;
  application.ProcessMessages;          // 描画待ち

  if not CF then exit;                  // 交点がなかったら光線軸計算無し
  scrollbar2.Enabled := True;;
  scrollbar1.Enabled := True;
IMG:
  if STF then begin                     // Button1により計算開始フラグ
    foc := 0;                           // 反射角90°時反射光補正長さゼロ
    sclpos := 1;                        // 反射光長さ補正スケール1
  end
  else foc := focusL;                   // 反射角90°時反射光補正長さ焦点距離に設定
  STF := False;                         // Button1により計算開始フラグ
  ypl := chart1.LeftAxis.IStartPos;     // y軸スタート座標
  ypm := chart1.LeftAxis.IEndPos;       // y軸エンド座標
  xpl := chart1.BottomAxis.IStartPos;   // x軸スタート座標
  xpm := chart1.BottomAxis.IEndPos;     // x軸エンド座標
  Hmax := chart1.BottomAxis.Maximum;
  Hmin := chart1.BottomAxis.Minimum;
  Vmax := chart1.LeftAxis.Maximum;
  Vmin := chart1.LeftAxis.Minimum;
  ax := (xpm - xpl) / (Hmax - Hmin);    // x方向座標変換係数
  ay := (ypm - ypl) / (Vmax - Vmin);    // y方向座標変換係数
  xsp := round((x2 - Hmin) * ax) + xpl; // 分割数設定の為の計算
  ysp := round((x1 - Hmin) * ax) + xpl;
  n := abs(xsp - ysp) div 21;           // 分割数
  if n < 5 then n := 5;
  dx := (x2 - x1) / n;
  Q5 := Q0 / 180 * pi;                  // 光線の角度deg
  yback := 0;                           // 焦点のy位置クリア
  for i := 0 to n do begin                            // 左から右へ計算
    x :=   i * dx + x1;                                 // xの値             x
    xsp := round((x - Hmin) * ax) + xpl;
    y := x * b2 + c2;                                   // 直線yの値         y
    if Q0 <> 90 then begin                              // 光線90°でない時
      ma := tan(Q5);                                      // 直線の傾き
      mc := y - ma * x;                                   // 傾き maの直線 y = max + mc
      b0 := b1 - ma;                                      // 放物線と光線の交点計算
      c0 := c1 - mc;
      d :=  b0 * b0 - 4 * a1 * c0;
      e := sqrt(d);

      if Q5 > 0 then x3 := (-b0 - e) / 2 / a1             // 放物線交点xの値     x3
                else x3 := (-b0 + e) / 2 / a1;
      y3 := x3 * x3 * a1 + x3 * b1 + c1;                  // 放物線交点yの値     y3
      // 光線の入射位置をグラフの端点に変更
      if a1 > 0 then Vy := Vmax
                else Vy := Vmin;
      x0m := (Vy - mc) / ma;
      y0m := Vy;
      if ma > 0 then begin
        if a1 > 0 then Vx := Hmax
                  else Vx := Hmin;
      end
      else begin
        if a1 > 0 then Vx := Hmin
                  else Vx := Hmax;
      end;
      y1m := ma * Vx + mc;
      x1m := Vx;
      if a1 > 0 then begin
        if y0m > y1m then begin
          y0m := y1m;
          x0m := x1m;
        end;
      end
      else begin
        if y0m < y1m then begin
          y0m := y1m;
          x0m := x1m;
        end;
      end;
      xsp := round((x0m - Hmin) * ax) + xpl;              // 入射光直線端点xの座標
      ysp := round((Vmax - y0m) * ay) + ypl;              // 入射光直線端点yの座標
    end
    else begin                                          // 光線90°の時
      X3 := x;                                            // 放物線xの値
      y3 := x3 * x3 * a1 + x3 * b1 + c1;                  // 放物線yの値     y3
      ysp := ypl;                                         // 直線yの座標
      if a1 < 0 then ysp := round((Vmax - Vmin) * ay) + ypl;
    end;
    ypp := round((Vmax - y3) * ay) + ypl;               // 放物線yの座標
    xpp := round((x3 - Hmin) * ax) + xpl;               // 放物線xの座標

    Image1.Canvas.Pen.Color := clSkyblue;
    Image1.Canvas.MoveTo(xsp, ysp);                     // 直線のx位置から
    Image1.Canvas.LineTo(xpp, ypp);                     // 放物線xの位置へ線描画

    b3 := 2 * a1 * x3 + b1;                             // 接線y1=m1x+c1 のm1の値  b3
    c3 := - b3 * x3 + y3;                               // 接線y1=m1x+c1 のc1の値  c3

    if Q0 <> 90 then begin                            // 光線角度90°以外だったら
      xl := sqrt((x3 - xc0) * (x3 - xc0) + (y3 - focusY) * (y3 - focusY));  // 反射光長
      xl := (xl + abs(focusL)) * sclpos;                // 反射光長補正
      Q1 := arctan(b3);                                 // 反射点の放物線との接線の角度rad
      fpoint(x3, y3, Q5, Q1, xl, x4, y4);               // 反射光線の端点位置を求めるサブルーチン
      xsp := round((x4 - Hmin) * ax) + xpl;             // 光線端点位置xの座標
      ysp := round((Vmax - y4) * ay) + ypl;             // 光線端点位置yの座標
    end
    else begin                                        // 光線90°の時
      Q2 := arctan(b3) / pi * 180;                      // 反射点の放物線との接線の角度deg
//      Q2 := 90 + Q1;
//      Q3 := 90 - Q2;
//      Q4 := Q2 - Q3;
      Q4 := 90 + 2 * Q2;                                // 反射角deg
      if Q4 <> 90 then begin                            // 反射角が90°以外だったら
        mf := tan(Q4 / 180 * pi);                         // 直線の傾き角rad
        mfc := -mf * x3 + y3;                             // 直線 y = mfX + mfc
        x4 := xc0;                                        // 放物線原点x
        y4 := X4 * mf + mfc;                              // 放物線原点xの反射光のy値
        yback := y4;                                      // y値のバックアップ
        Q1 := arctan(b3);                                 // 反射点の放物線との接線の角度rad
        xl := sqrt((x3 - x4) * (x3 - x4) + (y3 - y4) * (y3 - y4));  // 反射光長
        xl := (xl + abs(foc)) * sclpos;                   // 反射光長補正
        fpoint(x3, y3, Q5, Q1, xl, x4, y4);               // 反射光線の端点位置を求めるサブルーチン

        xsp := round((x4 - Hmin) * ax) + xpl;             // 焦点位置xの座標
        ysp := round((Vmax - y4) * ay) + ypl;             // 焦点位置y座標
      end
      else begin                                        // 反射角90°の時
        xsp := round((x - Hmin) * ax) + xpl;              // 焦点位置x座標
        if yback = 0 then y4 := abs(y3 + focusL + foc) * sclpos   // 垂直方向反射光長
                     else y4 := (yback + foc) * sclpos;
        if y4 > Vmax then y4 := Vmax;
        if y4 < Vmin then y4 := Vmin;
        ysp := round((Vmax - y4) * ay) + ypl;             // 反射光端点位置y座標
      end;
    end;
    Image1.Canvas.Pen.Color := clPurple;                  // 反射光線
    Image1.Canvas.MoveTo(xsp, ysp);                       // 光線端点xy座標から
    Image1.Canvas.LineTo(xpp, ypp);                       // 放物線交点座標
  end;
  if not ImgF then begin
    memo1.Lines.Add('');
    if abs(Q0) = 90 then begin
      memo1.Lines.Add('焦点x, y座標 = ' + floatTostrF(xc0, ffFixed, 13, 8) + ', '
                                                      + floatTostrF(yback, ffFixed, 13, 8))
    end
    else begin
      memo1.Lines.Add('焦点x, y座標 = ' + floatTostrF(xc0, ffFixed, 13, 8) + ', '
                                                      + floatTostrF(focusY, ffFixed, 13, 8));
      memo1.Lines.Add('焦点座標 拡散');
    end;
    memo1.Lines.Add('焦点距離 = ' + floatTostrF(focusL, ffFixed, 13, 8));
  end;

  // 文字の描画 α,β,c,o 
  fys := 12;
  Image1.Canvas.Font.Size := fys;
  Image1.Canvas.Font.Color := clRed;
  Image1.Canvas.TextOut(xpl + 10, ypl, '光線角度 = ' + floatTostr(Q0) + '°');
  Image1.Canvas.Font.Color := clBlack;
  Image1.Canvas.Font.Size := fys;
  xsp := round((beta - Hmin) * ax) + xpl + 10;
  ysp := round((Vmax - bety) * ay) + ypl - fys;
  Image1.Canvas.TextOut(xsp,ysp,'β');                  // 直線と放物線の座標β
  xsp := round((alpha - Hmin) * ax) + xpl - 22;
  ysp := round((Vmax - alpy) * ay) + ypl - fys;
  Image1.Canvas.TextOut(xsp,ysp,'α');                  // 直線と放物線の座標α

  xsp := round((xcc - Hmin) * ax) + xpl - 22;
  ysp := round((Vmax - ycc) * ay) + ypl - fys;
  Image1.Canvas.TextOut(xsp,ysp,'c');                   // 二接線の交点座標c

  xsp := round((xc0 - Hmin) * ax) + xpl - 8;
  if a1 > 0 then fys := 0;
  ysp := round((Vmax - yc0) * ay) + ypl - fys * 2;
  Image1.Canvas.TextOut(xsp,ysp,'o');                   // 放物線原点の座標o
  Image1.Canvas.Font.Size := 9;
  Image1.Canvas.Font.Color := clBlue;
end;

// Image画像消去
procedure TForm1.ImageClear;
begin
  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;
end;

// 計算開始
procedure TForm1.Button1Click(Sender: TObject);
begin
  Button1.Enabled := False;
  scrollbar1.Enabled := False;
  scrollbar2.Enabled := False;
  ImageClear;                       // Image画像消去
  memo1.Clear;
  Series1.Clear;                    // 放物線
  Series2.Clear;                    // 放物線に対する直線
  Series3.Clear;                    // 接線α
  Series4.Clear;                    // 接線β
  Series5.Clear;                    // Xyスケール設定
  Series6.Clear;                    // 放物線反射面
  if not datainput then begin
    Button1.Enabled := True;
    exit;
  end;
  CF := True;                       // 計算フラグ
  sclpos := scrollbar1.Position / 50;
  if not calc_point_of_intersection then begin
    Memo1.Lines.Add('交点無し');
    CF := False;                    // 交点が無しフラグ 光線計算無し
    x1 := b1 / 2 - abs(1 / a1);     // 交点なし時の作図範囲設定
    x2 := b1 / 2 + abs(1 / a1);
  end;
  ImgF := False;                    // Seriesデーター許可フラグ
  graphDraw;                        // 作図
  Chart1.BackImage.Bitmap := Image1.Picture.Bitmap;
  Button1.Enabled := True;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  bm : Tbitmap;
begin
  SCF := False;                     // 反射光線の長さ調整有無フラグ
  scrollbar1.Enabled := False;      // 反射光線の長さ調整スクロールバー
  scrollbar2.Enabled := False;;     // 入射光角度調整スクロールバー
  bm := Tbitmap.Create;
  bm.Width := Chart1.Width;
  bm.Height := Chart1.Height;
  Image1.Width := Chart1.Width;
  Image1.Height := Chart1.Height;
  Image1.Picture.Bitmap := bm;
  Image1.Canvas.Brush.Color := clWhite;
  Image1.Visible := False;
  bm.Free;
  Label5.Caption :=
          '放物面での反射は一度の反射計算です。' + #13#10 +
          '反射面の角度に近い角度で入光すると複数回反射する事があります。' + #13#10 +
          '複数回の反射は考慮していません。';
  Label6.Caption := '角度は、計算時、計算用の角度に変換されます。';
end;

// 反射光長調整
procedure TForm1.ScrollBar1Scroll(Sender: TObject; ScrollCode: TScrollCode;
  var ScrollPos: Integer);
begin
  ImageClear;                         // Image画像消去
  ImgF := True;                       // Image画像のみ作図フラグ
  sclpos := scrollbar1.Position / 50; // 反射光長さ設定係数
  graphDraw;
  Chart1.BackImage.Bitmap := Image1.Picture.Bitmap;
end;

// 入射光線角度調整
procedure TForm1.ScrollBar2Scroll(Sender: TObject; ScrollCode: TScrollCode;
  var ScrollPos: Integer);
begin
  if not CF then exit;
  Q0 := ScrollPos;
  if Q0 <= 0 then exit;
  if Q0 >= 180 then exit;
  if not SCF then exit;               // 反射光線の長さ調整有無フラグ
  if a1 < 0 then Q0 := - Q0;
  if a1 > 0 then begin
    if Q0 >   90 then Q0 := Q0 - 180;
  end
  else begin
    if Q0 <= -90 then Q0 := 180 + Q0;
  end;
  ImgF := True;                       // Image画像のみ作図フラグ
  ImageClear;                         // Image画像消去
  graphDraw;
  Chart1.BackImage.Bitmap := Image1.Picture.Bitmap;
end;

procedure TForm1.LabeledEdit1Change(Sender: TObject);
begin
  ScrollBar2.Enabled := False;
  SCF := False;                       // 反射光線の長さ調整有無フラグ
end;

procedure TForm1.LabeledEdit4Change(Sender: TObject);
begin
  ScrollBar2.Enabled := False;
  SCF := False;                       // 反射光線の長さ調整有無フラグ
end;

end.

    download parabore_focus.zip

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

      最初に戻る