斜方投射  速度の二乗に比例する空気抵抗がある場合

 斜方投射や、自由落下では、速度の二乗に比例して、空気抵抗(慣性抵抗)が発生するのが一般的です。
自由落下では、落下開始のほんの一瞬だけ、速度に比例した抵抗になるようですが、計算上無視できる時間のようです。
斜方投射では、初速度を持っているので、速度の二乗に比例した抵抗しか発生しないのが殆どとなります。
初速度ゼロの場合、自由落下として計算が可能です。
計算式の成り立ちについては、インターネットで"斜方投射"、"慣性抵抗"で検索してください。
単に斜方投射で検索すると、速度に空気抵抗が比例する例が表示されます。

 斜方投射の計算は、初速度v0に対して、水平方向の速度 v0x = v0cosθ と、垂直方向の速度v0y = v0sinθ に分けて計算します。

 

 上方投射の垂直方向は、上昇中と、下降中の二つに分けて計算します。
その為に、最初に最高点に達するまでの時間を先に計算して、最高点に達するまでの時間より前か、後かで計算を分けます。

 

 斜方投射計算は、あくまでも、斜め上に投射した場合で、下方への投射は考慮されていません。
投射が水平より、下方になった場合の垂直方向の計算で、下方への初速度がt∞の速度を超えている場合、自由落下 2 初速度がある場合の計算を使用します。
t∞の速度を超えない場合は、自由落下で初速度に達する時間t0、を計算し、投射からの時間tをt0に加算して計算します。
その後、初速度迄の値を減算すれば、目的を達することができます。
投射が下方になった場合でも、水平方向の計算は変わりません。
これで、投射角度 90°~-90°までの範囲で計算ができる様になります。

 左図は、斜方投射のプログラム実行画面です。
k:空気抵抗係数を放物線の計算に使用しますが、空気抵抗係数は、単独でも空気抗力係数Cd、空気密度ρ、有効面積Sから計算できるようになっています。
又、k:空気抵抗係数欄に値を入力すれば、入力した値が、放物線計算に使用されます。



プログラム

// 空気抵抗が速度の二乗に比例する場合
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,
  Vcl.Buttons;

type
  TForm1 = class(TForm)
    Chart1: TChart;
    Series1: TLineSeries;
    GroupBox1: TGroupBox;
    rhoEdit: TLabeledEdit;
    sumEdit: TLabeledEdit;
    KcalcBtn: TBitBtn;
    kEdit: TLabeledEdit;
    CdEdit: TLabeledEdit;
    Memo1: TMemo;
    GroupBox2: TGroupBox;
    mEdit: TLabeledEdit;
    v0Edit: TLabeledEdit;
    thEdit: TLabeledEdit;
    gEdit: TLabeledEdit;
    calcBtn: TBitBtn;
    Series2: TLineSeries;
    Series3: TLineSeries;
    tmEdit: TLabeledEdit;
    Label1: TLabel;
    Label2: TLabel;
    tyRadioButton: TRadioButton;
    XYRadioButton: TRadioButton;
    TXRadioButton: TRadioButton;
    tvRadioButton: TRadioButton;
    Series4: TLineSeries;
    RedCheckBox: TCheckBox;
    Series5: TPointSeries;
    VCheckBox: TCheckBox;
    mxyRadioButton: TRadioButton;
    Series6: TLineSeries;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    procedure calcBtnClick(Sender: TObject);
    procedure KcalcBtnClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses system.Math;

//----------------空気抵抗無しx方向位置計算-------------------------------------
// t    時間
// v0x  初速度
function xs(t, v0x: double): double;
begin
  result := t * v0x;
end;

//----------------空気抵抗無しy方向位置計算-------------------------------------
// t    時間
// v0y  初速度
// g    重力加速度
// vf   速度計算フラグ  true 速度 false 距離
function ys(t, v0y, g: double; vf: boolean): double;
begin
  if vf then                // 速度計算
    result := v0y - g * t
  else                      // 距離計算
    result := - g * t * t / 2 + v0y * t;
end;

//----------------空気抵抗有りx方向位置速度計算-------------------------------------
// t    時間
// k    空気抵抗係数
// m    質量
// v0x  初速度
// vf   速度計算フラグ  true 速度 false 距離
function xl(t, k, m, v0x: double; vf: boolean): double;
begin
  if m = 0 then begin
    result := 0;
    exit;
  end;
  if vf then begin          // 速度計算
    if v0x = 0 then result := 0
               else result := 1 / (k / m * t + 1 / v0x);
  end else                  // 距離計算
    result := m / k * ln(k * v0x / m * t + 1);
end;

//----------------空気抵抗有りy方向位置速度計算-------------------------------------
// t    時間
// k    空気抵抗係数
// m    質量
// v0y  初速度
// th   最高度時間
// g    重力加速度
// vf   速度計算フラグ  true 速度 false 距離
function yl(t, k, m, v0y, th, g: double; vf: boolean): double;
const
  KCC = 1.4142;
var
  cc  : double;
  tv  : double;   // v0yになる迄の仮の時間
  yv  : double;   // tvの落下距離
  y1v : double;   // 落下距離
  v0  : double;   // 実行速度
  nv0 : double;   // 予実行速度
  dt  : double;   // Δt
  fm  : double;   // 平均速度
  fv  : double;   // 減速後速度
  dm  : double;   // t∞抗力
  dv  : double;   // 速度抗力
  ndv : double;   // 予速度抗力
  mdg : double;   // 減速加速度
  nmdg : double;  // 予減速加速度
  vmax : double;  // t∞速度
  ymax : double;  // 最高点
  i, j : integer;
begin
  if vf then begin          // 速度計算
    if v0y >= 0 then begin      // 投射角度 >= 0
      if m = 0 then begin
        result := 0;
        exit;
      end;
      if t < th then begin      // 速度上昇計算
        result := sqrt(m * g / k) * tan(sqrt(k * g / m) * (th -t));
      end
      else begin                // 速度下降計算
         result := - sqrt(m * g / k) * tanh(sqrt(k * g / m) * (t - th));
      end;
    end
    else begin                  // 投射角度 < 0
      if m = 0 then begin
        result := 0;
        exit;
      end;
      cc := -v0y / sqrt(m * g / k);
      if cc <= 1 then begin          // 下降初速度が最大降下速度より遅い場合
        tv := arctanh(cc) / sqrt(k * g / m);  // 初速度に達する時間
        // ゼロからの時間tv + 指定時間tの速度
        result := - sqrt(m * g / k) * tanh(sqrt( k * g / m) * (tv + t));
      end
      else begin      // 下降初速度が最大降下速度より早い場合 近似計算
        if (t = 0) or (m = 0) then begin          // t = 0 なら
          result := v0y;             // 初速度
          exit;
        end;
        vmax := sqrt(m * g / k);           // t∞速度
        dm := vmax * vmax * k;             // t∞抗力
        j := 1000;                          // 分割数
        dt := t / j;                       // Δt
        v0 := -v0y;                        // 速度
        cc := power(maxdouble, 1 / 2) * m; // 演算オーバーフローチェック値
        for i := 1 to j do begin           // 分割積分
          dv := v0 * v0 * k;               // v0抗力
          mdg := (dv - dm) / m;            // 加速度
          nv0 := v0 - mdg * dt;            // Δt減速後速度
          ndv := nv0 * nv0 * k;            // 予減速後抗力
          nmdg := (ndv - dm) / m;          // 予減速後加速度
          mdg := (mdg + nmdg) / 2;         // 平均加速度
          v0 := v0 - mdg * dt;             // 新速度
          if (v0 > cc) or (v0 < 0) then break;      // オーバーフローの場合ブレーク
        end;
        if (v0 > cc) or (v0 < 0) then begin
          result := maxdouble;             // オーバーフロー数セット
          exit;
        end;
        result := -v0;        // 戻り値 速度
      end;
    end;
  end
  else begin                  // 距離計算
    if v0y >= 0 then begin      // 投射角度 >= 0
      if (t = 0) or (m = 0) then begin    // t = 0 なら
        result := 0;                      // 速度0
        exit;
      end;
      ymax := ln(1 + k / (m * g) * v0y * v0y);     // 最高点
      if t < th then begin      // 上昇計算
        cc := cos(sqrt(k * g / m) * (th - t));
        result := m / k * ln(cc) + m / (2 * k) * ymax;
      end
      else begin                // 下降計算
        cc := sqrt(k * g / m) * (t - th);
        if cc > 710.4 then begin     // double の計算で710.4より大きいとcoshがオーバーフロー
          result := maxdouble;
          exit;
        end;
        cc := cosh(sqrt(k * g / m) * (t - th));
        result := - m / k * ln(cc) + m / (2 * k) * ymax;
      end;
    end
    else begin                  // 投射角度 < 0
      cc := -v0y - sqrt(m * g / k);
      if cc < 0 then begin          // 下降初速度が最大降下速度より遅い場合
        cc := -v0y / sqrt(m * g / k);
        tv := arctanh(cc) / sqrt(k * g / m);  // 初速度に達する時間
        // ゼロからの初速度迄の距離
        cc := sqrt(k * g / m) * tv;
        if cc > 710.4 then begin     // double の計算で710.4より大きいとcoshがオーバーフロー
          result := maxdouble;
          exit;
        end;
        cc := cosh(cc);
        // 初速度に達するまでの距離 yv
        yv := - m / k * ln(cc);
        // 移動距離 = 速度ゼロからの距離  - 初速度に達するまでの距離
        cc := sqrt(k * g / m) * (tv + t);
        if cc > 710.4 then begin     // double の計算で710.4より大きいとcoshがオーバーフロー
          result := maxdouble;
          exit;
        end;
        cc := cosh(cc);
        // 移動距離
        result := - m / k * ln(cc)  - yv;
      end
      else begin      // 下降初速度が最大降下速度より早い場合 近似計算
        if (t = 0) or (m = 0) then begin    // t = 0 なら
          result := 0;                      // 距離 = 0
          exit;
        end;
        y1v := 0;                           // 距離0初期化
        vmax := sqrt(m * g / k);            // t∞速度
        dm := vmax * vmax * k;              // t∞抗力
        j := 1000;                           // 分割数
        dt := t / j;                        // Δt
        v0 := - v0y;                        // 速度
        cc := power(maxdouble, 1 / 2) * m;  // 演算オーバーフローチェック数
        for i := 1 to j do begin            // 分割積分
          dv := v0 * v0 * k;                // v0抗力
          mdg := (dv - dm) / m;             // 加速度
          fv := v0 - mdg * dt;              // 予Δt減速後速度
          ndv := fv * fv * k;               // 予fv抗力
          nmdg := (ndv - dm) / m;           // 予加速度
          mdg := (mdg + nmdg) / 2;          // 加速度平均
          fm := v0 - mdg * dt;              // 速度
          y1v := y1v + (fm + v0) / 2 * dt;  // Δt距離加算
          v0 := fm;                         // 新速度
          if (v0 > cc) or (v0 < 0) then break;      // オーバーフローの場合ブレーク
        end;
        if (v0 > cc) or (v0 < 0) then begin
          result := maxdouble;              // オーバーフローフラグ数セット
          exit;
        end;
        result := - y1v;                    // 戻り値 距離
      end;
    end;
  end;
end;

//-------------------計算実行---------------------------------------------------
procedure TForm1.calcBtnClick(Sender: TObject);
label
  TV, MXY;
var
  k  : double;         // 空気抵抗係数
  m  : double;         // 質量
  v0 : double;         // 初速度
  v0x, v0y : double;   // x,y方向初期速度
  thd, thr : double;   // 角度
  th : double;         // 最高度時間
  ymax : double;       // 最高点
  ymin : double;       // 最下点 y <= 0
  g    : double;       // 重力加速度
  t, dt : double;      // 時間
  ty    : double;      // 落下時間
  x, y, xmax : double;   // 位置
  L0   : double;       // 水平距離
  ch, i, imax : integer;
  tm   : double;       // グラフ表示時間
  cc   : double;
  MVF  : boolean;      // 下向き投射時最大策度より投射速度が速いフラグ
  mv   : double;       // 下向き投射時最大策度より投射速度が速い質量
begin
  Memo1.Clear;
  val(kEdit.Text, k, ch);
  if ch <> 0 then begin
    Memo1.Text := 'k:空気抵抗係数に間違いがあります。';
    exit;
  end;
  if k <= 0 then begin
    Memo1.Text := 'k:空気抵抗係数の値は0(ゼロ)より大きくしてください。';
    exit;
  end;
  val(mEdit.Text, m, ch);
  if ch <> 0 then begin
    Memo1.Text := 'm:質量に間違いがあります。';
    exit;
  end;
  if m <= 0 then begin
    Memo1.Text := 'm:質量の値は0(ゼロ)より大きくしてください。';
    exit;
  end;
  val(V0Edit.Text, v0, ch);
  if ch <> 0 then begin
    Memo1.Text := 'v0:初速度に間違いがあります。';
    exit;
  end;
  if v0 < 0 then begin
    Memo1.Text := 'v0:初速度の値は0(ゼロ)より大きくしてください。';
    exit;
  end;
  val(thEdit.Text, thd, ch);
  if ch <> 0 then begin
    Memo1.Text := 'th:投射角度に間違いがあります。';
    exit;
  end;
  if thd < -90 then begin
    Memo1.Text := 'th:投射角度の値は-90°より大きくしてください。';
    exit;
  end;
  if thd > 90 then begin
    Memo1.Text := 'th:投射角度の値は90°より小さくしてください。';
    exit;
  end;
  val(gEdit.Text, g, ch);
  if ch <> 0 then begin
    Memo1.Text := 'g:加速度に間違いがあります。';
    exit;
  end;
  if g <= 0 then begin
    Memo1.Text := 'g:加速度の値は0(ゼロ)より大きくしてください。';
    exit;
  end;
  val(tmEdit.Text, tm, ch);
  if ch <> 0 then begin
    Memo1.Text := 'tm:計算時間に間違いがあります。';
    exit;
  end;
  if tm <= 0 then begin
    Memo1.Text := 'tm:計算時間の値は0(ゼロ)より大きくしてください。';
    exit;
  end;

  Series1.Clear;
  Series2.Clear;
  Series3.Clear;
  Series4.Clear;
  Series5.Clear;
  Series6.Clear;
  Label1.Visible := false;
  Label2.Visible := false;
  Label3.Visible := false;
  Label4.Visible := false;
  Label5.Visible := false;

  thr := thd / 180 * pi;  // deg -> rad
  v0x := v0 * cos(thr);   // 水平速度
  v0y := v0 * sin(thr);   // 垂直速度
  if thd = -90 then begin
    v0x := 0;
    v0y := -v0;
  end;
  if thd = 90 then begin
    v0x := 0;
    v0y := v0;
  end;
  if thd < 0 then begin              // 下向き投射の時
    cc := -v0y / sqrt(m * g / k);
    if cc >= 1 then begin
      memo1.Lines.Append('下降の初速度が速すぎるので近似計算です。');
    end;
  end;
  th := 0;
  ty := 0;
  memo1.Lines.Append('速度の2乗に抵抗が比例する場合。');
  Chart1.BottomAxis.Title.Text := 'X';
  Chart1.LeftAxis.Title.Text := 'Y';


  if tvradiobutton.Checked = true then  goto TV;
  if mxyradiobutton.Checked = true then goto MXY;
  if redcheckbox.Checked = true then begin
    label2.Caption := '桃: 空気抵抗無';
    label2.Visible := true;
  end;
  label1.Caption := '青: 空気抵抗有';
  label1.Visible := true;
  if thd > 0 then begin
    th := sqrt(m / (k * g)) * arctan(sqrt(k / (m * g)) * v0y);    // 最高点到達時間
    ymax := m / (2 * k) * ln(1 + k / (m * g) * v0y * v0y);        // 最高点
    ty := sqrt(m / (g * k)) * arccosh(exp(ymax * k / m));         // y=0迄の落下時間 ymax >=0
    L0 := xl(th + ty, k, m, v0x, false);                                 // y=0水平距離
    memo1.Lines.Append(' 最高点     '+ floatTostr(ymax) + ' m');
    memo1.Lines.Append(' 最高点時間 '+ floatTostr(th) + ' sec');
    memo1.Lines.Append(' y=0水平距離   '+ floatTostr(l0) + ' m');
    memo1.Lines.Append(' y=0水平時間   '+ floatTostr(th + ty) + ' sec');
    memo1.Lines.Append(floatTostr(tm) + 'sec の距離');
    x := xl(tm, k, m, v0x, false);
    memo1.Lines.Append(' 水平距離   '+ floatTostr(x) + ' m');
    y := yl(tm, k, m, v0y, th, g, false);
    if y = maxdouble then begin
      memo1.Clear;
      memo1.Lines.Append('1 表示希望時間が長すぎます。');
      exit;
    end;
    memo1.Lines.Append(' 垂直距離   '+ floatTostr(y) + ' m');
  end
  else begin
    x := xl(tm, k, m, v0x, false);
    memo1.Lines.Append(' 水平距離   '+ floatTostr(x) + ' m');
    y := yl(tm, k, m, v0y, th, g, false);
    if y = maxdouble then begin
      memo1.Clear;
      memo1.Lines.Append('2 表示希望時間が長すぎます。');
      exit;
    end;
    memo1.Lines.Append(' 垂直距離   '+ floatTostr(y) + ' m');
    if thd = 0 then  begin                                        // 投射角 = 0
      ty := sqrt(m / (g * k)) * arccosh(exp(abs(y) * k / m));
      memo1.Lines.Append(' 落下時間   '+ floatTostr(ty) + ' sec');
    end
    else begin
      memo1.Lines.Append(' 落下時間   '+ floatTostr(tm) + ' sec');
    end;
  end;
  // tm グラフ用表示用計算時間長さ選択
  if thd = 0 then begin
    if (th + ty) > tm then  tm := round((th + ty) * 1.1 * 1000) / 1000;
  end
  else begin
    if thd > 0 then begin
      if ((th + ty) * 1.1) > tm then  tm := round((th + ty) * 1.1 * 1000) / 1000;
    end
    else
      tm := round(tm * 1.1 * 1000) / 1000;
  end;
  dt := round(tm * 1000) / 1000000;
  imax := round(tm / dt);
  t := 0;
  xmax := 0;
  ymax := 0;
  ymin := 0;
  Chart1.BottomAxis.Title.Text := 'm';
  Chart1.LeftAxis.Title.Text := 'm';
  // グラフ表示
  if XYradiobutton.Checked = true then begin    // XY距離
    memo1.Lines.Append('グラフ時間計算 tm   ' + floatTostr(tm) + ' sec');
    for i := 0 to imax do begin
      t := i * dt;                                // 時間
      if redcheckbox.Checked = true then begin    // 空気抵抗なしチェックあり
        x := xs(t, v0x);                          // 空気抵抗なし x
        y := ys(t, v0y, g, false);                // 空気抵抗なし y
        if x > xmax then xmax := x;
        if y > ymax then ymax := y;
        if ymin > y then ymin := y;
        Series2.AddXY(x, y);                      // 空気抵抗なしグラフデーター追加
      end;
      x := xl(t, k, m, v0x, false);                    // 空気抵抗有り x
      y := yl(t, k, m, v0y, th, g, false);             // 空気抵抗有り y
      if y = maxdouble then begin
        memo1.Clear;
        memo1.Lines.Append('3 表示希望時間が長すぎます。');
        exit;
      end;
      if x > xmax then xmax := x;
      if y > ymax then ymax := y;
      if ymin > y then ymin := y;
      Series1.AddXY(x, y);                      // xyグラフデーター追加
    end;
    // グラフ表示サイズ設定
    if xmax > (ymax - ymin) then ymax := xmax + ymin;
    if (ymax - ymin) > xmax then xmax := (ymax - ymin);
    Series3.AddXY(-xmax / 100,0);
    Series3.AddXY(xmax * 1.1, 0);
    if thd < 0 then Series5.AddXY(xmax * 1.1, ymin * 1.1)
               else Series5.AddXY(xmax * 1.1, ymax * 1.05);
  end;
  dt := tm / 1000;
  imax := round(tm / dt);
  if tyradiobutton.Checked = true then begin    // 垂直高さ
    memo1.text := memo1.text + 'グラフ時間軸 tm   ' + floatTostr(tm) + ' sec';
    for i := 0 to imax do begin
      t := i * dt;
      if redcheckbox.Checked = true then begin
        y := ys(t, v0y, g, false);              // 空気抵抗なし y
        if y > ymax then ymax := y;
        Series2.AddXY(t, y);                    // グラフデーター追加
      end;
      y := yl(t, k, m, v0y, th, g, false);      // 空気抵抗有り y
      if y = maxdouble then begin
        memo1.Clear;
        memo1.Lines.Append('4 表示希望時間が長すぎます。');
        exit;
      end;
      if y > ymax then ymax := y;
      Series1.AddXY(t, y);                      // tyグラフデーター追加
    end;
    Chart1.BottomAxis.Title.Text := 'sec';
    Chart1.LeftAxis.Title.Text := 'm';
    // グラフ表示サイズ設定
    Series3.AddXY(-t / 100,0);
    Series3.AddXY(t * 1.1, 0);
    Series5.AddXY(t * 1.1, ymax * 1.1);
  end;
  if txradiobutton.Checked = true then begin    // 水平距離
    memo1.text := memo1.text + 'グラフ時間軸 tm   ' + floatTostr(tm) + ' sec';
    for i := 0 to imax do begin
      t := i * dt;
      if redcheckbox.Checked = true then begin    // 空気抵抗なしチェックあり
        if thd = -90 then x := 0                  // 真下 距離x=0
                    else x := xs(t, v0x);         // 空気抵抗なし x
        if x > xmax then xmax := x;
        Series2.AddXY(t, x);                      // txグラフデーター追加
      end;
      if thd = -90 then x := 0                         // 真下なので距離ゼロ
                   else x := xl(t, k, m, v0x, false);  // 空気抵抗有り 距離x
      if x > xmax then xmax := x;
      Series1.AddXY(t, x);                      // txグラフデーター追加
    end;
    Chart1.BottomAxis.Title.Text := 'sec';
    Chart1.LeftAxis.Title.Text := 'm';
    // グラフ表示サイズ設定
    Series3.AddXY(-t / 100,0);
    Series3.AddXY(t * 1.1, 0);
    Series5.AddXY(t * 1.1, xmax * 1.1);
  end;
  exit;
TV:     // t秒後の速度計算
  memo1.Lines.Append(floatTostr(tm) + 'sec 後の速度');
  memo1.Lines.Append('速度の2乗に抵抗が比例する場合。');
  if thd > 0 then begin
    th := sqrt(m / (k * g)) * arctan(sqrt(k / (m * g)) * v0y);    // 最高点到達時間
    x := xl(tm, k, m, v0x, true);
    memo1.Lines.Append(' 水平速度   '+ floatTostr(x) + ' m / s');
    y := yl(tm, k, m, v0y, th, g, true);
    memo1.Lines.Append(' 垂直速度   '+ floatTostr(y) + ' m / s');
  end
  else begin
    x := xl(tm, k, m, v0x, true);
    memo1.Lines.Append(' 水平速度   '+ floatTostr(x) + ' m / s');
    y := yl(tm, k, m, v0y, th, g, true);
    memo1.Lines.Append(' 垂直速度   '+ floatTostr(y) + ' m');
  end;
  memo1.Lines.Append('空気抵抗がない場合。');
  x := v0x;                                         // 空気抵抗なし x
  y := ys(tm, v0y, g, true);                        // 空気抵抗なし y
  memo1.Lines.Append(' 水平速度   '+ floatTostr(x) + ' m / s');
  memo1.Lines.Append(' 垂直速度   '+ floatTostr(y) + ' m / s');
  // tm グラフ用表示用計算時間長さ選択
  tm := round(tm * 1.1 * 1000) / 1000;
  dt := round(tm * 1000) / 1000000;
  memo1.text := memo1.text + 'グラフ時間軸 tm   ' + floatTostr(tm) + ' sec';
  imax := round(tm / dt);
  ymax := 0;
  ymin := 0;
  // グラフ表示
  label1.Caption := '空気抵抗:有   垂直:青 ';
  label2.Caption := '空気抵抗:無   垂直:桃 ';
  label3.Caption := '水平:緑';
  label4.Caption := '水平:赤';
  label5.Caption := 't∞:茶色';
  if redcheckbox.Checked = true then begin        // 空気抵抗なしチェックあり
    label2.Visible := true;
    label4.Visible := true;
  end;
  label1.Visible := true;
  label3.Visible := true;

  Chart1.BottomAxis.Title.Text := 'sec';
  Chart1.LeftAxis.Title.Text := 'm/s';
  for i := 0 to imax do begin
    t := i * dt;
    if redcheckbox.Checked = true then begin      // 空気抵抗なしチェックあり
      x := v0x;                                     // 空気抵抗なし x
      y := ys(t, v0y, g, true);                     // 空気抵抗なし y
      if x > ymax then ymax := x;
      if y > ymax then ymax := y;
      if ymin > y then ymin := y;
      if ymin > x then ymin := x;
      Series2.AddXY(t, y);                          // グラフデーター追加
      Series4.AddXY(t, x);                          // グラフデーター追加
    end;
    x := xl(t, k, m, v0x, true);                    // 空気抵抗有り x
    y := yl(t, k, m, v0y, th, g, true);             // 空気抵抗有り y
    if x > ymax then ymax := x;
    if y > ymax then ymax := y;
    if ymin > y then ymin := y;
    if ymin > x then ymin := x;
    Series1.AddXY(t, y);                            // グラフデーター追加
    Series3.AddXY(t, x);                            // グラフデーター追加
  end;
  // グラフ表示サイズ設定
  Series5.AddXY(0,  -(ymax - ymin) * 0.1 + ymin);
  Series5.AddXY(0, (ymax - ymin) * 0.1 + ymax);

  exit;
MXY:      // 質量と距離速度の関係
  // tm グラフ用表示用計算時間長さ選択
  Chart1.BottomAxis.Title.Text := 'kg';
  if vcheckbox.Checked = false then               // 速度表示選択なし
    Chart1.LeftAxis.Title.Text := 'm'
  else                                            // 速度表示選択
    Chart1.LeftAxis.Title.Text := 'm/s';
  label1.Caption := '空気抵抗:有   X:青';
  label3.Caption := 'Y:緑';
  label1.Visible := true;
  label3.Visible := true;
  memo1.Lines.Append('計算時間 ' + floatTostr(tm) + ' sec');
  t := tm;
  tm := round(m * 1.1 * 1000) / 1000;
  dt := round(tm * 1000) / 1000000;
  imax := round(tm / dt);
  if vcheckbox.Checked = true then begin          // 速度表示選択
    memo1.Lines.Append('水色線 無限大時間速度');
    label1.Caption := '空気抵抗:有   X:青 ';
    label3.Caption := 'Y:緑 ';
    label5.Visible := true;
    label5.Caption := 't∞ 速度:茶';
  end;
  memo1.text := memo1.text + 'グラフ X軸 m   ' + floatTostr(tm) + ' kg';
  ymax := 0;
  ymin := 0;
  MVF := false;
  mv := 0;
  for i := 0 to imax do begin
    m := dt * i;                       // 質量
    if thd < 0 then begin              // 下向き投射の時
      if m > 0 then begin
        cc := -v0y - sqrt(m * g / k);
        if cc > 0 then begin            // 投射速度が最大速度より早いとき
          MVF := true;                  // フラグセット
          mv := m;
        end;
      end;
    end;
    if thd > 0 then begin               // 上向き投射
      th := 0;
      if m > 0 then
        th := sqrt(m / (k * g)) * arctan(sqrt(k / (m * g)) * v0y);    // 最高点到達時間
    end;
    if vcheckbox.Checked = true then begin        // 速度表示選択
      x := xl(t, k, m, v0x, true);                    // 空気抵抗有り速度 x
      y := yl(t, k, m, v0y, th, g, true);             // 空気抵抗有り速度 y
      ty := -sqrt(m * g / k);
      if ymin > ty then ymin := ty;
      Series6.AddXY(m, ty);                           // m,t∞速度グラフ
    end
    else begin
      x := xl(t, k, m, v0x, false);                   // 空気抵抗有距離り x
      y := yl(t, k, m, v0y, th, g, false);            // 空気抵抗有距離り y
    end;
    if y = maxdouble then begin                       // オーバーフローだったら
      memo1.Clear;
      memo1.Lines.Append('3 表示希望時間が長すぎます。');
      memo1.Lines.Append('又は初期速度が速すぎます。');
      exit;
    end;
    if x > ymax then ymax := x;
    if y > ymax then ymax := y;
    if ymin > y then ymin := y;
    if ymin > x then ymin := x;
    Series1.AddXY(m, x);                      // m,x グラフデーター追加
    Series3.AddXY(m, y);                      // m,y グラフデーター追加
  end;
  if MVF then begin                     // 投射速度が最大速度より早いとき
    memo1.Lines.Append('質量 m=' + floattostr(mv) + ' kg 迄');
    memo1.text := memo1.text + '下降の初速度が速すぎるので近似計算の範囲です。';
  end;
  // グラフ表示サイズ設定
  Series5.AddXY(0,  -(ymax - ymin) * 0.1 + ymin);
  Series5.AddXY(0, (ymax - ymin) * 0.1 + ymax);
end;

//-----------------空気抗力係数から空気抵抗計算kg/m計算-------------------------
procedure TForm1.KcalcBtnClick(Sender: TObject);
var
  k   : double;   // 空気抵抗係数  kg/m
  Cd  : double;   // 空気抗力係数
  r   : double;   // 空気密度   kg/m^3
  S   : double;   // 代表面積   m^2
  ch  : integer;
begin
  memo1.Clear;
  val(CdEdit.Text, Cd, ch);
  if ch <> 0 then begin
    Memo1.Text := 'Cd:空気抗力係数に間違いがあります。';
    exit;
  end;
  val(rhoEdit.Text, r, ch);
  if ch <> 0 then begin
    Memo1.Text := 'ρ:空気密度に間違いがあります。';
    exit;
  end;
  val(sumEdit.Text, S, ch);
  if ch <> 0 then begin
    Memo1.Text := 'S:代表面積に間違いがあります。';
    exit;
  end;
  k := Cd * r * s / 2;
  kEdit.Text := floatTostr(k);
end;

//------------------------------------------------------------------------------
procedure TForm1.FormCreate(Sender: TObject);
begin
  memo1.Text := '放物線の計算';
end;

end.

  download Projectile_motion.zip

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