C++Builder から Delphi への移植

解決


satomi  2009-09-20 01:36:09  No: 35723

以下はだいぶ前に拾った弾性衝突のC++のプログラムなんですがDelphiへの移植がうまくいきません。どこが悪いのでしょうか?  当方、C++ はさっぱりで、Delphiも詳しくないので単にコンパイルエラーが出ないようにするのがやっとでした。長文ですみません。

  double
    ox = 100, oy = 100,
    m1, m2,       // mass
    v1 = 0, v2,   // velocity
    dv1, dv2,     // delta v
    Ls = 0.1,     // length of spring
    x1 = Ls, x2 = -0.1,  // 位置
    dx1, dx2,     // delta x
    a1, a2,       // acceleration
    k,            // ばね定数
    dt = 0.01,    // 時間増分
    t = 0,
    F = 0;        // 力

//DrawSpring
void TMain::DrawSpring(double l)
{
  int i;
  TPoint points[12];
  points[0].x = (x1 - l) * 1000 + ox;
  points[0].y = oy - 20;
  for (i=1; i<= 10; i++)
  {
    points[i].x = points[0].x + l * 1000.0 / 10.0 * (i - 0.5);
    points[i].y = oy - 20 + 20 * (i - 2 * floor(i / 2)) - 10;
  }
  points[11].x = x1 * 1000 + ox;
  points[11].y = oy - 20;
  PaintBox->Canvas->Polyline(points,11);
}

//Button
void __fastcall TMain::Button1Click(TObject *Sender)
{
  if (t <= 0)
    Button3Click(Sender);
  Timer1->Enabled = true;
}

void __fastcall TMain::Button2Click(TObject *Sender)
{
  Timer1->Enabled = false;
}

void __fastcall TMain::Button3Click(TObject *Sender)
{
  Timer1->Enabled = false;
  m1 = StrToFloat(Edit1->Text);
  m2 = StrToFloat(Edit2->Text);
  v2 = StrToFloat(Edit3->Text);
  k  = StrToFloat(Edit4->Text);
  v1 = 0;
  x1 = Ls;
  x2 = -0.1;
  t = 0;
}

//Form
void __fastcall TMain::FormCreate(TObject *Sender)
{
  Left = 0;
  Top = 0;  
}

//Timer
void __fastcall TMain::Timer1Timer(TObject *Sender)
{
  PaintBox->Invalidate();
  Application->ProcessMessages();
  if (x1 - x2 < Ls)
  {
    a1 =   F / m1;
    dv1 = a1 * dt;
    v1 = v1 + dv1;
    a2 = - F / m2;
    dv2 = a2 * dt;
    v2 = v2 + dv2;
    DrawSpring(x1 - x2);
  }
  else
    DrawSpring(Ls);
  PaintBox->Canvas->Rectangle(x1 * 1000 + ox, oy - 40, x1 * 1000 + ox + m1 * 300, oy);
  PaintBox->Canvas->Rectangle(x2 * 1000 + ox, oy - 40, x2 * 1000 + ox - m2 * 300, oy);
  PaintBox->Canvas->MoveTo(0, oy);
  PaintBox->Canvas->LineTo(Width, oy);
  dx1 = v1 * dt;
  x1 = x1 + dx1;
  dx2 = v2 * dt;
  x2 = x2 + dx2;
  t = t + dt;
  F = k * (Ls - (x1 - x2));
}
--------------以下変更分-------------------
var
  ox , oy,      // 初期化はFormCreatでしました
  m1, m2,       // mass  質量
  v1, v2,       // velocity  速度
  dv1, dv2,     // delta v
  Ls,           // length of spring
  x1, x2,       // 位置
  dx1, dx2,     // delta x
  a1, a2,       // acceleration  加速度
  k,            // ばね定数
  dt,           // 時間増分
  t,
  F: Extended;  // 力

//DrawSpring
procedure TMomentumFrm.DrawSpring(L: Extended);
var
  i: Integer;
  Points: array[0..12] of TPoint;
begin
  Points[0].x := Round( (x1-L)*1000+ox );
  Points[0].y := Round(oy-20) ;
  for i := 1 to 10 do
  begin
    Points[i].x := Points[0].x+Round( L*1000.0/10.0*(i-0.5) );
    Points[i].y := Round( oy-20+20*(i-2*Round(i/2))-10 );
  end;
  Points[11].x := Round( x1*1000+ox );
  Points[11].y := Round(oy-20);
  PaintBox1.Canvas.Polyline(Points);
end;

//趣味で(笑) SpeedButton に変えています
procedure TMomentumFrm.SpeedButton1Click(Sender: TObject);
begin
  if t <= 0 then
    SpeedButton3Click(Sender);
  Timer1.Enabled := true;
end;

procedure TMomentumFrm.SpeedButton2Click(Sender: TObject);
begin
  Timer1.Enabled := false;
end;

procedure TMomentumFrm.SpeedButton3Click(Sender: TObject);
begin
  Timer1.Enabled := false;
  m1 := StrToFloat(Edit1.Text);
  m2 := StrToFloat(Edit2.Text);
  v2 := StrToFloat(Edit3.Text);
  k  := StrToFloat(Edit4.Text);
  v1 := 0;
  x1 := Ls;
  x2 := -0.1;
  t := 0;
end;

//Form
procedure TMomentumFrm.FormCreate(Sender: TObject);
begin
  Left := 0;
  Top := 0;  
  ox := 100;
  oy := 100;
  v1 := 0;    // velocity
  Ls := 0.1;  // length of spring
  x1 := Ls;   // 位置
  x2 := -0.1;
  dt := 0.01; // 時間増分
  t := 0;
  F := 0;     // 力
end;

//Timer
procedure TMomentumFrm.Timer1Timer(Sender: TObject);
begin
  PaintBox1.Invalidate;
  Application.ProcessMessages;
  if (x1-x2) < Ls then
  begin
    a1  := F/m1;
    dv1 := a1*dt;
    v1  := v1+dv1;
    a2  := -F/m2;
    dv2 := a2*dt;
    v2  := v2+dv2;
    DrawSpring(x1 - x2);
  end else
    DrawSpring(Ls);
  with PaintBox1.Canvas do
  begin
    Rectangle( Round(x1*1000+ox), Round(oy-40),
                                Round(x1*1000+ox+m1*300), Round(oy) );
    Canvas.Rectangle( Round(x2*1000+ox), Round(oy-40),
                                Round(x2*1000+ox-m2*300), Round(oy) );
    Canvas.MoveTo(0, Round(oy));
    Canvas.LineTo(Width, Round(oy));
  end;
  dx1 := v1*dt;
  x1 := x1+dx1;
  dx2 := v2*dt;
  x2 := x2+dx2;
  t := t+dt;
  F := k*(Ls-(x1-x2));
end;

end.


igy  2009-09-20 01:50:22  No: 35724

>うまくいきません。どこが悪いのでしょうか?
>単にコンパイルエラーが出ないようにするのがやっとでした。

具体的には、どのようにうまくいかないのですか?

# 実行時にエラーが表示するのですか?
# それとも期待する結果と実際に出る結果が違うのですか?


igy  2009-09-20 01:58:38  No: 35725

試していませんが、

    Canvas.Rectangle( Round(x2*1000+ox), Round(oy-40),
                                Round(x2*1000+ox-m2*300), Round(oy) );
    Canvas.MoveTo(0, Round(oy));
    Canvas.LineTo(Width, Round(oy));

    Rectangle( Round(x2*1000+ox), Round(oy-40),
                                Round(x2*1000+ox-m2*300), Round(oy) );
    MoveTo(0, Round(oy));
    LineTo(Width, Round(oy));

にしてみるとか。


satomi  2009-09-20 02:59:36  No: 35726

上のコードCanvasが余計でしたね。

># それとも期待する結果と実際に出る結果が違うのですか?
  その通りです。バネや物体がまったくまともに描かれないのです。
http://toku.xdisc.net/cgi/up/qqq/nm17223.zip
  ここにソースと元のアプリを置きましたので、確認していただけたらありがたいです


igy  2009-09-20 05:01:10  No: 35727

> 上のコードCanvasが余計でしたね。

で、Canvasを取り除いた場合でも、
まともに描かれないのですか?


igy  2009-09-20 08:33:51  No: 35728

あと、

試していませんが、
  Points: array[0..12] of TPoint;

  Points: array[0..11] of TPoint;
にしてみるとか。


D  2009-09-20 13:56:41  No: 35729

igyさんの指摘されている
>  Points: array[0..11] of TPoint;
>にしてみるとか。

の他にDrawSpringのループ中のyを求める式に間違いがあるようです。

>  begin
>    Points[i].x := Points[0].x+Round( L*1000.0/10.0*(i-0.5) );
>    Points[i].y := Round( oy-20+20*(i-2*Round(i/2))-10 );
>  end;

このうち
>    Points[i].y := Round( oy-20+20*(i-2*Round(i/2))-10 );

>    Points[i].y := Round( oy-20+20*(i-2*Floor(i/2))-10 );
ですね。

Floorはmathユニットにあります。

implementation
uses
  math;

としてください。
もしmathユニットが使えないのであれば

function Floor(X: Extended): Integer;
begin
  Result := Trunc(X);
  if Frac(X) < 0 then
    Dec(Result);
end;

でいけると思います。

あとは、

・ボタンがBevel1の後ろになっていて触れないので、Bevel1を右クリックして[コントロール]→[背面に移動]
・Timer1のEnabledはFalse
・Timer1のIntervalは60くらいでしょうか。

以上の5点を修正したらそれっぽい動きになりました。


D  2009-09-20 14:31:40  No: 35730

あと細かいことになりそうですが、DelphiのRound関数は通常の四捨五入とは違うので、こういったもに使うのは適切ではないのかも知れません。

たとえば、
Round(0.5)は0になります。
Round(1.5)は2になります。
Round(2.5)は2になります。
Round(3.5)は4になります。

詳しくはヘルプで確認してみてください。


satomi  2009-09-20 21:03:57  No: 35731

> ・ボタンがBevel1の後ろになっていて触れないので、Bevel1を右クリックして[コントロール]→[背面に移動]
  ああ!  そうですね。形だけまねたものですから。お粗末さまでした(^O^);

  TimerとかPolyline の使い方、よく知りませんでした。Floor もそうですね。
  どうしてこれで動くのかは物理のお勉強ですね。頑張って挑戦してみます。
  ありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加