aboutsummaryrefslogtreecommitdiff
path: root/Занимательное программирование/3/3_random/octahedron.pas
blob: 7bb06e17bbce9f0d4f3ebe299521691cbafd6670 (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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
unit Octahedron;

{$mode objfpc}{$H+}

interface

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

type

  { TForm1 }

  TForm1 = class(TForm)
    BackScreen: TImage;
    StartStopBtn: TButton;
    Screen: TPaintBox;
    procedure StartStopBtnClick(Sender: TObject);
  private

  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;
  Model: Object3D; { трехмерный объект }
  Vx, Vy, Vz: Real; { значения составляющих его скорости }
  xa, ya, za: Real;
  pause: Integer;
const
  MSecsPerFrame = 25; { скорость работы (кадров в секунду) }
begin
  xa := Random(10) / 100; { скорость вращения вокруг оси Ox }
  ya := Random(10) / 100; { скорость вращения вокруг оси Oy }
  za := Random(10) / 100; { скорость вращения вокруг оси Oz }

  if IsRunning then
  begin
    IsRunning := false;
    StartStopBtn.Caption := 'Пуск';
    Exit;
  end;
  StartStopBtn.Caption := 'Стоп';
  IsRunning := true;

  Model := LoadObject3D('octahedron.txt'); { загрузить объект }

  Model.xc := (Screen.Width div 2) - Random(Screen.Width); { начальное положение }
  Model.yc := (Screen.Height div 2) - Random(Screen.Height); { объекта }
  Model.zc := 200;
  Vx := 10 - Random(20); { и составляющие }
  Vy := 15 - Random(30); { скорости }
  Vz := 20 - Random(40);

  while IsRunning do
  begin
    oldtime := Now;

    { отразить от стена (3D-аналог "молекулы в закрытом сосуде") }
    if Model.xc > BackScreen.Width div 2 then
    begin
      Model.xc := BackScreen.Width div 2;
      Vx := -Vx;
    end;
    if Model.xc < -BackScreen.Width div 2 then
    begin
      Model.xc := -BackScreen.Width div 2;
      Vx := -Vx;
    end;

    if Model.yc > BackScreen.Height div 2 then
    begin
      Model.yc := BackScreen.Height div 2;
      Vy := -Vy;
    end;
    if Model.yc < -BackScreen.Height div 2 then
    begin
      Model.yc := -BackScreen.Height div 2;
      Vy := -Vy;
    end;

    if Model.zc > 1000 then
    begin
      Model.zc := 1000;
      Vz := -Vz;
    end;
    if Model.zc < 200 then
    begin
      Model.zc := 200;
      Vz := -Vz;
    end;

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

    Model.xc := Model.xc + Vx;
    Model.yc := Model.yc + Vy;
    Model.zc := Model.zc + Vz;

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

    if Application.Terminated then Exit;
  end;
end;

end.