以下はだいぶ前に拾った弾性衝突の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.
>うまくいきません。どこが悪いのでしょうか?
>単にコンパイルエラーが出ないようにするのがやっとでした。
具体的には、どのようにうまくいかないのですか?
# 実行時にエラーが表示するのですか?
# それとも期待する結果と実際に出る結果が違うのですか?
試していませんが、
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));
にしてみるとか。
上のコードCanvasが余計でしたね。
># それとも期待する結果と実際に出る結果が違うのですか?
その通りです。バネや物体がまったくまともに描かれないのです。
http://toku.xdisc.net/cgi/up/qqq/nm17223.zip
ここにソースと元のアプリを置きましたので、確認していただけたらありがたいです
> 上のコードCanvasが余計でしたね。
で、Canvasを取り除いた場合でも、
まともに描かれないのですか?
あと、
試していませんが、
Points: array[0..12] of TPoint;
を
Points: array[0..11] of TPoint;
にしてみるとか。
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点を修正したらそれっぽい動きになりました。
あと細かいことになりそうですが、DelphiのRound関数は通常の四捨五入とは違うので、こういったもに使うのは適切ではないのかも知れません。
たとえば、
Round(0.5)は0になります。
Round(1.5)は2になります。
Round(2.5)は2になります。
Round(3.5)は4になります。
詳しくはヘルプで確認してみてください。
> ・ボタンがBevel1の後ろになっていて触れないので、Bevel1を右クリックして[コントロール]→[背面に移動]
ああ! そうですね。形だけまねたものですから。お粗末さまでした(^O^);
TimerとかPolyline の使い方、よく知りませんでした。Floor もそうですね。
どうしてこれで動くのかは物理のお勉強ですね。頑張って挑戦してみます。
ありがとうございました。
ツイート | ![]() |