, - .
:
;
( , , );
.
, 3 (TBall), , (TBallPhysicController), , (TBallVisualController). .
3.1 .
3.1
TMainForm TBallVisualController . TBallVisualController TBallPhysicController , , TMainForm TBallPhysicController ( ). TBallPhysicController TBall , , TBallVisualController TMainForm -.
, . 3.2 .
3.2
TMainForm , Restart Pause, . Timer, TTimer , (.. ).
TBallVisualController . .. , , FBackground GenerateBackground. , Restart ResizeBox, . , TBallVisualController.Restart TMainForm.
TBallVisualController , . Iterate Timer TMainForm . CheckContact, CalculateAlpha GenerateSurface , , .
|
|
TBall , , . 1 , GetRect.
unit MainFm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, ActnList, Menus, Ball, XPMan, ComCtrls;
type
TMainForm = class(TForm)
WorkAreaPanel: TPanel;
LabMainMenu: TMainMenu;
FileMenuItem: TMenuItem;
ExitMenuItem: TMenuItem;
ParamMenuItem: TMenuItem;
AboutMenuItem: TMenuItem;
RestartMenuItem: TMenuItem;
ActionList1: TActionList;
ExitAction: TAction;
RestartAction: TAction;
AboutAction: TAction;
ManageGroupBox: TGroupBox;
RestartButton: TButton;
AnimateTimer: TTimer;
PauseButton: TButton;
XPManifest1: TXPManifest;
PauseAction: TAction;
PauseItem: TMenuItem;
SpeedEdit: TEdit;
SpeedUpDown: TUpDown;
RadiusEdit: TEdit;
RadiusUpDown: TUpDown;
AngleLabel: TLabel;
RadiusLabel: TLabel;
HideControlPanelAction: TAction;
HideControlPanelMenuItem: TMenuItem;
ButtonGroupBox: TGroupBox;
BallGroupBox: TGroupBox;
SurfaceGroupBox: TGroupBox;
AutoGenerateCheckBox: TCheckBox;
FirstSinLabel: TLabel;
FirstSinMEdit: TEdit;
FirstSinPiShiftEdit: TEdit;
OperationEdit: TEdit;
SecondSinMEdit: TEdit;
SecondSinLabel: TLabel;
SecondSinPiShiftEdit: TEdit;
Label1: TLabel;
AnglePiLabel: TLabel;
AngleEdit: TEdit;
RandomAngleCheckBox: TCheckBox;
FirstSinDividerEdit: TEdit;
SecondSinDividerEdit: TEdit;
procedure AboutActionExecute(Sender: TObject);
procedure RestartActionExecute(Sender: TObject);
procedure ExitActionExecute(Sender: TObject);
procedure AnimateTimerTimer(Sender: TObject);
procedure PauseButtonClick(Sender: TObject);
procedure FormPaint(Sender: TObject);
procedure PauseActionExecute(Sender: TObject);
procedure SpeedUpDownClick(Sender: TObject; Button: TUDBtnType);
procedure SpeedEditKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure FormCreate(Sender: TObject);
procedure RadiusUpDownClick(Sender: TObject; Button: TUDBtnType);
procedure RadiusEditKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure HideControlPanelActionExecute(Sender: TObject);
procedure WorkAreaPanelResize(Sender: TObject);
procedure AutoGenerateCheckBoxClick(Sender: TObject);
procedure RandomAngleCheckBoxClick(Sender: TObject);
private
FBallVisualController: TBallVisualController;
// .
procedure Restart;
end;
var
MainForm: TMainForm;
implementation
uses Math;
{$R *.dfm}
const
MAX_START_SPEED = 100;
type
EWrongOperation = class(Exception);
procedure TMainForm.AboutActionExecute(Sender: TObject);
const
ABOUT_MESSAGE = ' 2011 .' + sLineBreak +
' . 01-784-1 - ..' + sLineBreak +
'(- )';
begin
ShowMessage(ABOUT_MESSAGE);
end;
procedure TMainForm.RestartActionExecute(Sender: TObject);
begin
Restart;
end;
procedure TMainForm.ExitActionExecute(Sender: TObject);
begin
Close;
end;
procedure TMainForm.AnimateTimerTimer(Sender: TObject);
|
|
begin
AnimateTimer.Enabled:= False;
FBallVisualController.Iterate;
AnimateTimer.Enabled:= True;
end;
procedure TMainForm.PauseButtonClick(Sender: TObject);
begin
AnimateTimer.Enabled:= False;
end;
procedure TMainForm.FormPaint(Sender: TObject);
begin
if Assigned(FBallVisualController) then
FBallVisualController.Draw;
end;
procedure TMainForm.PauseActionExecute(Sender: TObject);
const
PAUSE_CAPTION = '';
PLAY_CAPTION = '';
begin
AnimateTimer.Enabled:= not AnimateTimer.Enabled;
if AnimateTimer.Enabled then
PauseAction.Caption:= PAUSE_CAPTION
else
PauseAction.Caption:= PLAY_CAPTION;
end;
procedure TMainForm.SpeedUpDownClick(Sender: TObject; Button: TUDBtnType);
begin
SpeedEdit.Text:= FloatToStr(SpeedUpDown.Position / 10);
end;
procedure TMainForm.SpeedEditKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
const
HIGH_SPEED_MESSAGE = ' , %d /c';
var
Value: Double;
begin
if Key = VK_RETURN then
try
Value:= StrToFloat(SpeedEdit.Text);
if Value < 0 then
SpeedEdit.Text:= '0'
else
if Value > MAX_START_SPEED then
begin
SpeedEdit.Text:= FloatToStr(MAX_START_SPEED);
ShowMessage(Format(HIGH_SPEED_MESSAGE, [MAX_START_SPEED]));
end;
SpeedUpDown.Position:= Round(StrToFloat(SpeedEdit.Text) * 10);
except
SpeedEdit.Text:= FloatToStr(SpeedUpDown.Position / 10);
end;
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
SpeedUpDown.Max:= MAX_START_SPEED * 10;
WorkAreaPanel.DoubleBuffered:= True;
WorkAreaPanel.ControlStyle:= WorkAreaPanel.ControlStyle + [ csOpaque ];
end;
procedure TMainForm.RadiusUpDownClick(Sender: TObject;
Button: TUDBtnType);
begin
RadiusEdit.Text:= FloatToStr(RadiusUpDown.Position / 10);
end;
procedure TMainForm.Restart;
const
PAUSE_CAPTION = '';
OPERATION_MINUS = '-';
OPERATION_PLUS = '+';
E_CONVERT_ERROR = '''%s'' ';
var
Buffer: String;
WrongFloatValue: String;
begin
try
if not Assigned(FBallVisualController) then
FBallVisualController:= TBallVisualController.Create(WorkAreaPanel);
PauseAction.Enabled:= True;
PauseAction.Caption:= PAUSE_CAPTION;
AnimateTimer.Enabled:= False;
FBallVisualController.BallPhysicController.BallDefaultZeroSpeed:=
SpeedUpDown.Position / 10;
FBallVisualController.BallPhysicController.Ball.Radius:=
RadiusUpDown.Position * 10;
if not AutoGenerateCheckBox.Checked then
with FBallVisualController.BallPhysicController.SurfaceGenerationParams do
begin
FirstSinusoidMultiplier:= StrToFloat(FirstSinMEdit.Text);
FirstSinusoidPIShift:= StrToFloat(FirstSinPiShiftEdit.Text);
FirstSinusoidDivider:= StrToFloat(FirstSinDividerEdit.Text);
SecondSinusoidMultiplier:= StrToFloat(SecondSinMEdit.Text);
SecondSinusoidPIShift:= StrToFloat(SecondSinPiShiftEdit.Text);
SecondSinusoidDivider:= StrToFloat(SecondSinDividerEdit.Text);
if Trim(OperationEdit.Text) = OPERATION_PLUS then
SinOperation:= 1
else
if Trim(OperationEdit.Text) = OPERATION_MINUS then
SinOperation:= -1
else
raise EWrongOperation.Create(' + -');
end;
if not RandomAngleCheckBox.Checked then
FBallVisualController.BallPhysicController.Alpha:= Pi / StrToFloat(AngleEdit.Text);
FBallVisualController.Restart(WorkAreaPanel.ClientWidth, WorkAreaPanel.ClientHeight,
AutoGenerateCheckBox.Checked, RandomAngleCheckBox.Checked);
if AutoGenerateCheckBox.Checked then
with FBallVisualController.BallPhysicController.SurfaceGenerationParams do
begin
FirstSinMEdit.Text:= FloatToStr(FirstSinusoidMultiplier);
FirstSinPiShiftEdit.Text:= FloatToStr(FirstSinusoidPIShift);
FirstSinDividerEdit.Text:= FloatToStr(FirstSinusoidDivider);
SecondSinMEdit.Text:= FloatToStr(SecondSinusoidMultiplier);
SecondSinPiShiftEdit.Text:= FloatToStr(SecondSinusoidPIShift);
|
|
SecondSinDividerEdit.Text:= FloatToStr(SecondSinusoidDivider);
case SinOperation of
-1:
OperationEdit.Text:= OPERATION_MINUS;
1:
OperationEdit.Text:= OPERATION_PLUS;
end;
end;
if RandomAngleCheckBox.Checked then
AngleEdit.Text:= FloatToStr(Pi / FBallVisualController.BallPhysicController.Alpha);
AnimateTimer.Enabled:= True;
except
on E: EConvertError do
begin
Buffer:= Copy(E.Message, 2, Length(E.Message) - 1);
WrongFloatValue:= Copy(Buffer, 1, Pos('''', Buffer) - 1);
ShowMessage(Format(E_CONVERT_ERROR, [WrongFloatValue]));
end;
on E: EWrongOperation do
ShowMessage(E.Message);
end;
end;
procedure TMainForm.RadiusEditKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
var
Value: Double;
begin
if Key = VK_RETURN then
try
Value:= StrToFloat(RadiusEdit.Text);
if Value < 0 then
RadiusEdit.Text:= '0'
else
if Value > 0.5 then
RadiusEdit.Text:= '0.5';
RadiusUpDown.Position:= Round(StrToFloat(RadiusEdit.Text) * 10);
except
RadiusEdit.Text:= FloatToStr(RadiusUpDown.Position / 10);
end;
end;
procedure TMainForm.HideControlPanelActionExecute(Sender: TObject);
const
HIDE_ACTION_CAPTION = ' ';
SHOW_ACTION_CAPTION = ' ';
begin
ManageGroupBox.Visible:= not ManageGroupBox.Visible;
case ManageGroupBox.Visible of
True:
HideControlPanelAction.Caption:= HIDE_ACTION_CAPTION;
False:
HideControlPanelAction.Caption:= SHOW_ACTION_CAPTION;
end;
end;
procedure TMainForm.WorkAreaPanelResize(Sender: TObject);
begin
if Assigned(FBallVisualController) then
FBallVisualController.ResizeBox(WorkAreaPanel.ClientWidth, WorkAreaPanel.ClientHeight);
end;
procedure TMainForm.AutoGenerateCheckBoxClick(Sender: TObject);
begin
FirstSinMEdit.Enabled:= not FirstSinMEdit.Enabled;
FirstSinPiShiftEdit.Enabled:= not FirstSinPiShiftEdit.Enabled;
OperationEdit.Enabled:= not OperationEdit.Enabled;
SecondSinMEdit.Enabled:= not SecondSinMEdit.Enabled;
SecondSinPiShiftEdit.Enabled:= not SecondSinPiShiftEdit.Enabled;
FirstSinLabel.Enabled:= not FirstSinLabel.Enabled;
SecondSinLabel.Enabled:= not SecondSinLabel.Enabled;
FirstSinDividerEdit.Enabled:= not FirstSinDividerEdit.Enabled;
SecondSinDividerEdit.Enabled:= not SecondSinDividerEdit.Enabled;
end;
procedure TMainForm.RandomAngleCheckBoxClick(Sender: TObject);
begin
AnglePiLabel.Enabled:= not AngleLabel.Enabled;
AngleEdit.Enabled:= not AngleEdit.Enabled;
end;
end.
unit Ball;
interface
uses
Windows, Graphics, Controls;
type
// .
TBall = class
private
FRadius: Double;
FX: Double;
FY: Double;
FSpeed: Double;
function GetRect: TRect;
public
// X .
property X: Double read FX write FX;
// Y .
property Y: Double read FY write FY;
// .
property Radius: Double read FRadius write FRadius;
// .
property Speed: Double read FSpeed write FSpeed;
// , .
property Rect: TRect read GetRect;
end;
// .
TSurface = array of TPoint;
// .
TSurfaceGenerationParams = record
// .
FirstSinusoidMultiplier: Double;
// .
SecondSinusoidMultiplier: Double;
// .
FirstSinusoidDivider: Double;
// .
SecondSinusoidDivider: Double;
// .
|
|
FirstSinusoidPIShift: Double;
// .
SecondSinusoidPIShift: Double;
// .
SinOperation: Integer;
// Y .
SurfaceMinY: Integer;
end;
// .
TContactType = (
// .
ctBoxLeft,
// .
ctBoxRight,
// .
ctBoxTop,
// .
ctBoxBottom,
// .
ctSurface);
// .
TBallPhysicController = class
private
// .
FBall: TBall;
// .
FSurface: TSurface;
// .
FBoxWidth: Integer;
// .
FBoxHeight: Integer;
// .
FTimeMoment: Double;
// .
FAlpha: Double;
// X .
FBallPreviousX: Double;
// Y .
FBallPreviousY: Double;
// 0 X .
FBallZeroPointX: Double;
// 0 Y .
FBallZeroPointY: Double;
// 0 .
FBallZeroSpeed: Double;
// 0 .
FBallDefaultZeroSpeed: Double;
// .
FSurfaceGenerationParams: TSurfaceGenerationParams;
// C .
procedure GenerateSurface;
// .
procedure GenerateRandomSurfaceParams;
// Y .
procedure CalculateSurfaceMinY;
// .
procedure CheckContact;
// .
procedure CalculateAlpha(
// .
const AContactType: TContactType;
// .
const ASurfacePointIndex: Integer = 0);
public
property Ball: TBall read FBall;
property Surface: TSurface read FSurface;
property BallDefaultZeroSpeed: Double read FBallDefaultZeroSpeed
write FBallDefaultZeroSpeed;
property SurfaceGenerationParams: TSurfaceGenerationParams
read FSurfaceGenerationParams;
property Alpha: Double read FAlpha write FAlpha;
// .
procedure ResizeBox(
// .
const ABoxWidth: Integer;
// .
const ABoxHeight: Integer);
// .
procedure Restart(
// .
const ABoxWidth: Integer;
// .
const ABoxHeight: Integer;
// .
const AIsAutoGeneratedSurface: Boolean;
// .
const AIsRandomAngle: Boolean);
// .
procedure Iterate;
// .
constructor Create;
// .
destructor Destroy; override;
end;
// .
TBallVisualController = class
private
// .
FBallPhysicController: TBallPhysicController;
// .
FWindow: TWinControl;
// .
FBoxHeight: Integer;
// .
FBoxWidth: Integer;
// .
FBackground: TBitmap;
// Y .
function Invert(APoint: TPoint): TPoint;
// Y .
function InvertRect(ARect: TRect): TRect;
// .
procedure GenerateBackground;
public
// .
procedure Draw;
// .
procedure ResizeBox(
// .
const ABoxWidth: Integer;
// .
const ABoxHeight: Integer);
// .
procedure Restart(
// .
const ABoxWidth: Integer;
// .
const ABoxHeight: Integer;
// .
const AIsAutoGeneratedSurface: Boolean;
// .
const AIsRandomAngle: Boolean);
// .
constructor Create(
// .
const AWindow: TWinControl);
// .
destructor Destroy; override;
// .
procedure Iterate;
// .
property BallPhysicController: TBallPhysicController read
FBallPhysicController;
end;
implementation
uses
Math, VectoryAlgebra, Types, SysUtils, Classes;
{ TBallPhysicController }
procedure TBallPhysicController.CalculateAlpha(
const AContactType: TContactType; const ASurfacePointIndex: Integer);
var
ContactPoint, PreviousPointMove, Perpendicular: TVector2R;
BufferAlpha, Teta, AxisDifferenceAngle: Double;
|
|
begin
// .
case AContactType of
ctBoxLeft:
ContactPoint:= AddVect2R(Vector2R(FBall.X, FBall.Y), Vector2R(- FBall.Radius, 0));
ctBoxRight:
ContactPoint:= AddVect2R(Vector2R(FBall.X, FBall.Y), Vector2R(FBall.Radius, 0));
ctBoxTop:
ContactPoint:= AddVect2R(Vector2R(FBall.X, FBall.Y), Vector2R(0, FBall.Radius));
ctBoxBottom:
ContactPoint:= AddVect2R(Vector2R(FBall.X, FBall.Y), Vector2R(0, - FBall.Radius));
ctSurface:
ContactPoint:= Vector2R(FSurface[ASurfacePointIndex].X, FSurface[ASurfacePointIndex].Y);
end;
PreviousPointMove:= SubVect2R(Vector2R(FBallPreviousX, FBallPreviousY),
Vector2R(FBall.X, FBall.Y));
Perpendicular:= RightPerpendicularVector2R(SubVect2R(Vector2R(FBall.X, FBall.Y), ContactPoint));
BufferAlpha:= AngelFromVectorToVector(Perpendicular, PreviousPointMove);
Teta:= Pi - BufferAlpha;
AxisDifferenceAngle:= AngelFromVectorToVector2Pi(Vector2R(1, 0), Perpendicular);
FAlpha:= Teta + AxisDifferenceAngle;
FBallZeroSpeed:= FBall.Speed;
FTimeMoment:= 0;
FBallZeroPointX:= FBall.X;
FBallZeroPointY:= FBall.Y;
end;
procedure TBallPhysicController.CalculateSurfaceMinY;
var
I, CalculatedValue: Integer;
begin
with FSurfaceGenerationParams do
begin
SurfaceMinY:= High(Integer);
// [0; 2Pi].
for I:= 0 to 628 do
begin
CalculatedValue:= Round((FirstSinusoidMultiplier * Sin(I / FirstSinusoidDivider) -
pi / FirstSinusoidPIShift) + SinOperation * (SecondSinusoidMultiplier *
Sin(I / SecondSinusoidDivider) + pi / SecondSinusoidPIShift));
if CalculatedValue < SurfaceMinY then
SurfaceMinY:= CalculatedValue;
end;
SurfaceMinY:= Abs(SurfaceMinY) + 10;
end;
end;
procedure TBallPhysicController.CheckContact;
var
I, MinSurfaceDistanceIndex: Integer;
MinSurfaceDistance, Distance: Double;
begin
// .
if (FBall.Rect.Left <= 0) and (FBall.X <= FBallPreviousX) then
CalculateAlpha(ctBoxLeft)
else
if (FBall.Rect.Right >= FBoxWidth) and (FBall.X >= FBallPreviousX) then
CalculateAlpha(ctBoxRight)
else
if (FBall.Rect.Top >= FBoxHeight) and (FBall.Y >= FBallPreviousY) then
CalculateAlpha(ctBoxTop)
else
if (FBall.Rect.Bottom <= 0) and (FBall.Y <= FBallPreviousY) then
CalculateAlpha(ctBoxBottom)
else
begin
// .
MinSurfaceDistance:= 1.7e308;
MinSurfaceDistanceIndex:= -1;
for I:= 0 to Length(FSurface) - 1 do
begin
Distance:= DistBetweenPoints2R(Vector2R(FSurface[I].X, FSurface[I].Y),
Vector2R(FBall.X, FBall.Y));
if Distance < MinSurfaceDistance then
begin
MinSurfaceDistance:= Distance;
MinSurfaceDistanceIndex:= I;
end;
end;
if (MinSurfaceDistanceIndex <> -1) and (MinSurfaceDistance <= FBall.Radius) and
(MinSurfaceDistance < DistBetweenPoints2R(Vector2R(FSurface[MinSurfaceDistanceIndex].X,
FSurface[MinSurfaceDistanceIndex].Y), Vector2R(FBallPreviousX, FBallPreviousY))) then
CalculateAlpha(ctSurface, MinSurfaceDistanceIndex);
end;
end;
constructor TBallPhysicController.Create;
begin
inherited Create;
FBall:= TBall.Create;
FBall.FRadius:= 20.0;
FBallDefaultZeroSpeed:= 0;
end;
destructor TBallPhysicController.Destroy;
begin
SetLength(FSurface, 0);
FBall.Free;
inherited;
end;
procedure TBallPhysicController.GenerateRandomSurfaceParams;
begin
with FSurfaceGenerationParams do
begin
Randomize;
FirstSinusoidMultiplier:= 25 * (Random(5) + 1);
SecondSinusoidMultiplier:= 15 * (Random(3) + 1);
FirstSinusoidDivider:= 100;
SecondSinusoidDivider:= 50;
FirstSinusoidPIShift:= Random(3) + 1;
SecondSinusoidPIShift:= Random(6) + 1;
SinOperation:= Random(2) - 1;
if SinOperation = 0 then
Inc(SinOperation);
end;
end;
procedure TBallPhysicController.GenerateSurface;
var
I: Integer;
begin
SetLength(FSurface, 0);
SetLength(FSurface, FBoxWidth);
with FSurfaceGenerationParams do
begin
for I:= 0 to FBoxWidth - 1 do
begin
FSurface[I].X:= I;
// 2 .
FSurface[I].Y:= Round(
(FirstSinusoidMultiplier * Sin(I / FirstSinusoidDivider) - pi / FirstSinusoidPIShift) +
SinOperation * (SecondSinusoidMultiplier * Sin(I/ SecondSinusoidDivider) +
pi / SecondSinusoidPIShift));
end;
// , .
for I:= 0 to FBoxWidth - 1 do
FSurface[I].Y:= FSurface[I].Y + Abs(SurfaceMinY);
end;
end;
procedure TBallPhysicController.Iterate;
var
I: Integer;
IterateCount: Integer;
begin
if FBall.Speed > 1 then
IterateCount:= Round(FBall.Speed) + 10
else
IterateCount:= 1;
for I:= 1 to IterateCount do
begin
FTimeMoment:= FTimeMoment + 0.01 / IterateCount;
FBallPreviousX:= FBall.X;
FBallPreviousY:= FBall.Y;
FBall.X:= FBallZeroPointX + Cos(FAlpha) * FBallZeroSpeed * FTimeMoment * 100;
FBall.Y:= FBallZeroPointY + (Sin(FAlpha) * FBallZeroSpeed * FTimeMoment -
4.9 * Sqr(FTimeMoment)) * 100;
FBall.Speed:= Sqrt(Sqr(FBallZeroSpeed * Cos(FAlpha)) +
Sqr(FBallZeroSpeed * Sin(FAlpha) - 9.8 * FTimeMoment));
CheckContact;
end;
end;
procedure TBallPhysicController.ResizeBox(const ABoxWidth,
ABoxHeight: Integer);
begin
FBoxWidth:= ABoxWidth;
FBoxHeight:= ABoxHeight;
GenerateSurface;
end;
procedure TBallPhysicController.Restart(const ABoxWidth,
ABoxHeight: Integer; const AIsAutoGeneratedSurface,
AIsRandomAngle: Boolean);
var
RandomAnglePart: Integer;
begin
FBoxWidth:= ABoxWidth;
FBoxHeight:= ABoxHeight;
if AIsAutoGeneratedSurface then
GenerateRandomSurfaceParams;
CalculateSurfaceMinY;
GenerateSurface;
FBall.X:= FBoxWidth / 2;
FBall.Y:= FBoxHeight / 4 * 3;
FBall.Speed:= 0;
FBallPreviousX:= FBall.X;
FBallPreviousY:= FBall.Y;
FBallZeroPointX:= FBall.X;
FBallZeroPointY:= FBall.Y;
FBallZeroSpeed:= FBallDefaultZeroSpeed;
FTimeMoment:= 0;
if AIsRandomAngle then
begin
Randomize;
RandomAnglePart:= Random(11) - 5;
if InRange (RandomAnglePart, -1, 1) then
RandomAnglePart:= - 3;
FAlpha:= pi / RandomAnglePart;
end;
end;
{ TBallVisualController }
constructor TBallVisualController.Create(
const AWindow: TWinControl);
begin
inherited Create;
FWindow:= AWindow;
FBallPhysicController:= TBallPhysicController.Create;
end;
procedure TBallVisualController.Draw;
var
BufferBitmap: TBitmap;
DC: HDC;
begin
BufferBitmap:= TBitmap.Create;
try
BufferBitmap.Width:= FBoxWidth;
BufferBitmap.Height:= FBoxHeight;
if Assigned(FBackground) then
BitBlt(BufferBitmap.Canvas.Handle, 0, 0, FBoxWidth, FBoxHeight,
FBackground.Canvas.Handle, 0, 0, SRCCOPY);
if Assigned(FBallPhysicController) then
with BufferBitmap.Canvas do
begin
Pen.Color:= clBlue;
Brush.Color:= clBlue;
Ellipse(InvertRect(FBallPhysicController.Ball.Rect));
end;
DC:= GetWindowDC(FWindow.Handle);
try
BitBlt(DC, 0, 0, FBoxWidth, FBoxHeight,
BufferBitmap.Canvas.Handle, 0, 0, SRCCOPY);
finally
ReleaseDC(FWindow.Handle, DC);
end;
finally
BufferBitmap.Free;
end;
end;
function TBallVisualController.InvertRect(ARect: TRect): TRect;
begin
Result:= ARect;
Result.TopLeft.Y:= FBoxHeight - Result.TopLeft.Y;
Result.BottomRight.Y:= FBoxHeight - Result.BottomRight.Y;
end;
function TBallVisualController.Invert(APoint: TPoint): TPoint;
begin
Result.X:= APoint.X;
Result.Y:= FBoxHeight - APoint.Y;
end;
procedure TBallVisualController.Iterate;
begin
FBallPhysicController.Iterate;
Draw;
end;
procedure TBallVisualController.Restart(const ABoxWidth,
ABoxHeight: Integer; const AIsAutoGeneratedSurface,
AIsRandomAngle: Boolean);
begin
FBallPhysicController.Restart(ABoxWidth, ABoxHeight,
AIsAutoGeneratedSurface, AIsRandomAngle);
FBoxHeight:= ABoxHeight;
FBoxWidth:= ABoxWidth;
GenerateBackground;
Draw;
end;
procedure TBallVisualController.GenerateBackground;
var
DrawableSurface: array of TPoint;
I: Integer;
begin
if not Assigned(FBackground) then
FBackGround:= TBitmap.Create;
FBackground.Width:= FBoxWidth;
FBackground.Height:= FBoxHeight;
SetLength(DrawableSurface, Length(FBallPhysicController.Surface) + 2);
try
for I:= 0 to Length(FBallPhysicController.Surface) - 1 do
DrawableSurface[I]:= Invert(FBallPhysicController.Surface[I]);
DrawableSurface[Length(FBallPhysicController.Surface)].X:= FBoxWidth;
DrawableSurface[Length(FBallPhysicController.Surface)].Y:= FBoxHeight;
DrawableSurface[Length(FBallPhysicController.Surface) + 1].X:= 0;
DrawableSurface[Length(FBallPhysicController.Surface) + 1].Y:= FBoxHeight;
with FBackground.Canvas do
begin
Pen.Color:= clRed;
Brush.Color:= clWhite;
Rectangle(FWindow.ClientRect);
Brush.Color:= clRed;
Polygon(DrawableSurface);
end;
finally
SetLength(DrawableSurface, 0);
end;
end;
destructor TBallVisualController.Destroy;
begin
if Assigned(FBackGround) then
FBackground.Free;
FBallPhysicController.Free;
inherited;
end;
procedure TBallVisualController.ResizeBox(const ABoxWidth,
ABoxHeight: Integer);
begin
FBallPhysicController.ResizeBox(ABoxWidth, ABoxHeight);
FBoxHeight:= ABoxHeight;
FBoxWidth:= ABoxWidth;
GenerateBackground;
end;
{ TBall }
function TBall.GetRect: TRect;
begin
Result.TopLeft.X:= Round(FX - FRadius);
Result.TopLeft.Y:= Round(FY + FRadius);
Result.BottomRight.X:= Round(FX + FRadius);
Result.BottomRight.Y:= Round(FY - FRadius);
end;
end.
- , . , .
4
, 3, .
.
, . , :
, , , ;
;
, .
300 | , . |
sin 3 | , .. . |
100 /c | , . |
1.
4.1 . , .. ¾ , . .
4.1 1
2.
4.2 . .
4.2 2
3.
4.3 . , 100 /, 100. .
4.3 3
. , .. .