diff --git a/3/1_move/geometry.pas b/3/1_move/geometry.pas
new file mode 100644
index 0000000..cc00fd7
--- /dev/null
+++ b/3/1_move/geometry.pas
@@ -0,0 +1,302 @@
+unit Geometry;
+
+{$mode ObjFPC}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils;
+
+type
+ { Вершины }
+ Vertex = record
+ x, y, z: Real; { Координаты вершины }
+ end;
+ { Ребра }
+ Edge = record
+ src, dest: Integer; { Индексы соединяемых вершин }
+ end;
+
+ Object3D = record
+ vertices: array of Vertex; { вершины }
+ edges: array of Edge; { ребра }
+ xc, yc, zc: Real; { координаты центра объекта }
+ end;
+
+ Matrix = array[1..4, 1..4] of Real; { матрица 4x4 }
+ Column = array[1..4] of Real; { столбец }
+
+function LoadObject3D(filename: String): Object3D;
+function CopyObject3D(Shape: Object3D): Object3D;
+
+{ умножение матрицы на матрицу }
+function MMMult(lhs, rhs: Matrix): Matrix;
+{ умножение матрицы на столбец }
+function MCMult(lhs: Matrix; rhs: Column): Column;
+{ матрица поворота модели }
+function RotateMatrix(xa, ya, za: Real): Matrix;
+function MoveMatrix(tx, ty, tz: Real): Matrix;
+function ResizeMatrix(sx, sy, sz: Real): Matrix;
+procedure RotateShape(var Shape: Object3D; xa, ya, za: Real);
+procedure MoveShape(var Shape: Object3D; tx, ty, tz: Real);
+procedure ResizeShape(var Shape: Object3D; sx, sy, sz: Real);
+
+implementation
+
+function LoadObject3D(filename: String): Object3D;
+var
+ F: TextFile; { исходный файл }
+ NV, NE: Integer; { количество вершин, количество ребер }
+ Shape: Object3D; { модель }
+ i: Integer; { счетчик цикла }
+begin
+ AssignFile(F, filename); { открываем исходный файл }
+ FileMode := 0; { в режиме "только чтение" }
+ Reset(F);
+
+ ReadLn(F, NV); { считываем количество вершин }
+ SetLength(Shape.vertices, NV); { и корректируем размер массива }
+ for i := 0 to NV - 1 do { считываем каждую вершину }
+ ReadLn(F, Shape.vertices[i].x, Shape.vertices[i].y, Shape.vertices[i].z);
+
+ ReadLn(F, NE); { то же для ребер }
+ SetLength(Shape.edges, NE);
+ for i := 0 to NE - 1 do
+ ReadLn(F, Shape.edges[i].src, Shape.edges[i].dest);
+
+ CloseFile(F);
+ LoadObject3D := Shape;
+end;
+
+function CopyObject3D(Shape: Object3D): Object3D;
+begin
+ CopyObject3D.vertices := Copy(Shape.vertices, 0);
+ CopyObject3D.edges := Copy(Shape.edges, 0);
+
+ CopyObject3D.xc := Shape.xc;
+ CopyObject3D.yc := Shape.yc;
+ CopyObject3D.zc := Shape.zc
+end;
+
+function MMMult(lhs, rhs: Matrix): Matrix;
+var
+ i, j, k: Integer;
+ r: Matrix;
+ s: Real;
+begin
+ for i := 1 to 4 do
+ for j := 1 to 4 do
+ begin
+ s := 0;
+ for k := 1 to 4 do
+ s := s + lhs[i, k] * rhs[k, j];
+ r[i, j] := s;
+ end;
+ MMMult := r;
+end;
+
+function MCMult(lhs: Matrix; rhs: Column): Column;
+var
+ k, i: Integer;
+ s: Real;
+ r: Column;
+begin
+ for i := 1 to 4 do { аналогично MMMult }
+ begin
+ s := 0;
+
+ for k := 1 to 4 do
+ s := s + lhs[i, k] * rhs[k];
+ r[i] := s;
+ end;
+
+ MCMult := r;
+end;
+
+function RotateMatrix(xa, ya, za: Real): Matrix;
+var
+ xr, yr, zr: Matrix;
+begin
+ { матрица поворота вокруг оси Ox }
+ xr[1, 1] := 1;
+ xr[1, 2] := 0;
+ xr[1, 3] := 0;
+ xr[1, 4] := 0;
+
+ xr[2, 1] := 0;
+ xr[2, 2] := Cos(xa);
+ xr[2, 3] := -Sin(xa);
+ xr[2, 4] := 0;
+
+ xr[3, 1] := 0;
+ xr[3, 2] := Sin(xa);
+ xr[3, 3] := Cos(xa);
+ xr[3, 4] := 0;
+
+ xr[4, 1] := 0;
+ xr[4, 2] := 0;
+ xr[4, 3] := 0;
+ xr[4, 4] := 1;
+
+ { матрица поворота вокруг оси Oy }
+ yr[1, 1] := Cos(ya);
+ yr[1, 2] := 0;
+ yr[1, 3] := Sin(ya);
+ yr[1, 4] := 0;
+
+ yr[2, 1] := 0;
+ yr[2, 2] := 1;
+ yr[2, 3] := 0;
+ yr[2, 4] := 0;
+
+ yr[3, 1] := -Sin(ya);
+ yr[3, 2] := 0;
+ yr[3, 3] := Cos(ya);
+ yr[3, 4] := 0;
+
+ yr[4, 1] := 0;
+ yr[4, 2] := 0;
+ yr[4, 3] := 0;
+ yr[4, 4] := 1;
+
+ { матрица поворота вокруг оси Oz }
+ zr[1, 1] := Cos(za);
+ zr[1, 2] := -Sin(za);
+ zr[1, 3] := 0;
+ zr[1, 4] := 0;
+
+ zr[2, 1] := Sin(za);
+ zr[2, 2] := Cos(za);
+ zr[2, 3] := 0;
+ zr[2, 4] := 0;
+
+ zr[3, 1] := 0;
+ zr[3, 2] := 0;
+ zr[3, 3] := 1;
+ zr[3, 4] := 0;
+
+ zr[4, 1] := 0;
+ zr[4, 2] := 0;
+ zr[4, 3] := 0;
+ zr[4, 4] := 1;
+
+ RotateMatrix := MMMult(MMMult(xr, yr), zr)
+end;
+
+function MoveMatrix(tx, ty, tz: Real): Matrix;
+begin
+ MoveMatrix[1, 1] := 1;
+ MoveMatrix[1, 2] := 0;
+ MoveMatrix[1, 3] := 0;
+ MoveMatrix[1, 4] := tx;
+
+ MoveMatrix[2, 1] := 0;
+ MoveMatrix[2, 2] := 1;
+ MoveMatrix[2, 3] := 0;
+ MoveMatrix[2, 4] := ty;
+
+ MoveMatrix[3, 1] := 0;
+ MoveMatrix[3, 2] := 0;
+ MoveMatrix[3, 3] := 1;
+ MoveMatrix[3, 4] := tz;
+
+ MoveMatrix[4, 1] := 0;
+ MoveMatrix[4, 2] := 0;
+ MoveMatrix[4, 3] := 0;
+ MoveMatrix[4, 4] := 1
+end;
+
+function ResizeMatrix(sx, sy, sz: Real): Matrix;
+begin
+ ResizeMatrix[1, 1] := sx;
+ ResizeMatrix[1, 2] := 0;
+ ResizeMatrix[1, 3] := 0;
+ ResizeMatrix[1, 4] := 0;
+
+ ResizeMatrix[2, 1] := 0;
+ ResizeMatrix[2, 2] := sy;
+ ResizeMatrix[2, 3] := 0;
+ ResizeMatrix[2, 4] := 0;
+
+ ResizeMatrix[3, 1] := 0;
+ ResizeMatrix[3, 2] := 0;
+ ResizeMatrix[3, 3] := sz;
+ ResizeMatrix[3, 4] := 0;
+
+ ResizeMatrix[4, 1] := 0;
+ ResizeMatrix[4, 2] := 0;
+ ResizeMatrix[4, 3] := 0;
+ ResizeMatrix[4, 4] := 1
+end;
+
+procedure RotateShape(var Shape: Object3D; xa, ya, za: Real);
+var
+ rm: Matrix;
+ i: Integer;
+ c: Column;
+begin
+ rm := RotateMatrix(xa, ya, za); { сгенерировать матрицу вращения }
+ c[4] := 1; { последний элемент столбца всегда равен единице }
+
+ for i := 0 to High(Shape.vertices) do { цикл по всем вершинам }
+ begin { High(a) возвращает верхний индекс массива a }
+ c[1] := Shape.vertices[i].x; { инициализация столбца }
+ c[2] := Shape.vertices[i].y;
+ c[3] := Shape.vertices[i].z;
+
+ c := MCMult(rm, c); { вызов преобразования }
+
+ Shape.vertices[i].x := c[1]; { внесение изменений в модель }
+ Shape.vertices[i].y := c[2]; { в соответствии с полученным }
+ Shape.vertices[i].z := c[3] { результатом преобразования }
+ end
+end;
+
+procedure MoveShape(var Shape: Object3D; tx, ty, tz: Real);
+var
+ mm: Matrix;
+ i: Integer;
+ c: Column;
+begin
+ mm := MoveMatrix(tx, ty, tz);
+ c[4] := 1;
+
+ for i := 0 to High(Shape.vertices) do
+ begin
+ c[1] := Shape.vertices[i].x;
+ c[2] := Shape.vertices[i].y;
+ c[3] := Shape.vertices[i].z;
+
+ c := MCMult(mm, c);
+
+ Shape.vertices[i].x := c[1];
+ Shape.vertices[i].y := c[2];
+ Shape.vertices[i].z := c[3]
+ end
+end;
+
+procedure ResizeShape(var Shape: Object3D; sx, sy, sz: Real);
+var
+ mm: Matrix;
+ i: Integer;
+ c: Column;
+begin
+ mm := ResizeMatrix(sx, sy, sz);
+ c[4] := 1;
+
+ for i := 0 to High(Shape.vertices) do
+ begin
+ c[1] := Shape.vertices[i].x;
+ c[2] := Shape.vertices[i].y;
+ c[3] := Shape.vertices[i].z;
+
+ c := MCMult(mm, c);
+
+ Shape.vertices[i].x := c[1];
+ Shape.vertices[i].y := c[2];
+ Shape.vertices[i].z := c[3]
+ end
+end;
+
+end.
+
diff --git a/3/1_move/graphics_3d.lpi b/3/1_move/graphics_3d.lpi
new file mode 100644
index 0000000..67fa0db
--- /dev/null
+++ b/3/1_move/graphics_3d.lpi
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
diff --git a/3/1_move/graphics_3d.lpr b/3/1_move/graphics_3d.lpr
new file mode 100644
index 0000000..619b96b
--- /dev/null
+++ b/3/1_move/graphics_3d.lpr
@@ -0,0 +1,28 @@
+program graphics_3d;
+
+{$mode objfpc}{$H+}
+
+uses
+ {$IFDEF UNIX}
+ cthreads,
+ {$ENDIF}
+ {$IFDEF HASAMIGA}
+ athreads,
+ {$ENDIF}
+ Interfaces, // this includes the LCL widgetset
+ Forms, Octahedron, geometry
+ { you can add units after this };
+
+{$R *.res}
+
+begin
+ RequireDerivedFormResource:=True;
+ Application.Scaled:=True;
+ {$PUSH}{$WARN 5044 OFF}
+ Application.MainFormOnTaskbar:=True;
+ {$POP}
+ Application.Initialize;
+ Application.CreateForm(TForm1, Form1);
+ Application.Run;
+end.
+
diff --git a/3/1_move/graphics_3d.lps b/3/1_move/graphics_3d.lps
new file mode 100644
index 0000000..a00e45a
--- /dev/null
+++ b/3/1_move/graphics_3d.lps
@@ -0,0 +1,179 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/3/1_move/octahedron.lfm b/3/1_move/octahedron.lfm
new file mode 100644
index 0000000..34b256e
--- /dev/null
+++ b/3/1_move/octahedron.lfm
@@ -0,0 +1,75 @@
+object Form1: TForm1
+ Left = 352
+ Height = 673
+ Top = 32
+ Width = 600
+ Caption = 'Form1'
+ ClientHeight = 673
+ ClientWidth = 600
+ DesignTimePPI = 120
+ object BackScreen: TImage
+ AnchorSideLeft.Control = Owner
+ AnchorSideTop.Control = Owner
+ AnchorSideRight.Control = Owner
+ AnchorSideRight.Side = asrBottom
+ AnchorSideBottom.Control = StartStopBtn
+ Left = 0
+ Height = 600
+ Top = 0
+ Width = 600
+ Anchors = [akTop, akLeft, akRight, akBottom]
+ BorderSpacing.Bottom = 15
+ end
+ object Screen: TPaintBox
+ AnchorSideLeft.Control = Owner
+ AnchorSideTop.Control = Owner
+ AnchorSideRight.Control = Owner
+ AnchorSideRight.Side = asrBottom
+ AnchorSideBottom.Control = StartStopBtn
+ Left = 0
+ Height = 600
+ Top = 0
+ Width = 600
+ Anchors = [akTop, akLeft, akRight, akBottom]
+ BorderSpacing.Bottom = 15
+ end
+ object StartStopBtn: TButton
+ AnchorSideLeft.Control = Owner
+ AnchorSideLeft.Side = asrCenter
+ AnchorSideRight.Control = Owner
+ AnchorSideRight.Side = asrBottom
+ AnchorSideBottom.Control = Owner
+ AnchorSideBottom.Side = asrBottom
+ Left = 472
+ Height = 48
+ Top = 615
+ Width = 118
+ Anchors = [akRight, akBottom]
+ BorderSpacing.Right = 10
+ BorderSpacing.Bottom = 10
+ Caption = 'Пуск'
+ TabOrder = 0
+ OnClick = StartStopBtnClick
+ end
+ object ResizeField: TFloatSpinEdit
+ AnchorSideLeft.Control = Owner
+ AnchorSideBottom.Control = Owner
+ AnchorSideBottom.Side = asrBottom
+ Left = 10
+ Height = 28
+ Top = 635
+ Width = 63
+ Anchors = [akLeft, akBottom]
+ BorderSpacing.Left = 10
+ BorderSpacing.Bottom = 10
+ TabOrder = 1
+ Value = 1
+ end
+ object Label1: TLabel
+ Left = 10
+ Height = 20
+ Top = 615
+ Width = 125
+ Caption = 'Изменить размер'
+ end
+end
diff --git a/3/1_move/octahedron.pas b/3/1_move/octahedron.pas
new file mode 100644
index 0000000..a5aae45
--- /dev/null
+++ b/3/1_move/octahedron.pas
@@ -0,0 +1,162 @@
+unit Octahedron;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls,
+ Spin, Geometry;
+
+type
+
+ { TForm1 }
+
+ TForm1 = class(TForm)
+ BackScreen: TImage;
+ Label1: TLabel;
+ ResizeField: TFloatSpinEdit;
+ 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, BackModel: Object3D; { трехмерный объект }
+ Vx, Vy, Vz: Real; { значения составляющих его скорости }
+ pause: Integer;
+const
+ MSecsPerFrame = 25; { скорость работы (кадров в секунду) }
+ xa = 0.01; { скорость вращения вокруг оси Ox }
+ ya = 0.05; { скорость вращения вокруг оси Oy }
+ za = 0.08; { скорость вращения вокруг оси Oz }
+begin
+ if IsRunning then
+ begin
+ IsRunning := false;
+ StartStopBtn.Caption := 'Пуск';
+ Exit;
+ end;
+ StartStopBtn.Caption := 'Стоп';
+ IsRunning := true;
+
+ Model := LoadObject3D('octahedron.txt'); { загрузить объект }
+ ResizeShape(Model, ResizeField.Value, ResizeField.Value, ResizeField.Value);
+
+ Model.xc := 0; { начальное положение }
+ Model.yc := 0; { объекта }
+ Model.zc := 200;
+ Vx := 10; { и составляющие }
+ Vy := 15; { скорости }
+ Vz := 20;
+
+ 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); { поворот модели }
+ BackModel := CopyObject3D(Model);
+ MoveShape(Model, Vx, Vy, Vz); { переместить объект }
+
+ 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 := CopyObject3D(BackModel);
+ 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.
+
diff --git a/3/1_move/octahedron.txt b/3/1_move/octahedron.txt
new file mode 100644
index 0000000..770d229
--- /dev/null
+++ b/3/1_move/octahedron.txt
@@ -0,0 +1,20 @@
+6
+-100 -100 0
+-100 100 0
+100 100 0
+100 -100 0
+0 0 -141.42
+0 0 141.42
+12
+0 1
+1 2
+2 3
+3 0
+4 0
+4 1
+4 2
+4 3
+5 0
+5 1
+5 2
+5 3
diff --git a/3/2_projection/geometry.pas b/3/2_projection/geometry.pas
new file mode 100644
index 0000000..cc00fd7
--- /dev/null
+++ b/3/2_projection/geometry.pas
@@ -0,0 +1,302 @@
+unit Geometry;
+
+{$mode ObjFPC}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils;
+
+type
+ { Вершины }
+ Vertex = record
+ x, y, z: Real; { Координаты вершины }
+ end;
+ { Ребра }
+ Edge = record
+ src, dest: Integer; { Индексы соединяемых вершин }
+ end;
+
+ Object3D = record
+ vertices: array of Vertex; { вершины }
+ edges: array of Edge; { ребра }
+ xc, yc, zc: Real; { координаты центра объекта }
+ end;
+
+ Matrix = array[1..4, 1..4] of Real; { матрица 4x4 }
+ Column = array[1..4] of Real; { столбец }
+
+function LoadObject3D(filename: String): Object3D;
+function CopyObject3D(Shape: Object3D): Object3D;
+
+{ умножение матрицы на матрицу }
+function MMMult(lhs, rhs: Matrix): Matrix;
+{ умножение матрицы на столбец }
+function MCMult(lhs: Matrix; rhs: Column): Column;
+{ матрица поворота модели }
+function RotateMatrix(xa, ya, za: Real): Matrix;
+function MoveMatrix(tx, ty, tz: Real): Matrix;
+function ResizeMatrix(sx, sy, sz: Real): Matrix;
+procedure RotateShape(var Shape: Object3D; xa, ya, za: Real);
+procedure MoveShape(var Shape: Object3D; tx, ty, tz: Real);
+procedure ResizeShape(var Shape: Object3D; sx, sy, sz: Real);
+
+implementation
+
+function LoadObject3D(filename: String): Object3D;
+var
+ F: TextFile; { исходный файл }
+ NV, NE: Integer; { количество вершин, количество ребер }
+ Shape: Object3D; { модель }
+ i: Integer; { счетчик цикла }
+begin
+ AssignFile(F, filename); { открываем исходный файл }
+ FileMode := 0; { в режиме "только чтение" }
+ Reset(F);
+
+ ReadLn(F, NV); { считываем количество вершин }
+ SetLength(Shape.vertices, NV); { и корректируем размер массива }
+ for i := 0 to NV - 1 do { считываем каждую вершину }
+ ReadLn(F, Shape.vertices[i].x, Shape.vertices[i].y, Shape.vertices[i].z);
+
+ ReadLn(F, NE); { то же для ребер }
+ SetLength(Shape.edges, NE);
+ for i := 0 to NE - 1 do
+ ReadLn(F, Shape.edges[i].src, Shape.edges[i].dest);
+
+ CloseFile(F);
+ LoadObject3D := Shape;
+end;
+
+function CopyObject3D(Shape: Object3D): Object3D;
+begin
+ CopyObject3D.vertices := Copy(Shape.vertices, 0);
+ CopyObject3D.edges := Copy(Shape.edges, 0);
+
+ CopyObject3D.xc := Shape.xc;
+ CopyObject3D.yc := Shape.yc;
+ CopyObject3D.zc := Shape.zc
+end;
+
+function MMMult(lhs, rhs: Matrix): Matrix;
+var
+ i, j, k: Integer;
+ r: Matrix;
+ s: Real;
+begin
+ for i := 1 to 4 do
+ for j := 1 to 4 do
+ begin
+ s := 0;
+ for k := 1 to 4 do
+ s := s + lhs[i, k] * rhs[k, j];
+ r[i, j] := s;
+ end;
+ MMMult := r;
+end;
+
+function MCMult(lhs: Matrix; rhs: Column): Column;
+var
+ k, i: Integer;
+ s: Real;
+ r: Column;
+begin
+ for i := 1 to 4 do { аналогично MMMult }
+ begin
+ s := 0;
+
+ for k := 1 to 4 do
+ s := s + lhs[i, k] * rhs[k];
+ r[i] := s;
+ end;
+
+ MCMult := r;
+end;
+
+function RotateMatrix(xa, ya, za: Real): Matrix;
+var
+ xr, yr, zr: Matrix;
+begin
+ { матрица поворота вокруг оси Ox }
+ xr[1, 1] := 1;
+ xr[1, 2] := 0;
+ xr[1, 3] := 0;
+ xr[1, 4] := 0;
+
+ xr[2, 1] := 0;
+ xr[2, 2] := Cos(xa);
+ xr[2, 3] := -Sin(xa);
+ xr[2, 4] := 0;
+
+ xr[3, 1] := 0;
+ xr[3, 2] := Sin(xa);
+ xr[3, 3] := Cos(xa);
+ xr[3, 4] := 0;
+
+ xr[4, 1] := 0;
+ xr[4, 2] := 0;
+ xr[4, 3] := 0;
+ xr[4, 4] := 1;
+
+ { матрица поворота вокруг оси Oy }
+ yr[1, 1] := Cos(ya);
+ yr[1, 2] := 0;
+ yr[1, 3] := Sin(ya);
+ yr[1, 4] := 0;
+
+ yr[2, 1] := 0;
+ yr[2, 2] := 1;
+ yr[2, 3] := 0;
+ yr[2, 4] := 0;
+
+ yr[3, 1] := -Sin(ya);
+ yr[3, 2] := 0;
+ yr[3, 3] := Cos(ya);
+ yr[3, 4] := 0;
+
+ yr[4, 1] := 0;
+ yr[4, 2] := 0;
+ yr[4, 3] := 0;
+ yr[4, 4] := 1;
+
+ { матрица поворота вокруг оси Oz }
+ zr[1, 1] := Cos(za);
+ zr[1, 2] := -Sin(za);
+ zr[1, 3] := 0;
+ zr[1, 4] := 0;
+
+ zr[2, 1] := Sin(za);
+ zr[2, 2] := Cos(za);
+ zr[2, 3] := 0;
+ zr[2, 4] := 0;
+
+ zr[3, 1] := 0;
+ zr[3, 2] := 0;
+ zr[3, 3] := 1;
+ zr[3, 4] := 0;
+
+ zr[4, 1] := 0;
+ zr[4, 2] := 0;
+ zr[4, 3] := 0;
+ zr[4, 4] := 1;
+
+ RotateMatrix := MMMult(MMMult(xr, yr), zr)
+end;
+
+function MoveMatrix(tx, ty, tz: Real): Matrix;
+begin
+ MoveMatrix[1, 1] := 1;
+ MoveMatrix[1, 2] := 0;
+ MoveMatrix[1, 3] := 0;
+ MoveMatrix[1, 4] := tx;
+
+ MoveMatrix[2, 1] := 0;
+ MoveMatrix[2, 2] := 1;
+ MoveMatrix[2, 3] := 0;
+ MoveMatrix[2, 4] := ty;
+
+ MoveMatrix[3, 1] := 0;
+ MoveMatrix[3, 2] := 0;
+ MoveMatrix[3, 3] := 1;
+ MoveMatrix[3, 4] := tz;
+
+ MoveMatrix[4, 1] := 0;
+ MoveMatrix[4, 2] := 0;
+ MoveMatrix[4, 3] := 0;
+ MoveMatrix[4, 4] := 1
+end;
+
+function ResizeMatrix(sx, sy, sz: Real): Matrix;
+begin
+ ResizeMatrix[1, 1] := sx;
+ ResizeMatrix[1, 2] := 0;
+ ResizeMatrix[1, 3] := 0;
+ ResizeMatrix[1, 4] := 0;
+
+ ResizeMatrix[2, 1] := 0;
+ ResizeMatrix[2, 2] := sy;
+ ResizeMatrix[2, 3] := 0;
+ ResizeMatrix[2, 4] := 0;
+
+ ResizeMatrix[3, 1] := 0;
+ ResizeMatrix[3, 2] := 0;
+ ResizeMatrix[3, 3] := sz;
+ ResizeMatrix[3, 4] := 0;
+
+ ResizeMatrix[4, 1] := 0;
+ ResizeMatrix[4, 2] := 0;
+ ResizeMatrix[4, 3] := 0;
+ ResizeMatrix[4, 4] := 1
+end;
+
+procedure RotateShape(var Shape: Object3D; xa, ya, za: Real);
+var
+ rm: Matrix;
+ i: Integer;
+ c: Column;
+begin
+ rm := RotateMatrix(xa, ya, za); { сгенерировать матрицу вращения }
+ c[4] := 1; { последний элемент столбца всегда равен единице }
+
+ for i := 0 to High(Shape.vertices) do { цикл по всем вершинам }
+ begin { High(a) возвращает верхний индекс массива a }
+ c[1] := Shape.vertices[i].x; { инициализация столбца }
+ c[2] := Shape.vertices[i].y;
+ c[3] := Shape.vertices[i].z;
+
+ c := MCMult(rm, c); { вызов преобразования }
+
+ Shape.vertices[i].x := c[1]; { внесение изменений в модель }
+ Shape.vertices[i].y := c[2]; { в соответствии с полученным }
+ Shape.vertices[i].z := c[3] { результатом преобразования }
+ end
+end;
+
+procedure MoveShape(var Shape: Object3D; tx, ty, tz: Real);
+var
+ mm: Matrix;
+ i: Integer;
+ c: Column;
+begin
+ mm := MoveMatrix(tx, ty, tz);
+ c[4] := 1;
+
+ for i := 0 to High(Shape.vertices) do
+ begin
+ c[1] := Shape.vertices[i].x;
+ c[2] := Shape.vertices[i].y;
+ c[3] := Shape.vertices[i].z;
+
+ c := MCMult(mm, c);
+
+ Shape.vertices[i].x := c[1];
+ Shape.vertices[i].y := c[2];
+ Shape.vertices[i].z := c[3]
+ end
+end;
+
+procedure ResizeShape(var Shape: Object3D; sx, sy, sz: Real);
+var
+ mm: Matrix;
+ i: Integer;
+ c: Column;
+begin
+ mm := ResizeMatrix(sx, sy, sz);
+ c[4] := 1;
+
+ for i := 0 to High(Shape.vertices) do
+ begin
+ c[1] := Shape.vertices[i].x;
+ c[2] := Shape.vertices[i].y;
+ c[3] := Shape.vertices[i].z;
+
+ c := MCMult(mm, c);
+
+ Shape.vertices[i].x := c[1];
+ Shape.vertices[i].y := c[2];
+ Shape.vertices[i].z := c[3]
+ end
+end;
+
+end.
+
diff --git a/3/2_projection/graphics_3d.lpi b/3/2_projection/graphics_3d.lpi
new file mode 100644
index 0000000..67fa0db
--- /dev/null
+++ b/3/2_projection/graphics_3d.lpi
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
diff --git a/3/2_projection/graphics_3d.lpr b/3/2_projection/graphics_3d.lpr
new file mode 100644
index 0000000..619b96b
--- /dev/null
+++ b/3/2_projection/graphics_3d.lpr
@@ -0,0 +1,28 @@
+program graphics_3d;
+
+{$mode objfpc}{$H+}
+
+uses
+ {$IFDEF UNIX}
+ cthreads,
+ {$ENDIF}
+ {$IFDEF HASAMIGA}
+ athreads,
+ {$ENDIF}
+ Interfaces, // this includes the LCL widgetset
+ Forms, Octahedron, geometry
+ { you can add units after this };
+
+{$R *.res}
+
+begin
+ RequireDerivedFormResource:=True;
+ Application.Scaled:=True;
+ {$PUSH}{$WARN 5044 OFF}
+ Application.MainFormOnTaskbar:=True;
+ {$POP}
+ Application.Initialize;
+ Application.CreateForm(TForm1, Form1);
+ Application.Run;
+end.
+
diff --git a/3/2_projection/graphics_3d.lps b/3/2_projection/graphics_3d.lps
new file mode 100644
index 0000000..6b42edd
--- /dev/null
+++ b/3/2_projection/graphics_3d.lps
@@ -0,0 +1,175 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/3/2_projection/octahedron.lfm b/3/2_projection/octahedron.lfm
new file mode 100644
index 0000000..34b256e
--- /dev/null
+++ b/3/2_projection/octahedron.lfm
@@ -0,0 +1,75 @@
+object Form1: TForm1
+ Left = 352
+ Height = 673
+ Top = 32
+ Width = 600
+ Caption = 'Form1'
+ ClientHeight = 673
+ ClientWidth = 600
+ DesignTimePPI = 120
+ object BackScreen: TImage
+ AnchorSideLeft.Control = Owner
+ AnchorSideTop.Control = Owner
+ AnchorSideRight.Control = Owner
+ AnchorSideRight.Side = asrBottom
+ AnchorSideBottom.Control = StartStopBtn
+ Left = 0
+ Height = 600
+ Top = 0
+ Width = 600
+ Anchors = [akTop, akLeft, akRight, akBottom]
+ BorderSpacing.Bottom = 15
+ end
+ object Screen: TPaintBox
+ AnchorSideLeft.Control = Owner
+ AnchorSideTop.Control = Owner
+ AnchorSideRight.Control = Owner
+ AnchorSideRight.Side = asrBottom
+ AnchorSideBottom.Control = StartStopBtn
+ Left = 0
+ Height = 600
+ Top = 0
+ Width = 600
+ Anchors = [akTop, akLeft, akRight, akBottom]
+ BorderSpacing.Bottom = 15
+ end
+ object StartStopBtn: TButton
+ AnchorSideLeft.Control = Owner
+ AnchorSideLeft.Side = asrCenter
+ AnchorSideRight.Control = Owner
+ AnchorSideRight.Side = asrBottom
+ AnchorSideBottom.Control = Owner
+ AnchorSideBottom.Side = asrBottom
+ Left = 472
+ Height = 48
+ Top = 615
+ Width = 118
+ Anchors = [akRight, akBottom]
+ BorderSpacing.Right = 10
+ BorderSpacing.Bottom = 10
+ Caption = 'Пуск'
+ TabOrder = 0
+ OnClick = StartStopBtnClick
+ end
+ object ResizeField: TFloatSpinEdit
+ AnchorSideLeft.Control = Owner
+ AnchorSideBottom.Control = Owner
+ AnchorSideBottom.Side = asrBottom
+ Left = 10
+ Height = 28
+ Top = 635
+ Width = 63
+ Anchors = [akLeft, akBottom]
+ BorderSpacing.Left = 10
+ BorderSpacing.Bottom = 10
+ TabOrder = 1
+ Value = 1
+ end
+ object Label1: TLabel
+ Left = 10
+ Height = 20
+ Top = 615
+ Width = 125
+ Caption = 'Изменить размер'
+ end
+end
diff --git a/3/2_projection/octahedron.pas b/3/2_projection/octahedron.pas
new file mode 100644
index 0000000..4059739
--- /dev/null
+++ b/3/2_projection/octahedron.pas
@@ -0,0 +1,156 @@
+unit Octahedron;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls,
+ Spin, Geometry;
+
+type
+
+ { TForm1 }
+
+ TForm1 = class(TForm)
+ BackScreen: TImage;
+ Label1: TLabel;
+ ResizeField: TFloatSpinEdit;
+ 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: Real;
+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;
+ { вычисляем экранные координаты }
+ Xs := Round(Form1.Screen.Width div 2 + x);
+ Ys := Round(Form1.Screen.Height div 2 + y);
+
+ 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;
+
+ Xs := Round(Form1.Screen.Width div 2 + x);
+ Ys := Round(Form1.Screen.Height div 2 + y);
+
+ Form1.BackScreen.Canvas.LineTo(Xs, Ys); { рисуем ребро }
+ end;
+end;
+
+{ TForm1 }
+
+procedure TForm1.StartStopBtnClick(Sender: TObject); { главная процедура }
+var
+ oldtime: TDateTime;
+ Model, BackModel: Object3D; { трехмерный объект }
+ Vx, Vy, Vz: Real; { значения составляющих его скорости }
+ pause: Integer;
+const
+ MSecsPerFrame = 25; { скорость работы (кадров в секунду) }
+ xa = 0.01; { скорость вращения вокруг оси Ox }
+ ya = 0.05; { скорость вращения вокруг оси Oy }
+ za = 0.08; { скорость вращения вокруг оси Oz }
+begin
+ if IsRunning then
+ begin
+ IsRunning := false;
+ StartStopBtn.Caption := 'Пуск';
+ Exit;
+ end;
+ StartStopBtn.Caption := 'Стоп';
+ IsRunning := true;
+
+ Model := LoadObject3D('octahedron.txt'); { загрузить объект }
+ ResizeShape(Model, ResizeField.Value, ResizeField.Value, ResizeField.Value);
+
+ Model.xc := 0; { начальное положение }
+ Model.yc := 0; { объекта }
+ Model.zc := 200;
+ Vx := 10; { и составляющие }
+ Vy := 15; { скорости }
+ Vz := 20;
+
+ 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); { поворот модели }
+ BackModel := CopyObject3D(Model);
+ MoveShape(Model, Vx, Vy, Vz); { переместить объект }
+
+ 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 := CopyObject3D(BackModel);
+ 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.
+
diff --git a/3/2_projection/octahedron.txt b/3/2_projection/octahedron.txt
new file mode 100644
index 0000000..770d229
--- /dev/null
+++ b/3/2_projection/octahedron.txt
@@ -0,0 +1,20 @@
+6
+-100 -100 0
+-100 100 0
+100 100 0
+100 -100 0
+0 0 -141.42
+0 0 141.42
+12
+0 1
+1 2
+2 3
+3 0
+4 0
+4 1
+4 2
+4 3
+5 0
+5 1
+5 2
+5 3
diff --git a/3/3_random/geometry.pas b/3/3_random/geometry.pas
new file mode 100644
index 0000000..cc00fd7
--- /dev/null
+++ b/3/3_random/geometry.pas
@@ -0,0 +1,302 @@
+unit Geometry;
+
+{$mode ObjFPC}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils;
+
+type
+ { Вершины }
+ Vertex = record
+ x, y, z: Real; { Координаты вершины }
+ end;
+ { Ребра }
+ Edge = record
+ src, dest: Integer; { Индексы соединяемых вершин }
+ end;
+
+ Object3D = record
+ vertices: array of Vertex; { вершины }
+ edges: array of Edge; { ребра }
+ xc, yc, zc: Real; { координаты центра объекта }
+ end;
+
+ Matrix = array[1..4, 1..4] of Real; { матрица 4x4 }
+ Column = array[1..4] of Real; { столбец }
+
+function LoadObject3D(filename: String): Object3D;
+function CopyObject3D(Shape: Object3D): Object3D;
+
+{ умножение матрицы на матрицу }
+function MMMult(lhs, rhs: Matrix): Matrix;
+{ умножение матрицы на столбец }
+function MCMult(lhs: Matrix; rhs: Column): Column;
+{ матрица поворота модели }
+function RotateMatrix(xa, ya, za: Real): Matrix;
+function MoveMatrix(tx, ty, tz: Real): Matrix;
+function ResizeMatrix(sx, sy, sz: Real): Matrix;
+procedure RotateShape(var Shape: Object3D; xa, ya, za: Real);
+procedure MoveShape(var Shape: Object3D; tx, ty, tz: Real);
+procedure ResizeShape(var Shape: Object3D; sx, sy, sz: Real);
+
+implementation
+
+function LoadObject3D(filename: String): Object3D;
+var
+ F: TextFile; { исходный файл }
+ NV, NE: Integer; { количество вершин, количество ребер }
+ Shape: Object3D; { модель }
+ i: Integer; { счетчик цикла }
+begin
+ AssignFile(F, filename); { открываем исходный файл }
+ FileMode := 0; { в режиме "только чтение" }
+ Reset(F);
+
+ ReadLn(F, NV); { считываем количество вершин }
+ SetLength(Shape.vertices, NV); { и корректируем размер массива }
+ for i := 0 to NV - 1 do { считываем каждую вершину }
+ ReadLn(F, Shape.vertices[i].x, Shape.vertices[i].y, Shape.vertices[i].z);
+
+ ReadLn(F, NE); { то же для ребер }
+ SetLength(Shape.edges, NE);
+ for i := 0 to NE - 1 do
+ ReadLn(F, Shape.edges[i].src, Shape.edges[i].dest);
+
+ CloseFile(F);
+ LoadObject3D := Shape;
+end;
+
+function CopyObject3D(Shape: Object3D): Object3D;
+begin
+ CopyObject3D.vertices := Copy(Shape.vertices, 0);
+ CopyObject3D.edges := Copy(Shape.edges, 0);
+
+ CopyObject3D.xc := Shape.xc;
+ CopyObject3D.yc := Shape.yc;
+ CopyObject3D.zc := Shape.zc
+end;
+
+function MMMult(lhs, rhs: Matrix): Matrix;
+var
+ i, j, k: Integer;
+ r: Matrix;
+ s: Real;
+begin
+ for i := 1 to 4 do
+ for j := 1 to 4 do
+ begin
+ s := 0;
+ for k := 1 to 4 do
+ s := s + lhs[i, k] * rhs[k, j];
+ r[i, j] := s;
+ end;
+ MMMult := r;
+end;
+
+function MCMult(lhs: Matrix; rhs: Column): Column;
+var
+ k, i: Integer;
+ s: Real;
+ r: Column;
+begin
+ for i := 1 to 4 do { аналогично MMMult }
+ begin
+ s := 0;
+
+ for k := 1 to 4 do
+ s := s + lhs[i, k] * rhs[k];
+ r[i] := s;
+ end;
+
+ MCMult := r;
+end;
+
+function RotateMatrix(xa, ya, za: Real): Matrix;
+var
+ xr, yr, zr: Matrix;
+begin
+ { матрица поворота вокруг оси Ox }
+ xr[1, 1] := 1;
+ xr[1, 2] := 0;
+ xr[1, 3] := 0;
+ xr[1, 4] := 0;
+
+ xr[2, 1] := 0;
+ xr[2, 2] := Cos(xa);
+ xr[2, 3] := -Sin(xa);
+ xr[2, 4] := 0;
+
+ xr[3, 1] := 0;
+ xr[3, 2] := Sin(xa);
+ xr[3, 3] := Cos(xa);
+ xr[3, 4] := 0;
+
+ xr[4, 1] := 0;
+ xr[4, 2] := 0;
+ xr[4, 3] := 0;
+ xr[4, 4] := 1;
+
+ { матрица поворота вокруг оси Oy }
+ yr[1, 1] := Cos(ya);
+ yr[1, 2] := 0;
+ yr[1, 3] := Sin(ya);
+ yr[1, 4] := 0;
+
+ yr[2, 1] := 0;
+ yr[2, 2] := 1;
+ yr[2, 3] := 0;
+ yr[2, 4] := 0;
+
+ yr[3, 1] := -Sin(ya);
+ yr[3, 2] := 0;
+ yr[3, 3] := Cos(ya);
+ yr[3, 4] := 0;
+
+ yr[4, 1] := 0;
+ yr[4, 2] := 0;
+ yr[4, 3] := 0;
+ yr[4, 4] := 1;
+
+ { матрица поворота вокруг оси Oz }
+ zr[1, 1] := Cos(za);
+ zr[1, 2] := -Sin(za);
+ zr[1, 3] := 0;
+ zr[1, 4] := 0;
+
+ zr[2, 1] := Sin(za);
+ zr[2, 2] := Cos(za);
+ zr[2, 3] := 0;
+ zr[2, 4] := 0;
+
+ zr[3, 1] := 0;
+ zr[3, 2] := 0;
+ zr[3, 3] := 1;
+ zr[3, 4] := 0;
+
+ zr[4, 1] := 0;
+ zr[4, 2] := 0;
+ zr[4, 3] := 0;
+ zr[4, 4] := 1;
+
+ RotateMatrix := MMMult(MMMult(xr, yr), zr)
+end;
+
+function MoveMatrix(tx, ty, tz: Real): Matrix;
+begin
+ MoveMatrix[1, 1] := 1;
+ MoveMatrix[1, 2] := 0;
+ MoveMatrix[1, 3] := 0;
+ MoveMatrix[1, 4] := tx;
+
+ MoveMatrix[2, 1] := 0;
+ MoveMatrix[2, 2] := 1;
+ MoveMatrix[2, 3] := 0;
+ MoveMatrix[2, 4] := ty;
+
+ MoveMatrix[3, 1] := 0;
+ MoveMatrix[3, 2] := 0;
+ MoveMatrix[3, 3] := 1;
+ MoveMatrix[3, 4] := tz;
+
+ MoveMatrix[4, 1] := 0;
+ MoveMatrix[4, 2] := 0;
+ MoveMatrix[4, 3] := 0;
+ MoveMatrix[4, 4] := 1
+end;
+
+function ResizeMatrix(sx, sy, sz: Real): Matrix;
+begin
+ ResizeMatrix[1, 1] := sx;
+ ResizeMatrix[1, 2] := 0;
+ ResizeMatrix[1, 3] := 0;
+ ResizeMatrix[1, 4] := 0;
+
+ ResizeMatrix[2, 1] := 0;
+ ResizeMatrix[2, 2] := sy;
+ ResizeMatrix[2, 3] := 0;
+ ResizeMatrix[2, 4] := 0;
+
+ ResizeMatrix[3, 1] := 0;
+ ResizeMatrix[3, 2] := 0;
+ ResizeMatrix[3, 3] := sz;
+ ResizeMatrix[3, 4] := 0;
+
+ ResizeMatrix[4, 1] := 0;
+ ResizeMatrix[4, 2] := 0;
+ ResizeMatrix[4, 3] := 0;
+ ResizeMatrix[4, 4] := 1
+end;
+
+procedure RotateShape(var Shape: Object3D; xa, ya, za: Real);
+var
+ rm: Matrix;
+ i: Integer;
+ c: Column;
+begin
+ rm := RotateMatrix(xa, ya, za); { сгенерировать матрицу вращения }
+ c[4] := 1; { последний элемент столбца всегда равен единице }
+
+ for i := 0 to High(Shape.vertices) do { цикл по всем вершинам }
+ begin { High(a) возвращает верхний индекс массива a }
+ c[1] := Shape.vertices[i].x; { инициализация столбца }
+ c[2] := Shape.vertices[i].y;
+ c[3] := Shape.vertices[i].z;
+
+ c := MCMult(rm, c); { вызов преобразования }
+
+ Shape.vertices[i].x := c[1]; { внесение изменений в модель }
+ Shape.vertices[i].y := c[2]; { в соответствии с полученным }
+ Shape.vertices[i].z := c[3] { результатом преобразования }
+ end
+end;
+
+procedure MoveShape(var Shape: Object3D; tx, ty, tz: Real);
+var
+ mm: Matrix;
+ i: Integer;
+ c: Column;
+begin
+ mm := MoveMatrix(tx, ty, tz);
+ c[4] := 1;
+
+ for i := 0 to High(Shape.vertices) do
+ begin
+ c[1] := Shape.vertices[i].x;
+ c[2] := Shape.vertices[i].y;
+ c[3] := Shape.vertices[i].z;
+
+ c := MCMult(mm, c);
+
+ Shape.vertices[i].x := c[1];
+ Shape.vertices[i].y := c[2];
+ Shape.vertices[i].z := c[3]
+ end
+end;
+
+procedure ResizeShape(var Shape: Object3D; sx, sy, sz: Real);
+var
+ mm: Matrix;
+ i: Integer;
+ c: Column;
+begin
+ mm := ResizeMatrix(sx, sy, sz);
+ c[4] := 1;
+
+ for i := 0 to High(Shape.vertices) do
+ begin
+ c[1] := Shape.vertices[i].x;
+ c[2] := Shape.vertices[i].y;
+ c[3] := Shape.vertices[i].z;
+
+ c := MCMult(mm, c);
+
+ Shape.vertices[i].x := c[1];
+ Shape.vertices[i].y := c[2];
+ Shape.vertices[i].z := c[3]
+ end
+end;
+
+end.
+
diff --git a/3/3_random/graphics_3d.lpi b/3/3_random/graphics_3d.lpi
new file mode 100644
index 0000000..67fa0db
--- /dev/null
+++ b/3/3_random/graphics_3d.lpi
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
diff --git a/3/3_random/graphics_3d.lpr b/3/3_random/graphics_3d.lpr
new file mode 100644
index 0000000..3778580
--- /dev/null
+++ b/3/3_random/graphics_3d.lpr
@@ -0,0 +1,29 @@
+program graphics_3d;
+
+{$mode objfpc}{$H+}
+
+uses
+ {$IFDEF UNIX}
+ cthreads,
+ {$ENDIF}
+ {$IFDEF HASAMIGA}
+ athreads,
+ {$ENDIF}
+ Interfaces, // this includes the LCL widgetset
+ Forms, Octahedron, geometry
+ { you can add units after this };
+
+{$R *.res}
+
+begin
+ Randomize;
+ RequireDerivedFormResource:=True;
+ Application.Scaled:=True;
+ {$PUSH}{$WARN 5044 OFF}
+ Application.MainFormOnTaskbar:=True;
+ {$POP}
+ Application.Initialize;
+ Application.CreateForm(TForm1, Form1);
+ Application.Run;
+end.
+
diff --git a/3/3_random/graphics_3d.lps b/3/3_random/graphics_3d.lps
new file mode 100644
index 0000000..9c5a149
--- /dev/null
+++ b/3/3_random/graphics_3d.lps
@@ -0,0 +1,180 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/3/3_random/octahedron.lfm b/3/3_random/octahedron.lfm
new file mode 100644
index 0000000..c020ad3
--- /dev/null
+++ b/3/3_random/octahedron.lfm
@@ -0,0 +1,53 @@
+object Form1: TForm1
+ Left = 352
+ Height = 673
+ Top = 32
+ Width = 600
+ Caption = 'Form1'
+ ClientHeight = 673
+ ClientWidth = 600
+ DesignTimePPI = 120
+ object BackScreen: TImage
+ AnchorSideLeft.Control = Owner
+ AnchorSideTop.Control = Owner
+ AnchorSideRight.Control = Owner
+ AnchorSideRight.Side = asrBottom
+ AnchorSideBottom.Control = StartStopBtn
+ Left = 0
+ Height = 600
+ Top = 0
+ Width = 600
+ Anchors = [akTop, akLeft, akRight, akBottom]
+ BorderSpacing.Bottom = 15
+ end
+ object Screen: TPaintBox
+ AnchorSideLeft.Control = Owner
+ AnchorSideTop.Control = Owner
+ AnchorSideRight.Control = Owner
+ AnchorSideRight.Side = asrBottom
+ AnchorSideBottom.Control = StartStopBtn
+ Left = 0
+ Height = 600
+ Top = 0
+ Width = 600
+ Anchors = [akTop, akLeft, akRight, akBottom]
+ BorderSpacing.Bottom = 15
+ end
+ object StartStopBtn: TButton
+ AnchorSideLeft.Control = Owner
+ AnchorSideLeft.Side = asrCenter
+ AnchorSideRight.Control = Owner
+ AnchorSideRight.Side = asrCenter
+ AnchorSideBottom.Control = Owner
+ AnchorSideBottom.Side = asrBottom
+ Left = 241
+ Height = 48
+ Top = 615
+ Width = 118
+ Anchors = [akLeft, akBottom]
+ BorderSpacing.Bottom = 10
+ Caption = 'Пуск'
+ TabOrder = 0
+ OnClick = StartStopBtnClick
+ end
+end
diff --git a/3/3_random/octahedron.pas b/3/3_random/octahedron.pas
new file mode 100644
index 0000000..7bb06e1
--- /dev/null
+++ b/3/3_random/octahedron.pas
@@ -0,0 +1,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.
+
diff --git a/3/3_random/octahedron.txt b/3/3_random/octahedron.txt
new file mode 100644
index 0000000..770d229
--- /dev/null
+++ b/3/3_random/octahedron.txt
@@ -0,0 +1,20 @@
+6
+-100 -100 0
+-100 100 0
+100 100 0
+100 -100 0
+0 0 -141.42
+0 0 141.42
+12
+0 1
+1 2
+2 3
+3 0
+4 0
+4 1
+4 2
+4 3
+5 0
+5 1
+5 2
+5 3
diff --git a/3/4_cube/geometry.pas b/3/4_cube/geometry.pas
new file mode 100644
index 0000000..cc00fd7
--- /dev/null
+++ b/3/4_cube/geometry.pas
@@ -0,0 +1,302 @@
+unit Geometry;
+
+{$mode ObjFPC}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils;
+
+type
+ { Вершины }
+ Vertex = record
+ x, y, z: Real; { Координаты вершины }
+ end;
+ { Ребра }
+ Edge = record
+ src, dest: Integer; { Индексы соединяемых вершин }
+ end;
+
+ Object3D = record
+ vertices: array of Vertex; { вершины }
+ edges: array of Edge; { ребра }
+ xc, yc, zc: Real; { координаты центра объекта }
+ end;
+
+ Matrix = array[1..4, 1..4] of Real; { матрица 4x4 }
+ Column = array[1..4] of Real; { столбец }
+
+function LoadObject3D(filename: String): Object3D;
+function CopyObject3D(Shape: Object3D): Object3D;
+
+{ умножение матрицы на матрицу }
+function MMMult(lhs, rhs: Matrix): Matrix;
+{ умножение матрицы на столбец }
+function MCMult(lhs: Matrix; rhs: Column): Column;
+{ матрица поворота модели }
+function RotateMatrix(xa, ya, za: Real): Matrix;
+function MoveMatrix(tx, ty, tz: Real): Matrix;
+function ResizeMatrix(sx, sy, sz: Real): Matrix;
+procedure RotateShape(var Shape: Object3D; xa, ya, za: Real);
+procedure MoveShape(var Shape: Object3D; tx, ty, tz: Real);
+procedure ResizeShape(var Shape: Object3D; sx, sy, sz: Real);
+
+implementation
+
+function LoadObject3D(filename: String): Object3D;
+var
+ F: TextFile; { исходный файл }
+ NV, NE: Integer; { количество вершин, количество ребер }
+ Shape: Object3D; { модель }
+ i: Integer; { счетчик цикла }
+begin
+ AssignFile(F, filename); { открываем исходный файл }
+ FileMode := 0; { в режиме "только чтение" }
+ Reset(F);
+
+ ReadLn(F, NV); { считываем количество вершин }
+ SetLength(Shape.vertices, NV); { и корректируем размер массива }
+ for i := 0 to NV - 1 do { считываем каждую вершину }
+ ReadLn(F, Shape.vertices[i].x, Shape.vertices[i].y, Shape.vertices[i].z);
+
+ ReadLn(F, NE); { то же для ребер }
+ SetLength(Shape.edges, NE);
+ for i := 0 to NE - 1 do
+ ReadLn(F, Shape.edges[i].src, Shape.edges[i].dest);
+
+ CloseFile(F);
+ LoadObject3D := Shape;
+end;
+
+function CopyObject3D(Shape: Object3D): Object3D;
+begin
+ CopyObject3D.vertices := Copy(Shape.vertices, 0);
+ CopyObject3D.edges := Copy(Shape.edges, 0);
+
+ CopyObject3D.xc := Shape.xc;
+ CopyObject3D.yc := Shape.yc;
+ CopyObject3D.zc := Shape.zc
+end;
+
+function MMMult(lhs, rhs: Matrix): Matrix;
+var
+ i, j, k: Integer;
+ r: Matrix;
+ s: Real;
+begin
+ for i := 1 to 4 do
+ for j := 1 to 4 do
+ begin
+ s := 0;
+ for k := 1 to 4 do
+ s := s + lhs[i, k] * rhs[k, j];
+ r[i, j] := s;
+ end;
+ MMMult := r;
+end;
+
+function MCMult(lhs: Matrix; rhs: Column): Column;
+var
+ k, i: Integer;
+ s: Real;
+ r: Column;
+begin
+ for i := 1 to 4 do { аналогично MMMult }
+ begin
+ s := 0;
+
+ for k := 1 to 4 do
+ s := s + lhs[i, k] * rhs[k];
+ r[i] := s;
+ end;
+
+ MCMult := r;
+end;
+
+function RotateMatrix(xa, ya, za: Real): Matrix;
+var
+ xr, yr, zr: Matrix;
+begin
+ { матрица поворота вокруг оси Ox }
+ xr[1, 1] := 1;
+ xr[1, 2] := 0;
+ xr[1, 3] := 0;
+ xr[1, 4] := 0;
+
+ xr[2, 1] := 0;
+ xr[2, 2] := Cos(xa);
+ xr[2, 3] := -Sin(xa);
+ xr[2, 4] := 0;
+
+ xr[3, 1] := 0;
+ xr[3, 2] := Sin(xa);
+ xr[3, 3] := Cos(xa);
+ xr[3, 4] := 0;
+
+ xr[4, 1] := 0;
+ xr[4, 2] := 0;
+ xr[4, 3] := 0;
+ xr[4, 4] := 1;
+
+ { матрица поворота вокруг оси Oy }
+ yr[1, 1] := Cos(ya);
+ yr[1, 2] := 0;
+ yr[1, 3] := Sin(ya);
+ yr[1, 4] := 0;
+
+ yr[2, 1] := 0;
+ yr[2, 2] := 1;
+ yr[2, 3] := 0;
+ yr[2, 4] := 0;
+
+ yr[3, 1] := -Sin(ya);
+ yr[3, 2] := 0;
+ yr[3, 3] := Cos(ya);
+ yr[3, 4] := 0;
+
+ yr[4, 1] := 0;
+ yr[4, 2] := 0;
+ yr[4, 3] := 0;
+ yr[4, 4] := 1;
+
+ { матрица поворота вокруг оси Oz }
+ zr[1, 1] := Cos(za);
+ zr[1, 2] := -Sin(za);
+ zr[1, 3] := 0;
+ zr[1, 4] := 0;
+
+ zr[2, 1] := Sin(za);
+ zr[2, 2] := Cos(za);
+ zr[2, 3] := 0;
+ zr[2, 4] := 0;
+
+ zr[3, 1] := 0;
+ zr[3, 2] := 0;
+ zr[3, 3] := 1;
+ zr[3, 4] := 0;
+
+ zr[4, 1] := 0;
+ zr[4, 2] := 0;
+ zr[4, 3] := 0;
+ zr[4, 4] := 1;
+
+ RotateMatrix := MMMult(MMMult(xr, yr), zr)
+end;
+
+function MoveMatrix(tx, ty, tz: Real): Matrix;
+begin
+ MoveMatrix[1, 1] := 1;
+ MoveMatrix[1, 2] := 0;
+ MoveMatrix[1, 3] := 0;
+ MoveMatrix[1, 4] := tx;
+
+ MoveMatrix[2, 1] := 0;
+ MoveMatrix[2, 2] := 1;
+ MoveMatrix[2, 3] := 0;
+ MoveMatrix[2, 4] := ty;
+
+ MoveMatrix[3, 1] := 0;
+ MoveMatrix[3, 2] := 0;
+ MoveMatrix[3, 3] := 1;
+ MoveMatrix[3, 4] := tz;
+
+ MoveMatrix[4, 1] := 0;
+ MoveMatrix[4, 2] := 0;
+ MoveMatrix[4, 3] := 0;
+ MoveMatrix[4, 4] := 1
+end;
+
+function ResizeMatrix(sx, sy, sz: Real): Matrix;
+begin
+ ResizeMatrix[1, 1] := sx;
+ ResizeMatrix[1, 2] := 0;
+ ResizeMatrix[1, 3] := 0;
+ ResizeMatrix[1, 4] := 0;
+
+ ResizeMatrix[2, 1] := 0;
+ ResizeMatrix[2, 2] := sy;
+ ResizeMatrix[2, 3] := 0;
+ ResizeMatrix[2, 4] := 0;
+
+ ResizeMatrix[3, 1] := 0;
+ ResizeMatrix[3, 2] := 0;
+ ResizeMatrix[3, 3] := sz;
+ ResizeMatrix[3, 4] := 0;
+
+ ResizeMatrix[4, 1] := 0;
+ ResizeMatrix[4, 2] := 0;
+ ResizeMatrix[4, 3] := 0;
+ ResizeMatrix[4, 4] := 1
+end;
+
+procedure RotateShape(var Shape: Object3D; xa, ya, za: Real);
+var
+ rm: Matrix;
+ i: Integer;
+ c: Column;
+begin
+ rm := RotateMatrix(xa, ya, za); { сгенерировать матрицу вращения }
+ c[4] := 1; { последний элемент столбца всегда равен единице }
+
+ for i := 0 to High(Shape.vertices) do { цикл по всем вершинам }
+ begin { High(a) возвращает верхний индекс массива a }
+ c[1] := Shape.vertices[i].x; { инициализация столбца }
+ c[2] := Shape.vertices[i].y;
+ c[3] := Shape.vertices[i].z;
+
+ c := MCMult(rm, c); { вызов преобразования }
+
+ Shape.vertices[i].x := c[1]; { внесение изменений в модель }
+ Shape.vertices[i].y := c[2]; { в соответствии с полученным }
+ Shape.vertices[i].z := c[3] { результатом преобразования }
+ end
+end;
+
+procedure MoveShape(var Shape: Object3D; tx, ty, tz: Real);
+var
+ mm: Matrix;
+ i: Integer;
+ c: Column;
+begin
+ mm := MoveMatrix(tx, ty, tz);
+ c[4] := 1;
+
+ for i := 0 to High(Shape.vertices) do
+ begin
+ c[1] := Shape.vertices[i].x;
+ c[2] := Shape.vertices[i].y;
+ c[3] := Shape.vertices[i].z;
+
+ c := MCMult(mm, c);
+
+ Shape.vertices[i].x := c[1];
+ Shape.vertices[i].y := c[2];
+ Shape.vertices[i].z := c[3]
+ end
+end;
+
+procedure ResizeShape(var Shape: Object3D; sx, sy, sz: Real);
+var
+ mm: Matrix;
+ i: Integer;
+ c: Column;
+begin
+ mm := ResizeMatrix(sx, sy, sz);
+ c[4] := 1;
+
+ for i := 0 to High(Shape.vertices) do
+ begin
+ c[1] := Shape.vertices[i].x;
+ c[2] := Shape.vertices[i].y;
+ c[3] := Shape.vertices[i].z;
+
+ c := MCMult(mm, c);
+
+ Shape.vertices[i].x := c[1];
+ Shape.vertices[i].y := c[2];
+ Shape.vertices[i].z := c[3]
+ end
+end;
+
+end.
+
diff --git a/3/4_cube/graphics_3d.lpi b/3/4_cube/graphics_3d.lpi
new file mode 100644
index 0000000..67fa0db
--- /dev/null
+++ b/3/4_cube/graphics_3d.lpi
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
diff --git a/3/4_cube/graphics_3d.lpr b/3/4_cube/graphics_3d.lpr
new file mode 100644
index 0000000..3778580
--- /dev/null
+++ b/3/4_cube/graphics_3d.lpr
@@ -0,0 +1,29 @@
+program graphics_3d;
+
+{$mode objfpc}{$H+}
+
+uses
+ {$IFDEF UNIX}
+ cthreads,
+ {$ENDIF}
+ {$IFDEF HASAMIGA}
+ athreads,
+ {$ENDIF}
+ Interfaces, // this includes the LCL widgetset
+ Forms, Octahedron, geometry
+ { you can add units after this };
+
+{$R *.res}
+
+begin
+ Randomize;
+ RequireDerivedFormResource:=True;
+ Application.Scaled:=True;
+ {$PUSH}{$WARN 5044 OFF}
+ Application.MainFormOnTaskbar:=True;
+ {$POP}
+ Application.Initialize;
+ Application.CreateForm(TForm1, Form1);
+ Application.Run;
+end.
+
diff --git a/3/4_cube/graphics_3d.lps b/3/4_cube/graphics_3d.lps
new file mode 100644
index 0000000..d92f2c8
--- /dev/null
+++ b/3/4_cube/graphics_3d.lps
@@ -0,0 +1,180 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/3/4_cube/octahedron.lfm b/3/4_cube/octahedron.lfm
new file mode 100644
index 0000000..c020ad3
--- /dev/null
+++ b/3/4_cube/octahedron.lfm
@@ -0,0 +1,53 @@
+object Form1: TForm1
+ Left = 352
+ Height = 673
+ Top = 32
+ Width = 600
+ Caption = 'Form1'
+ ClientHeight = 673
+ ClientWidth = 600
+ DesignTimePPI = 120
+ object BackScreen: TImage
+ AnchorSideLeft.Control = Owner
+ AnchorSideTop.Control = Owner
+ AnchorSideRight.Control = Owner
+ AnchorSideRight.Side = asrBottom
+ AnchorSideBottom.Control = StartStopBtn
+ Left = 0
+ Height = 600
+ Top = 0
+ Width = 600
+ Anchors = [akTop, akLeft, akRight, akBottom]
+ BorderSpacing.Bottom = 15
+ end
+ object Screen: TPaintBox
+ AnchorSideLeft.Control = Owner
+ AnchorSideTop.Control = Owner
+ AnchorSideRight.Control = Owner
+ AnchorSideRight.Side = asrBottom
+ AnchorSideBottom.Control = StartStopBtn
+ Left = 0
+ Height = 600
+ Top = 0
+ Width = 600
+ Anchors = [akTop, akLeft, akRight, akBottom]
+ BorderSpacing.Bottom = 15
+ end
+ object StartStopBtn: TButton
+ AnchorSideLeft.Control = Owner
+ AnchorSideLeft.Side = asrCenter
+ AnchorSideRight.Control = Owner
+ AnchorSideRight.Side = asrCenter
+ AnchorSideBottom.Control = Owner
+ AnchorSideBottom.Side = asrBottom
+ Left = 241
+ Height = 48
+ Top = 615
+ Width = 118
+ Anchors = [akLeft, akBottom]
+ BorderSpacing.Bottom = 10
+ Caption = 'Пуск'
+ TabOrder = 0
+ OnClick = StartStopBtnClick
+ end
+end
diff --git a/3/4_cube/octahedron.pas b/3/4_cube/octahedron.pas
new file mode 100644
index 0000000..7bb06e1
--- /dev/null
+++ b/3/4_cube/octahedron.pas
@@ -0,0 +1,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.
+
diff --git a/3/4_cube/octahedron.txt b/3/4_cube/octahedron.txt
new file mode 100644
index 0000000..c47a3b7
--- /dev/null
+++ b/3/4_cube/octahedron.txt
@@ -0,0 +1,22 @@
+8
+-100 -100 -100
+-100 100 -100
+100 100 -100
+100 -100 -100
+-100 -100 100
+-100 100 100
+100 100 100
+100 -100 100
+12
+0 1
+1 2
+2 3
+3 0
+4 5
+5 6
+6 7
+7 4
+0 4
+1 5
+2 6
+3 7
diff --git a/3/5_ideal_gas/geometry.pas b/3/5_ideal_gas/geometry.pas
new file mode 100644
index 0000000..cbbff5d
--- /dev/null
+++ b/3/5_ideal_gas/geometry.pas
@@ -0,0 +1,344 @@
+unit Geometry;
+
+{$mode ObjFPC}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils;
+
+type
+ { Вершины }
+ Vertex = record
+ x, y, z: Real; { Координаты вершины }
+ end;
+ { Ребра }
+ Edge = record
+ src, dest: Integer; { Индексы соединяемых вершин }
+ end;
+
+ Object3D = class
+ public
+ vertices: array of Vertex; { вершины }
+ edges: array of Edge; { ребра }
+ xc, yc, zc: Real; { координаты центра объекта }
+ vx, vy, vz: Real; { значения составляющих его скорости }
+ xa, ya, za: Real; { скорость вращения вокруг оси }
+
+ constructor Create(var center, velocity, axis: Vertex);
+
+ procedure MoveCenter();
+ end;
+
+ Matrix = array[1..4, 1..4] of Real; { матрица 4x4 }
+ Column = array[1..4] of Real; { столбец }
+
+function CopyObject3D(Shape: Object3D): Object3D;
+
+{ умножение матрицы на матрицу }
+function MMMult(lhs, rhs: Matrix): Matrix;
+{ умножение матрицы на столбец }
+function MCMult(lhs: Matrix; rhs: Column): Column;
+{ матрица поворота модели }
+function RotateMatrix(xa, ya, za: Real): Matrix;
+function MoveMatrix(tx, ty, tz: Real): Matrix;
+function ResizeMatrix(sx, sy, sz: Real): Matrix;
+procedure RotateShape(Shape: Object3D);
+procedure MoveShape(Shape: Object3D; Width, Height: Real);
+procedure ResizeShape(var Shape: Object3D; sx, sy, sz: Real);
+
+implementation
+
+constructor Object3D.Create(var center, velocity, axis: Vertex);
+const
+ size = 10;
+ cubeVertices: array of Vertex = (
+ (x: -size; y: -size; z: -size),
+ (x: -size; y: size; z: -size),
+ (x: size; y: size; z: -size),
+ (x: size; y: -size; z: -size),
+ (x: -size; y: -size; z: size),
+ (x: -size; y: size; z: size),
+ (x: size; y: size; z: size),
+ (x: size; y: -size; z: size)
+ );
+ cubeEdges: array of Edge = (
+ (src: 0; dest: 1),
+ (src: 1; dest: 2),
+ (src: 2; dest: 3),
+ (src: 3; dest: 0),
+ (src: 4; dest: 5),
+ (src: 5; dest: 6),
+ (src: 6; dest: 7),
+ (src: 7; dest: 4),
+ (src: 0; dest: 4),
+ (src: 1; dest: 5),
+ (src: 2; dest: 6),
+ (src: 3; dest: 7)
+ );
+begin
+ vertices := cubeVertices;
+ edges := cubeEdges;
+ xc := center.x;
+ yc := center.y;
+ zc := center.z;
+ vx := velocity.x;
+ vy := velocity.y;
+ vz := velocity.z;
+ xa := axis.x;
+ ya := axis.y;
+ za := axis.z
+end;
+
+procedure Object3D.MoveCenter();
+begin
+ xc := xc + Vx;
+ yc := yc + Vy;
+ zc := zc + Vz
+end;
+
+function CopyObject3D(Shape: Object3D): Object3D;
+begin
+ CopyObject3D.vertices := Copy(Shape.vertices, 0);
+ CopyObject3D.edges := Copy(Shape.edges, 0);
+
+ CopyObject3D.xc := Shape.xc;
+ CopyObject3D.yc := Shape.yc;
+ CopyObject3D.zc := Shape.zc
+end;
+
+function MMMult(lhs, rhs: Matrix): Matrix;
+var
+ i, j, k: Integer;
+ r: Matrix;
+ s: Real;
+begin
+ for i := 1 to 4 do
+ for j := 1 to 4 do
+ begin
+ s := 0;
+ for k := 1 to 4 do
+ s := s + lhs[i, k] * rhs[k, j];
+ r[i, j] := s;
+ end;
+ MMMult := r;
+end;
+
+function MCMult(lhs: Matrix; rhs: Column): Column;
+var
+ k, i: Integer;
+ s: Real;
+ r: Column;
+begin
+ for i := 1 to 4 do { аналогично MMMult }
+ begin
+ s := 0;
+
+ for k := 1 to 4 do
+ s := s + lhs[i, k] * rhs[k];
+ r[i] := s;
+ end;
+
+ MCMult := r;
+end;
+
+function RotateMatrix(xa, ya, za: Real): Matrix;
+var
+ xr, yr, zr: Matrix;
+begin
+ { матрица поворота вокруг оси Ox }
+ xr[1, 1] := 1;
+ xr[1, 2] := 0;
+ xr[1, 3] := 0;
+ xr[1, 4] := 0;
+
+ xr[2, 1] := 0;
+ xr[2, 2] := Cos(xa);
+ xr[2, 3] := -Sin(xa);
+ xr[2, 4] := 0;
+
+ xr[3, 1] := 0;
+ xr[3, 2] := Sin(xa);
+ xr[3, 3] := Cos(xa);
+ xr[3, 4] := 0;
+
+ xr[4, 1] := 0;
+ xr[4, 2] := 0;
+ xr[4, 3] := 0;
+ xr[4, 4] := 1;
+
+ { матрица поворота вокруг оси Oy }
+ yr[1, 1] := Cos(ya);
+ yr[1, 2] := 0;
+ yr[1, 3] := Sin(ya);
+ yr[1, 4] := 0;
+
+ yr[2, 1] := 0;
+ yr[2, 2] := 1;
+ yr[2, 3] := 0;
+ yr[2, 4] := 0;
+
+ yr[3, 1] := -Sin(ya);
+ yr[3, 2] := 0;
+ yr[3, 3] := Cos(ya);
+ yr[3, 4] := 0;
+
+ yr[4, 1] := 0;
+ yr[4, 2] := 0;
+ yr[4, 3] := 0;
+ yr[4, 4] := 1;
+
+ { матрица поворота вокруг оси Oz }
+ zr[1, 1] := Cos(za);
+ zr[1, 2] := -Sin(za);
+ zr[1, 3] := 0;
+ zr[1, 4] := 0;
+
+ zr[2, 1] := Sin(za);
+ zr[2, 2] := Cos(za);
+ zr[2, 3] := 0;
+ zr[2, 4] := 0;
+
+ zr[3, 1] := 0;
+ zr[3, 2] := 0;
+ zr[3, 3] := 1;
+ zr[3, 4] := 0;
+
+ zr[4, 1] := 0;
+ zr[4, 2] := 0;
+ zr[4, 3] := 0;
+ zr[4, 4] := 1;
+
+ RotateMatrix := MMMult(MMMult(xr, yr), zr)
+end;
+
+function MoveMatrix(tx, ty, tz: Real): Matrix;
+begin
+ MoveMatrix[1, 1] := 1;
+ MoveMatrix[1, 2] := 0;
+ MoveMatrix[1, 3] := 0;
+ MoveMatrix[1, 4] := tx;
+
+ MoveMatrix[2, 1] := 0;
+ MoveMatrix[2, 2] := 1;
+ MoveMatrix[2, 3] := 0;
+ MoveMatrix[2, 4] := ty;
+
+ MoveMatrix[3, 1] := 0;
+ MoveMatrix[3, 2] := 0;
+ MoveMatrix[3, 3] := 1;
+ MoveMatrix[3, 4] := tz;
+
+ MoveMatrix[4, 1] := 0;
+ MoveMatrix[4, 2] := 0;
+ MoveMatrix[4, 3] := 0;
+ MoveMatrix[4, 4] := 1
+end;
+
+function ResizeMatrix(sx, sy, sz: Real): Matrix;
+begin
+ ResizeMatrix[1, 1] := sx;
+ ResizeMatrix[1, 2] := 0;
+ ResizeMatrix[1, 3] := 0;
+ ResizeMatrix[1, 4] := 0;
+
+ ResizeMatrix[2, 1] := 0;
+ ResizeMatrix[2, 2] := sy;
+ ResizeMatrix[2, 3] := 0;
+ ResizeMatrix[2, 4] := 0;
+
+ ResizeMatrix[3, 1] := 0;
+ ResizeMatrix[3, 2] := 0;
+ ResizeMatrix[3, 3] := sz;
+ ResizeMatrix[3, 4] := 0;
+
+ ResizeMatrix[4, 1] := 0;
+ ResizeMatrix[4, 2] := 0;
+ ResizeMatrix[4, 3] := 0;
+ ResizeMatrix[4, 4] := 1
+end;
+
+procedure RotateShape(Shape: Object3D);
+var
+ rm: Matrix;
+ i: Integer;
+ c: Column;
+begin
+ rm := RotateMatrix(Shape.xa, Shape.ya, Shape.za); { сгенерировать матрицу вращения }
+ c[4] := 1; { последний элемент столбца всегда равен единице }
+
+ for i := 0 to High(Shape.vertices) do { цикл по всем вершинам }
+ begin { High(a) возвращает верхний индекс массива a }
+ c[1] := Shape.vertices[i].x; { инициализация столбца }
+ c[2] := Shape.vertices[i].y;
+ c[3] := Shape.vertices[i].z;
+
+ c := MCMult(rm, c); { вызов преобразования }
+
+ Shape.vertices[i].x := c[1]; { внесение изменений в модель }
+ Shape.vertices[i].y := c[2]; { в соответствии с полученным }
+ Shape.vertices[i].z := c[3] { результатом преобразования }
+ end
+end;
+
+procedure MoveShape(Shape: Object3D; Width, Height: Real);
+begin
+ if Shape.xc > Width then
+ begin
+ Shape.xc := Width;
+ Shape.Vx := -Shape.Vx
+ end;
+ if Shape.xc < -Width then
+ begin
+ Shape.xc := -Width;
+ Shape.Vx := -Shape.Vx
+ end;
+
+ if Shape.yc > Height then
+ begin
+ Shape.yc := Height;
+ Shape.Vy := -Shape.Vy
+ end;
+ if Shape.yc < -Height then
+ begin
+ Shape.yc := -Height;
+ Shape.Vy := -Shape.Vy
+ end;
+
+ if Shape.zc > 500 then
+ begin
+ Shape.zc := 500;
+ Shape.Vz := -Shape.Vz
+ end;
+ if Shape.zc < 200 then
+ begin
+ Shape.zc := 200;
+ Shape.Vz := -Shape.Vz
+ end;
+end;
+
+procedure ResizeShape(var Shape: Object3D; sx, sy, sz: Real);
+var
+ mm: Matrix;
+ i: Integer;
+ c: Column;
+begin
+ mm := ResizeMatrix(sx, sy, sz);
+ c[4] := 1;
+
+ for i := 0 to High(Shape.vertices) do
+ begin
+ c[1] := Shape.vertices[i].x;
+ c[2] := Shape.vertices[i].y;
+ c[3] := Shape.vertices[i].z;
+
+ c := MCMult(mm, c);
+
+ Shape.vertices[i].x := c[1];
+ Shape.vertices[i].y := c[2];
+ Shape.vertices[i].z := c[3]
+ end
+end;
+
+end.
+
diff --git a/3/5_ideal_gas/graphics_3d.lpi b/3/5_ideal_gas/graphics_3d.lpi
new file mode 100644
index 0000000..67fa0db
--- /dev/null
+++ b/3/5_ideal_gas/graphics_3d.lpi
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
diff --git a/3/5_ideal_gas/graphics_3d.lpr b/3/5_ideal_gas/graphics_3d.lpr
new file mode 100644
index 0000000..3778580
--- /dev/null
+++ b/3/5_ideal_gas/graphics_3d.lpr
@@ -0,0 +1,29 @@
+program graphics_3d;
+
+{$mode objfpc}{$H+}
+
+uses
+ {$IFDEF UNIX}
+ cthreads,
+ {$ENDIF}
+ {$IFDEF HASAMIGA}
+ athreads,
+ {$ENDIF}
+ Interfaces, // this includes the LCL widgetset
+ Forms, Octahedron, geometry
+ { you can add units after this };
+
+{$R *.res}
+
+begin
+ Randomize;
+ RequireDerivedFormResource:=True;
+ Application.Scaled:=True;
+ {$PUSH}{$WARN 5044 OFF}
+ Application.MainFormOnTaskbar:=True;
+ {$POP}
+ Application.Initialize;
+ Application.CreateForm(TForm1, Form1);
+ Application.Run;
+end.
+
diff --git a/3/5_ideal_gas/graphics_3d.lps b/3/5_ideal_gas/graphics_3d.lps
new file mode 100644
index 0000000..d227996
--- /dev/null
+++ b/3/5_ideal_gas/graphics_3d.lps
@@ -0,0 +1,179 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/3/5_ideal_gas/octahedron.lfm b/3/5_ideal_gas/octahedron.lfm
new file mode 100644
index 0000000..8db0965
--- /dev/null
+++ b/3/5_ideal_gas/octahedron.lfm
@@ -0,0 +1,54 @@
+object Form1: TForm1
+ Left = 352
+ Height = 673
+ Top = 32
+ Width = 600
+ Caption = 'Form1'
+ ClientHeight = 673
+ ClientWidth = 600
+ DesignTimePPI = 120
+ LCLVersion = '4.2.0.0'
+ object BackScreen: TImage
+ AnchorSideLeft.Control = Owner
+ AnchorSideTop.Control = Owner
+ AnchorSideRight.Control = Owner
+ AnchorSideRight.Side = asrBottom
+ AnchorSideBottom.Control = StartStopBtn
+ Left = 0
+ Height = 600
+ Top = 0
+ Width = 600
+ Anchors = [akTop, akLeft, akRight, akBottom]
+ BorderSpacing.Bottom = 15
+ end
+ object Screen: TPaintBox
+ AnchorSideLeft.Control = Owner
+ AnchorSideTop.Control = Owner
+ AnchorSideRight.Control = Owner
+ AnchorSideRight.Side = asrBottom
+ AnchorSideBottom.Control = StartStopBtn
+ Left = 0
+ Height = 600
+ Top = 0
+ Width = 600
+ Anchors = [akTop, akLeft, akRight, akBottom]
+ BorderSpacing.Bottom = 15
+ end
+ object StartStopBtn: TButton
+ AnchorSideLeft.Control = Owner
+ AnchorSideLeft.Side = asrCenter
+ AnchorSideRight.Control = Owner
+ AnchorSideRight.Side = asrCenter
+ AnchorSideBottom.Control = Owner
+ AnchorSideBottom.Side = asrBottom
+ Left = 241
+ Height = 48
+ Top = 615
+ Width = 118
+ Anchors = [akLeft, akBottom]
+ BorderSpacing.Bottom = 10
+ Caption = 'Пуск'
+ TabOrder = 0
+ OnClick = StartStopBtnClick
+ end
+end
diff --git a/3/5_ideal_gas/octahedron.pas b/3/5_ideal_gas/octahedron.pas
new file mode 100644
index 0000000..31fe970
--- /dev/null
+++ b/3/5_ideal_gas/octahedron.pas
@@ -0,0 +1,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.
+