aboutsummaryrefslogtreecommitdiff
path: root/Занимательное программирование/3/5_ideal_gas/octahedron.pas
blob: 31fe970f9533636c078e8b8087509cf70b036fee (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
unit Octahedron;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls,
  Geometry;

type

  { TForm1 }

  TForm1 = class(TForm)
    BackScreen: TImage;
    StartStopBtn: TButton;
    Screen: TPaintBox;
    procedure StartStopBtnClick(Sender: TObject);
  private
    Model: array[1..10] of Object3D; { трехмерный объект }
  public

  end;

var
  Form1: TForm1;
  IsRunning: Boolean = false;

implementation

{$R *.lfm}

procedure ShowShape(Shape: Object3D); { нарисовать объект }
var
  i, Xs, Ys: Integer;
  x, y, z: Real;
const
  N = 300; { "глубина" экрана }
begin
  for i := 0 to High(Shape.edges) do { цикл по всем ребрам }
  begin { координаты первой точки ребра }
    x := Shape.vertices[Shape.edges[i].src].x + Shape.xc;
    y := Shape.vertices[Shape.edges[i].src].y + Shape.yc;
    z := Shape.vertices[Shape.edges[i].src].z + Shape.zc;
    if Abs(z) < 0.01 then z := 0.01; { избегаем деления на нуль }
                                     { вычисляем экранные координаты }
    Xs := Round(Form1.Screen.Width div 2 + N * x / z);
    Ys := Round(Form1.Screen.Height div 2 + N * y / z);

    Form1.BackScreen.Canvas.MoveTo(Xs, Ys);
                                       { координаты второй точки зрения }
    x := Shape.vertices[Shape.edges[i].dest].x + Shape.xc;
    y := Shape.vertices[Shape.edges[i].dest].y + Shape.yc;
    z := Shape.vertices[Shape.edges[i].dest].z + Shape.zc;
    if Abs(z) < 0.01 then z := 0.01; { избегаем деления на нуль }
                                     { и ее экранные координаты }
    Xs := Round(Form1.Screen.Width div 2 + N * x / z);
    Ys := Round(Form1.Screen.Height div 2 + N * y / z);

    Form1.BackScreen.Canvas.LineTo(Xs, Ys); { рисуем ребро }
  end;
end;

{ TForm1 }

procedure TForm1.StartStopBtnClick(Sender: TObject); { главная процедура }
var
  oldtime: TDateTime;
  pause: Integer;
  center, velocity, axis: Vertex;
  i: Integer;
const
  MSecsPerFrame = 25; { скорость работы (кадров в секунду) }
begin
  if IsRunning then
  begin
    IsRunning := false;
    StartStopBtn.Caption := 'Пуск';
    for i := 1 to Length(Model) do
      FreeAndNil(Model[i]);
    Exit;
  end;
  StartStopBtn.Caption := 'Стоп';
  IsRunning := true;

  for i := 1 to Length(Model) do
  begin
    axis.x := Random(10) / 100; { скорость вращения вокруг оси Ox }
    axis.y := Random(10) / 100; { скорость вращения вокруг оси Oy }
    axis.z := Random(10) / 100; { скорость вращения вокруг оси Oz }
    velocity.x := 10 - Random(20); { и составляющие }
    velocity.y := 15 - Random(30); { скорости }
    velocity.z := 20 - Random(40);
    center.x := (Screen.Width div 2) - Random(Screen.Width); { начальное положение }
    center.y := (Screen.Width div 2) - Random(Screen.Width); { начальное положение }
    center.z := Random(300) + 200;
    Model[i] := Object3D.Create(center, velocity, axis) { загрузить объект }
  end;

  while IsRunning do
  begin
    oldtime := Now;
    BackScreen.Canvas.FillRect(Rect(0, 0, Screen.Width, Screen.Height));

    for i := 1 to Length(Model) do
    begin
      { отразить от стена (3D-аналог "молекулы в закрытом сосуде") }
      MoveShape(Model[i], BackScreen.Width div 2, BackScreen.Height div 2);

      RotateShape(Model[i]); { поворот модели }
      ShowShape(Model[i]); { очистить экран и нарисовать объект }
      Model[i].MoveCenter()
    end;
    { отобразить объект на основном экране }
    Screen.Canvas.CopyRect(Rect(0, 0, Screen.Width, Screen.Height),
        BackScreen.Canvas, Rect(0, 0, Screen.Width, Screen.Height));


    Application.ProcessMessages;
    pause := Round(MSecsPerFrame - (Now - oldtime) * MSecsPerDay);
    if pause > 0 then Sleep(pause); { задержка }

    if Application.Terminated then Exit;
  end;
end;

end.