|
Упреждающее объявление процедур и функций (forward)Для реализации алгоритмов с косвенной рекурсией в языке Delphi предусмотрена специальная директива предварительного описания подпрограмм forward. Предварительное описание состоит из заголовка подпрограммы и следующего за ним зарезервированного слова forward, например: procedure Proc; forward; function Func(X: Integer): Boolean; forward; Заметим, что после такого первичного описания в полном описании процедуры или функции можно не указывать список формальных параметров и тип возвращаемого значения (для функции). Например: procedure Proc2(<формальные параметры>); forward;
procedure Proc1; begin ... Proc2(<фактические параметры>); ... end;
procedure Proc2; // Список формальных параметров опущен begin ... Proc1; ... end;
begin ... Proc1; ... end.
Лекция 16 Графические построения Основные понятия компьютерной графики Фактически во всех приложениях для операционной системы Windows используется графический интерфейс пользователя (GUI – graphic user interface). При этом экран компьютера работает в графическом режиме. Он разбивается на отдельные точки, называемые пикселами (от picture element). Каждая точка может окрашиваться в тот или иной цвет. Общее число точек, на которые разбивается весь экран, может меняться и зависит от имеющегося объема видеопамяти компьютера и возможностей используемого монитора. Число точек по горизонтали и вертикали экрана называется его разрешением. Например, используются разрешения 800х600, 1024х768 и другие. Почему число точек по вертикали меньше, чем по горизонтали? Дело в том, что традиционно соотношение сторон киноэкрана (неширокоформатного), телевизионного кадра, кадра на фотопленке или в цифровом фотоаппарате – 4:3. Такое же соотношение сохраняется и на компьютерных экранах. На каждый пиксел изображения операционная система отводит определенный объем видеопамяти. В настройках Windows этот параметр называется "цветовая глубина". Цветовая глубина может составлять 16, 24, 32 бита. Чем выше цветовая глубина, тем большее количество оттенков цвета может принимать каждая точка. При 16-битной цветовой глубине количество оттенков составляет 216=65536, а при глубине в 24 бита – уже 16777216. Подавляющее большинство людей просто не различает свыше 16 миллионов оттенков, поэтому дальнейшее наращивание цветовой глубины фактически бессмысленно. А каким образом разрешение зависит от типа применяемого монитора? Для мониторов на электронно-лучевой трубке важнейшим параметром является частота кадровой развертки, показывающая, сколько раз в секунду электронный луч перерисовывает картинку на экране. Чем выше частота кадровой развертки, тем меньше мерцание изображение и тем меньше устают глаза. Рекомендуется работать при частоте не ниже 85Гц, а еще лучше – 100Гц. Мониторы поддерживают разную максимальную частоту в зависимости от разрешения. Это и понятно – чем выше разрешение, тем больше точек надо перерисовывать. Например, монитор может обеспечивать частоту 100Гц при разрешении 800х600 и 75Гц – при разрешении 1024х768. Мониторы на основе ТFТ-матрицы ("плоские") имеют фиксированное разрешение, так как в них каждый пиксел является физически изготовленным светодиодом. Это крупный недостаток таких мониторов – при работе с ними разрешение можно только понизить, но в результате качество изображения резко ухудшится. Цвет каждого пиксела задается смешением в заданных пропорциях трех базовых цветов – красного (Red), зеленого (Green) и синего (Blue) (RGB-модель). При R=0, G=0, B=0 получается черный цвет, а при R=255, G=255, B=255 – белый. Значения, при которых R=G=B, соответствуют оттенкам серого. Еще одним важнейшим параметром изображения является линейное разрешение, измеряемое в количестве пикселов, приходящихся на один дюйм изображения (dpi – dots per inch). Применительно к компьютерному экрану линейное разрешение имеет "виртуальный" характер – оно не зависит от размера экрана, а определяется лишь возможностями операционной системы. Windows поддерживает экранные разрешения в 72 и 96dpi. Вообще говоря, это низкие разрешения, но они достаточны для экранного изображения. А вот при выводе на бумагу необходимо разрешение не ниже 300dpi. 16.2 Получение сведений о режимах экрана. Очевидно, что, прежде чем программировать графические задачи, необходимо знать текущее разрешение экрана, цветовую глубину и другие параметры. Для этого в каждом приложении на Delphi существует системная переменная-объект Screen типа TScreen. Объявлять ее не надо, это делается автоматически. Рассмотрим основные свойства объекта TScreen:
Width – ширина экрана в пикселах; Height – высота экрана в пикселах; DesktopWidth – ширина виртуального рабочего стола в пикселах; DesktopHeight – высота виртуального рабочего стола в пикселах; PixelsPerInch – разрешение экрана, точек на дюйм.
Чем отличаются размеры экрана от размеров виртуального рабочего стола? Если на компьютере установлен один монитор, то ничем. Если же видеокарта поддерживает несколько устройств (например, монитор и телевизор, подключаемый к выходу TV-out), то Windows создает так называемый виртуальный рабочий стол, часть которого может отображаться на одном мониторе, а часть – на другом. Размеры такого виртуального стола в пикселах могут в два раза превышать размеры экрана. Следующий фрагмент программы при нажатии на кнопку Button1 выводит в объект Label1 информацию о текущем состоянии экрана:
procedure TForm1.Button1Click(Sender: TObject); begin Label1.Caption:=IntToStr(Screen.Width)+'x'+ IntToStr(Screen.Height)+', '+ IntToStr(Screen.PixelsPerInch)+'dpi' end;
Объект "форма" (типа TForm) тоже имеет ряд свойств, относящихся к графике. Наиболее интересны из них те, что обеспечивают эффекты прозрачности всей формы или отдельных ее элементов. Следует иметь в виду, что эти эффекты работают только в операционных системах Windows2000/XP. При этом используется так называемый альфа-канал – дополнительный "невидимый" цветовой канал в дополнение к базовым красному, зеленому и синему каналам. Свойство AlphaBlend (Boolean) включает/выключает режим прозрачности всей формы, а свойство AlphaBlendValue (BYTE) задает уровень прозрачности. При AlphaBlendValue=255 форма полностью непрозрачна и отображается, как обычно, а при AlphaBlendValue=0 форма вообще невидима. Напишем следующий обработчик события создания формы OnActivate:
procedure TForm1.FormActivate(Sender: TObject);
var i:byte;
begin AlphaBlend:=True; for i:=0 to 127 do begin AlphaBlendValue:=i*2; Repaint end end;
Предварительно установим у формы значение AlphaBlendValue=0. После запуска программы будет наблюдаться интересный эффект постепенного "проявления" формы. Аналогично можно сделать постепенное "выцветание" формы при завершении работы программы. Чтобы сделать прозрачным отдельный элемент на форме, следует использовать свойства TransparentColor (Boolean) и TransparentColorValue (TColor). Если TransparentColor=True, то все элементы на форме, имеющие цвет, равный заданному в свойстве TransparentColorValue, будут прозрачными. Создадим форму с круглым прозрачным окном посередине. Поместим на форму компонент Shape с палитры компонентов Additional и установим его свойство Shape в значение stCircle. Разместим полученную окружность в центре формы и поменяем ее цвет (свойство Brush.Color) на clSkyBlue. В свойствах формы зададим TransparentColor=True и TransparentColorValue=clSkyBlue. Запустим приложение и получим очень интересный эффект (Рис. 1).
Рис. 1. Форма с прозрачной областью. Графические построения Чтобы что-то нарисовать, художнику необходим холст, на который он наносит краски. Точно также для рисования на компьютере необходим объект-холст, на котором специальными командами строится изображение. Он так и называется – Canvas (англ. "холст"). Объект типа TCanvas входит в состав очень многих визуальных компонентов. Например, каждая форма имеет свой холст, на котором можно рисовать. Объект Canvas имеется также у таких компонентов, как Panel, Image, Grid и многих других. При этом в объектном типе TCanvas уже реализована масса методов для рисования самых различных изображений – велосипед изобретать не придется. Главный компонент для работы с изображениями – Image. В него можно и загрузить статическую картинку (скажем, фон) из внешнего файла, и отрисовывать программным путем любые вещи – графики функций, геометрические фигуры, анимацию и пр. Данный компонент обеспечивает лишь плоскую (2D) графику. Для работы с 3D графикой обычно применяется встроенная в Windows библиотека OpenGL, рассмотрение которой выходит за пределы настоящего курса. Самое простое применение компонента TImage – размещение статического изображения, загруженного из внешнего файла. Данный компонент поддерживает графические форматы ani, gif, bmp, wmf, emf, jpeg, ico. Кратко рассмотрим, чем они отличаются. - формат ani – растровый анимированный курсор. Когда в Windows на экране вертятся песочные часы или скачет лошадка – это и есть отображение ani-файла. Правда, в компоненте TImage лошадка скакать не будет, а отобразится только первый кадр анимации; - формат gif – растровый формат, широко используемый в Интернете. Недостаток – поддерживаются только 256 цветов. Достоинство – возможность задания прозрачных областей и возможность создания так называемых анимированных gif-файлов, состоящих из последовательности кадров (как и для ani-файлов, TImage будет отображать только первый кадр); - формат bmp – ветеран, существует еще со времен операционной системы DOS. Это растровый формат с различными вариантами цветовой глубины (от черно-белой картинки до 16 млн. цветов) и с возможностью сжатия информации без потерь. Прозрачные области не поддерживаются; - формат wmf (Windows metafile) – векторный формат, специально разработанный для Windows. Внутри это последовательность команд отрисовки изображения. Компактен, отлично подходит для вывода чертежей, схем, графиков. Формат wmf понимают практически все программы: Word, Visio, Corel, Компас, AutoCAD и многие другие; - формат emf (enhanced metafile) – улучшенная версия wmf. Достоинства те же. - формат jpeg (Joint Photographic Experts Group) – особый растровый формат. В нем реализовано сжатие изображения с потерями. За счет устранения мелких деталей изображение удается сжать в 100 и более раз, поэтому данный формат очень популярен в Интернете. Один из главных недостатков – отсутствие возможности создавать прозрачные области; - формат ico – растровый формат хранения значков ("иконок") Windows фиксированного размера 16х16 или 32х32 пиксела. Графический файл загружается в компонент Image при помощи его свойства Picture (в Инспекторе объектов открывается окошко просмотра картинки с кнопкой "Load"). При этом содержимое файла физически копируется и добавляется в exe – файл нашей программы. Поэтому нежелательно таким образом загружать многомегабайтные картинки: программа разрастается и начинает заметно тормозить. Внешним видом загруженной картинки управляют свойства Center (центрирование изображения), Stretch (если True, то картинка автоматически масштабируется в размер компонента Image), AutoSize (противоположна Stretch: при AutoSize=True компонент Image принимает размер загруженной картинки). Если свойство Transparent=True, то при загрузке картинки с прозрачными областями (в форматах gif, wmf, emf) сквозь них будет виден фон. Свойство Proportional указывает, можно ли искажать пропорции загруженной картинки или нет. Наконец, свойство IncrementalDisplay включает режим постепенного вывода больших изображений. При этом картинка выводится на экран по частям, чтобы пользователь видел, что процесс идет, и программа не зависла. Для самостоятельного рисования на объекте Image существует масса способов. Во-первых, у объекта Canvas, принадлежащего объекту Image, доступен массив пикселов, изменяя который, можно заняться "вышиванием бисером" – построением картинки по точкам. Давайте, изобразим звездное небо. Поместим на форму компоненты Image1 и Button1. Установим следующие значения свойств компонента Image1: Transparent=False; Stretch=False; AutoSize=False; Center=False и создадим следующий обработчик нажатия кнопки:
procedure TForm1.Button1Click(Sender: TObject);
var i:word;
begin with Image1.Canvas do begin Brush.Color:=clBlack; FillRect(Rect(0,0,Image1.Width,Image1.Height)); for i:=1 to 1000 do Pixels[random(Image1.Width),random(Image1.Height)]:= clWhite end end;
Рассмотрим приведенный фрагмент. Оператор WITH "выносит за скобки" ссылку на объект Image1.Canvas, чтобы не писать его каждый раз. Свойство Brush объекта Canvas отвечает за цвет фона, поэтому устанавливаем его в черный. Метод FillRect заполняет прямоугольник с указанными координатами только что заданным цветом фона. А вот координаты требуется задать через специальный тип данных TRect. Чтобы им воспользоваться, нужно применить стандартную функцию Rect(x1,y1,x2,y2), где x1, y1 и x2, y2 – координаты концов диагонали прямоугольника. В нашем случае мы хотим заполнить все поле, поэтому левый нижний угол прямоугольника лежит в точке (0,0), а правый верхний – в точке, координаты которой равны ширине и высоте всего компонента Image1. Далее в цикле из прямоугольного массива Pixels случайным образом выбирается элемент и ему присваивается новое значение цвета. После запуска при каждом нажатии на кнопку в окне компонента Image1 будет отображаться вполне реалистичное звездное небо (Рис. 2).
Рис. 2. Изображение звездного неба.
Вернемся к строчке Brush.Color:=clBlack. А еcли мы захотим сделать фон светлее, показав, скажем, предрассветное небо? В стандартном наборе цветов подходящего серо-голубого оттенка не предусмотрено. Как быть? Прежде всего, нужно подобрать требуемый цвет в любой программе, поддерживающей цветовую модель RGB. Можно воспользоваться Adobe Photoshop или CorelDRAW. Подобрав там нужный цвет, следует запомнить его составляющие. Приятный серо-голубой оттенок образуется при R=203, G=201, B=226. Изменим в программе всего одну строчку:
Brush.Color:=RGB(203,201,226);
Встроенная функция RGB преобразует интенсивности трех базовых цветов в тип данных TColor. Запустите программу – фон звездного неба изменится на серо-голубой, отсутствующий в стандартном наборе цветов Delphi. Теперь нарисуем что-нибудь более сложное. Принцип рисования заключается в том, что картинка строится пером (pen), которое перемещается по холсту, проводя на нем линии. Кроме пера, у холста еще есть уже знакомая нам кисть (brush), которой можно закрашивать большие области. Наконец, имеются отдельные команды для построения отрезков, прямоугольников, эллипсов (окружность – частный случай эллипса). Давайте нарисуем изображение, показанное на Рис. 3.
Рис. 3. Пример изображения.
Надо понимать, что размер нашего поля рисования в пикселах – величина переменная. Пользователь может растянуть форму, поменять разрешение экрана и т.д. Поэтому ширину поля рисования нужно брать из свойства Image1.Width, а высоту – из свойства Image1.Height. Как и обычно, процедуру рисования будем писать так, чтобы она вызывалась при нажатии на кнопку. Для этого поместим на форму кнопку "Рисовать". Процедура рисования будет выглядеть следующим образом: procedure TForm1.Button1Click(Sender: TObject); begin WITH Image1.Canvas DO BEGIN Brush.Color:=clWhite; Pen.Color:=clBlack; FillRect(Image1.ClientRect); MoveTo(0,Image1.Height DIV 2); LineTo(Image1.Width,Image1.Height DIV 2); MoveTo(Image1.Width DIV 2,0); LineTo(Image1.Width DIV 2,Image1.Height); Brush.Color:=clGreen; Rectangle(Image1.Width DIV 2 - 50, Image1.Height DIV 2 - 50, Image1.Width DIV 2 + 50, Image1.Height DIV 2 + 50); Brush.Color:=clRed; Ellipse(Image1.Width DIV 2 - 10, Image1.Height DIV 2 - 10, Image1.Width DIV 2 + 10, Image1.Height DIV 2 + 10) END end; Разберемся, что здесь написано. Команды Brush.Color и Pen.Color задают будущие цвета заливки и контура, соответственно. Команда FillRect, как мы уже знаем, рисует закрашенный прямоугольник. Здесь она применена для очистки всего поля рисования, поэтому в качестве прямоугольника задается тот прямоугольник, который занимает весь объект Image1. Он хранится в свойстве ClientRect. Проще говоря, команда FillRect(Image1.ClientRect)заливает весь холст цветом заливки. Рисование отрезка от точки x1,y1 в точку x2,y2 выполняется пером в два этапа. Сначала перо командой MoveTo(x1,y1) позиционируется без проведения линии в начальную точку отрезка, а затем отрезок проводится командой LineTo(x2,y2). Координаты начал и концов отрезков показаны на Рис. 3, они легко вычисляются. Не забывайте, что координаты на экране могут быть только целыми, поэтому надо использовать операцию целочисленного деления DIV. Команда Rectangle рисует прямоугольник с заданными цветами контура и заливки. Прямоугольник задается координатами концов любой его диагонали. В нашем случае длина стороны прямоугольника будет 20 пикселов и он строится симметрично относительно середины холста. Команда Ellipse строит окружности и эллипсы. Параметры ее немного необычны – нужно указать координаты концов диагонали прямоугольника, в который будет вписан эллипс или окружность. Порядок рисования фигур имеет значение. Если переставить местами команды Rectangle и Ellipse, то прямоугольник закроет собой окружность, и вы ее никогда не увидите. В таблице представлены основные методы рисования и их параметры:
Определенную сложность вызывает построение дуги (Рис. 4). Метод Arc(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer) строит дугу эллипса, вписанного в прямоугольник с координатами диагонали X1, Y1, X2, Y2. Дуга отрисовывается против часовой стрелки от точки пересечения эллипса с прямой (центр эллипса)-(x3,y3) до точки (центр эллипса)-(x4,y4).
Рис. 4. Построение дуги.
Помимо сплошной заливки, можно применять и другие ее виды (штриховка, точки и т.д.), меняя свойство Style объекта Brush (Рис. 5):
Рис. 5 Способы заливки.
Значение bsSolid соответствует сплошной заливке, bsClear – ее отсутствию, что позволяет рисовать незакрашенные фигуры. Для линий свойство Pen.Style устанавливает тип линии:
Построение графиков функций Одно из наиболее востребованных применений компьютерной графики - построение графиков функций. Давайте нарисуем на экране синусоиду. Форма должна иметь вид, показанный на Рис. 6.
Рис. 6. Форма программы построения графика функции.
Сама программа расчета очевидна:
procedure TForm1.Button1Click(Sender: TObject);
VAR Xmin, Xmax, h, y: REAL; cx,cy:REAL;
begin TRY Xmin:=StrToFLoat(LabeledEdit1.Text); Xmax:=StrToFLoat(LabeledEdit2.Text); h:=StrToFLoat(LabeledEdit3.Text) EXCEPT MessageDlg('Неверные данные', mtError, [mbOK],0); Exit END; IF Xmax<=Xmin THEN BEGIN MessageDlg('Xmin>Xmax!', mtError, [mbOK],0); Exit END; Memo1.Lines.Clear; WITH Image1.Canvas DO BEGIN Brush.Color:=clBlue; FillRect(Image1.ClientRect); // Ось Х Pen.Color:=clWhite; MoveTo(0,Image1.Height DIV 2); LineTo(Image1.Width, Image1.Height DIV 2); cx:=(Xmax-Xmin)/Image1.Width; cy:=2/Image1.Height; IF cy>cx THEN cx:=cy; MoveTo(0,Image1.Height DIV 2); // начало графика WHILE Xmin<=Xmax DO BEGIN y:=sin(xmin); Memo1.Lines.Add('sin('+ FloatToStrF(Xmin,ffFixed,10,4)+')='+ FloatToStrF(y,ffFixed,10,4)); LineTo(TRUNC(Xmin/cx), Image1.height DIV 2 - TRUNC(y/cx)); Xmin:=Xmin+h END END end;
Остановимся на командах построения графика. Сначала мы заливаем весь холст синим цветом и проводим посередине горизонтальную белую линию – это ось ОХ. Затем необходимо рассчитать масштабные коэффициенты по осям координат, чтобы при любых значениях Xmin, Xmax и h график уместился в окне. Для функции sin(x) очевидно, что по оси OY ее значения будут изменяться от -1 до 1 (значение синуса достигает четырех только в военное время), а по оси ОХ – от Xmin до Xmax. Коэффициенты по осям будут равны:
;
Рис. 7 Результат построения синусоиды.
Из этих двух коэффициентов нужно выбрать наибольший, тогда весь график гарантированно уместится на холсте. Далее для получения экранных координат текущие значения x и sin(x) надо разделить на рассчитанный ранее максимальный коэффициент. Наконец, при построении графика его надо сместить вниз на половину высоты холста, поскольку ось ОХ проходит посередине. Обратите внимание на использование функции TRUNC для получения целочисленных значений координат. Построенная синусоида показана на Рис. 7. Было бы любопытно увидеть процесс постепенной отрисовки графика функции. График бы "полз", создавая иллюзию анимации. Попробуем поставить в цикл расчета функции команду задержки Sleep(100), останавливающую программу на 100мс: ... WHILE Xmin<=Xmax DO BEGIN Sleep(100); y:=sin(xmin); ... Нажимаем F9, и… ничего не изменилось, только расчет стал выполняться медленнее, но график все равно отображается только в самом конце. Почему? Здесь мы столкнулись с одной важной особенностью программ на Delphi: при работе цикла никаких действий с элементами интерфейса не выполняется. Они как бы накапливаются и затем выполняются все сразу. Что же делать? Надо сказать Delphi, чтобы он не ленился и даже в цикле производил перерисовку нашего графика. Делается это просто – достаточно в теле цикла записать команду Application.ProcessMessages. Вот теперь все получилось, график красиво "ползет". ЧТО ПРОИСХОДИТ, КОГДА МЫ ССОРИМСЯ Не понимая различий, существующих между мужчинами и женщинами, очень легко довести дело до ссоры... Что делает отдел по эксплуатации и сопровождению ИС? Отвечает за сохранность данных (расписания копирования, копирование и пр.)... Система охраняемых территорий в США Изучение особо охраняемых природных территорий(ООПТ) США представляет особый интерес по многим причинам... Что способствует осуществлению желаний? Стопроцентная, непоколебимая уверенность в своем... Не нашли то, что искали? Воспользуйтесь поиском гугл на сайте:
|