OpenGL в Delphi

         

Для того чтобы нарисовать отверстия в плите, пришлось потрудиться




Замечание
Использование буфера трафарета привело бы здесь к потере производительности в десятки раз. Напоминаю, что есть еще один способ решения таких задач - использование tess-объектов.

Код воспроизведения кадра, как и в предыдущем примере, становится сравнительно кратким после того, как вся система поэлементно описана в дисплейных списках:

begin // используется в case
// очистка буфера цвета и буфера глубины
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER BIT);
glPushMatrix; // запомнили текущую систему координат - 0,0
// Установочный сдвиг
glTranslatef(AddXYZ [1], AddXYZ [2], AddXYZ [3] -7.0);
glRotatef (AngleXYZ [1], 1, О, О);
glRotatef (AngleXYZ [2], 0, 1, 0);
glRotatef (AngleXYZ [3], 0, 0, 1);
If flgSquare then glCallList (1); // рисуем площадку (плоскость узла)
If flgOc then OcXYZ; // рисуем оси
If flgLight then begin // рисуем источник света
glTranslatef (PLPosition^ [1], PLPosition^ [2], PLPositaon^[3]);
gluSphere (ObjSphere, 0.01, 5, 5);
glTranslatef (-PLPosition^ [1], —PLPosition^ [2], —PLPosition^ [3]);
end;
glScalef (CoeffX, CoeffY, CoeffZ);
glTranslatef (0.0, 0.0, SmallB);
glCallList (3); // пружина
glCallList (10); // дырки в плите под болты
glCallList (5); // плита
glRotatef (AngleX, 1.0, 0.0, 0.0);
glRotatef (AngleY, 0.0, 1.0, 0.0);
glTranslatef (0.0, 0.0, Smallh);
glCallList (4); // диск
glCallList (8); // первый болт
glCallList (9); // второй болт
glRotatef (AngleZ, 0.0, 0.0, 1.0);
glCallList (2); // шпильковерт со шпинделем
glCallList (6); // патрон
glCallList (7); //деталь
glPopMatrix;
// конец работы
SwapBuffers(DC);
end;

Конечно, вызываемые подряд списки можно также объединить, и после этого код вообще уложится в десяток строк.



Дополнительные замечания


Приложению требуется несколько секунд для завершения работы. Чтобы у пользователя не сложилось впечатление, что система зависла, по окончании работы я убираю (минимизирую) окно приложения в панель задач:

postMessage(Window, WM_SYSCOMMAND, SC_MINIMIZE, 0);

Возможно, дотошный читатель обратит внимание на то, как я громоздко провожу проверку на правильность заполнения полей редактирования в модуле, реализующем диалоговое окно "Параметры системы", каждое поле редактирования контролируется отдельно.

Замечание
Здесь я сознательно не использовал операторы Delphi as и is, упрощающие и сокращающие код, поскольку их использование заметно замедляет работу приложения

Серьезным упреком к процедуре проверки может быть то, что для перевода вещественных чисел в строку и наоборот используются процедуры val и str, не учитывающие, какой разделитель дробной части установлен в системе, поэтому могут возникнуть неудобства, если пользователь сильно привык к запятой в качестве такого разделителя
При проверке содержимого каждого поля в случае допущенной пользователем ошибки применяется "тихая" исключительная ситуация - стандартный прием:

If edtFAmbientR.Text = '' then raise EAbort.Create ('Заполните все поля1');
Val (edtFAmbientR.Text, dW, iW) ;
If (iW<>0) then raise EAbort.Create ('Числовые данные введены с ошибкой1');

Обращение к процедуре проверки осуществляется в защищенном блоке, при возникновении ошибки класса EAbort пользователь получает соответствующую информацию, и попытка применить введенные значения прекращается.

try
Forml.Proverka except
on E : EAbort do With Forml do begin
TabbedNotebookl.Visible := False;
btnApply.Visible := False;
btnCancel.Visible := False;
btnOK.Visible := False;
btnError.Visible := True;
IblError.Caption := E.Message;
IblError.Visible := True;
Exit;
// ошибка, данные применять нельзя
end;// with
end; // try

Разобранную программу можно использовать в качестве шаблона для проектирования других приложений подобного типа. Подкаталог Ех05 содержит еще один проект по визуализации автоматов, также схематично демонстрирующий работу реальной установки
(Рисунок 5.6).