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.