Вступление
Здравствйте! |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Новости сайта[07.02.03] В разделе Компиляторы появился компилятор Dev-Pascal v.1.9.2. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ТеорияДо этого наши программы работали в текстовом режиме. Кроме него существует ещё и графический. В текстовом режиме вам доступны только 256 символов псевдографики (как бы графики), которые вы можете отобразить на экране. Этих символов достаточно для простых графических работ, например: нарисовать таблицу, написать карточную игру, оболочку (типа Norton Commander), многоконный тестовый редактор (взять даже BP) и для много другого. Графический же режим характеризуется возможностью задавать цвет любой точки экрана. И соответственно мы можем рисовать сложные геометрические фигуры. Для работы с графикой в комплект поставки BP входит модуль graph, в котором содержатся основные функции и процедуры. Так же для работы нужны специальные графические драйвера, которые тоже входят в BP и находятся в папке BP\BGI. Кстати модуль graph называется библиотекой BGI (Borland Graphic Interface). Прежде чем начать работу с графикой нам надо перейти в графический режим, т.к. наша программа запускается по умолчанию в текстовом. И соответственно перейти назад в текстовый после завершения работы. Поэтому каркас программы превращается в нечто более сложное, чем было до этого:
ну что неплохое начало :) Эта программа "ничего" не делает. Просто переходт в графический режим, ждёт нажатия клавиши и выходит назад в текстовый. Теперь давайте разберём, что же мы тут использовали:
procedure InitGraph(var GraphDriver:Integer; var GraphMode: Integer; PathToDriver: string);процедра инициализации графики. Т.е. по просту говоря для перехода в графический режим. GraphDriver - графический драйвер, которым мы хотим пользоваться. Мы задаём этому параметру значение Detect - авто определение(т.е. программа сама определит наилучший режим). Для современной техники это 640x480x16 :) GraphMode определяет графический режим, т.к. у нас стоит автоопределение он определится сам (это и будет режим 640х480 16 цветов). PathToDriver - путь к файлу драйвера. Если путь не задаётся, как это сделали мы, то драйвер ищётся в текущем каталоге программы. Если использовать автоопределение, то нам нужен файл egavga.bgi из папки bp\bgi. На всякий случай, вдруг кто не знает скажу кое-что об обозначениях что значит 640х480х16 - то точная характеристика графического режима: (число точек по горизонтали)х(число точек по вертикали)х(количество цветов). Т.е. 640 точек по горизонтали, 480 по вертикали и 16 цветов. В интернете сейчас можно найти версии файлов *.bgi для работы в более лучших режимах. Однако я всё же буду рассказывать про стандартную поставку, т.к. углубляться в графику мы пока особо не будем. Всему своё время. Следующая функция возвращает нам резльтат предыдущей функции или процедуры, если возникла ошибка: function GraphResult: Integer;Если возникла ошибка (например при инициализации графики программа не нашла файл с драйвером), то возвращаемое значение не равно grOk. Расшифровку ошибки можно получить вызвав: function GraphErrorMsg(ErrorCode: Integer): string;возвращает строку, содержащую содержание ошибки, заданной переменной ErrorCode. Не помню писал ли я про halt, так что если повторюсь, то ничего: procedure Halt ( Exitcode: Word );останавливает выполнеие программы и передаёт управление операционной системе. Если параметр Exitcode = 1 значит это завершение программы с ошибкой. Ну и последняя процедура: procedure CloseGraph;переходит назад в текстовый режим. Обратите внимание на вызов ReadLn перед вызовом CloseGraph. Зачем мы это делаем? Если опустить этот вызов, то после выполнения программы сразу произойдёт выход в текстовый режим и соответственно экран очистится. Что бы посмотреть результаты выполнения программы мы вынужденны ожидать нажатия клавиши. Если вы думаете, что это сложно, то вы ошибаетесь, что бы создать простое пустое окошко в программе для Windows надо написать раза в три-четыре больше :) Этот код будем считать каркасом наших будующих графических программ. Теперь самое время поговорить о координатах. Всё дело в том, что в компьютерах координатные оси направленны по другому. Для примера: мы пользуемся декартовой системой координат ( © by Рене Декарт :), центром которой является точка (0,0) опять же извиняюсь за убогость, но картинку не приаттачить: ^ y | | | --------+-------> | x | |В компьютерах же используется несколько другая система +-----------------------> | x | | | | | | V yКак видите в ней отсутствуют отрицательные числа и ось ординат (y) направленна вниз. Вызвано это следующей причиной. Разрешение экрана определяют в пикселях. Пиксель (сокращение от Picture Element - элемент картинки) - это минимальная логическая точка (или если хотите единица) экрана. Т.е. разрешению экрана 800х600 означает, что по оси Х у нас максимально 800 пикселей, а по оси Y - 600. Один пиксель может состоять из множества физических точек (т.е. точек подсветкой которых можно управлять аппаратно). Так вот левому верхнему углу экрана соответствеут пиксель с координатами (0,0), а правому нижнему в зависимости от разрешения. Если оно 800х600 тогда (799, 599). Пиксель - это всегда целое положительное число. У вас не может быть 1/3 пикселя или пиксель с координатой (-5, -5). Так как мы не можем посветить на экране 1/3 пикселя и мы не можем высветить пиксель (-5, -5) т.к. это выпадает за границу экрана. Кстати количесто цветов на экране определяется количеством бит видеопамяти, отводимой под 1 пиксель. Так если под один пиксель отводится 1 байт (8 бит) тогда это режим с 256 цветами (напомню, что столько может быть различных комбинаций бит в байте). Теперь рассмотрим самую важную процедуру в графике - подсветка пикселя: procedure PutPixel(X, Y: Integer; color: Word);соответственно х, у - координаты. Color - цвет. Кстати о цветах. Для первых 16 цветов введены специальные константы. Ниже приводится таблица соответствия цветов и номеров (для текстовой версии к сожалению графа пример останется незаполненной, т.к. нет такой возможности):
поэтому putpixel (10, 10, 15) и putpixel (10, 10, WHITE) произведёт одинаковый эффект. Таким образом мы можем рисовать любые фигуры, используя посветку точек на экране. Однако в BGI есть специальные процедуры для рисования графических примитивов - линий, окружностей, прямоугольников, вывода текста, работы с изображениями и многим другим. К сожалению в формат выпусков не влезет описать все возможности BGI, да многое и не понадобится. Поэтому проведу обзор самых нужных функций и процедур.
program demo; uses Crt, Graph; const r = 20; StartX = 100; StartY = 50; var grDriver, grMode, ErrCode : integer; MaxX, MaxY : word; Saucer : pointer; X, Y : integer; ulx, uly : word; lrx, lry : word; Size : word; I : word; procedure MoveSaucer(var X, Y : integer; Width, Height : integer); var Step : integer; begin Step := Random(2*r); if Odd(Step) then Step := -Step; X := X + Step; Step := Random(r); if Odd(Step) then Step := -Step; Y := Y + Step; if X > MaxX then X := MaxX else if (X < 0) then X := 0; if Y > MaxY then Y := 1 else if Y < 0 then Y := 0 end; begin grDriver := Detect; InitGraph(grDriver, grMode,''); ErrCode := GraphResult; if ErrCode <> grOk then begin writeLn ('Graphics error:', GraphErrorMsg(ErrCode)); halt (1) end; ClearDevice; MaxX := getmaxx; MaxY := getmaxy; { рисуем НЛО } Ellipse(StartX, StartY, 0, 360, r, (r div 3)+2); Ellipse(StartX, StartY-4, 190, 357, r, r div 3); Line(StartX+7, StartY-6, StartX+10, StartY-12); Circle(StartX+10, StartY-12, 2); Line(StartX-7, StartY-6, StartX-10, StartY-12); Circle(StartX-10, StartY-12, 2); SetFillStyle(SolidFill, WHITE); FloodFill(StartX+1, StartY+4, GetColor); { вычисляем границы прямоугольника в который вмещается НЛО } ulx := StartX-(r+1); uly := StartY-14; lrx := StartX+(r+1); lry := StartY+(r div 3)+3; Size := ImageSize(ulx, uly, lrx, lry); GetMem(Saucer, Size); GetImage(ulx, uly, lrx, lry, Saucer^); PutImage(ulx, uly, Saucer^, XORput); { рисуем звёздное небо :) } for I := 1 to 1000 do PutPixel(Random(MaxX), Random(MaxY), random (WHITE)); X := MaxX div 2; Y := MaxY div 2; repeat PutImage(X, Y, Saucer^, XORput); Delay (10000); PutImage(X, Y, Saucer^, XORput); MoveSaucer(X, Y, lrx - ulx + 1, lry - uly + 1); until KeyPressed; FreeMem(Saucer, size); ReadLn; closegraph end.Сначала одна неизвестная до этого момента функция function Odd(X: Longint): Boolean;проверяет является ли число нечётным. и возвращает true в случае успеха. Так вот думаю вы ужу понаблюдали за НЛО, теперь разберём как это происходит. Процедура MoveSaucer не осуществляет ничего сверх естественного и просто изменяет координаты НЛО, которые хранятся в глобальных переменных Х,Y. На ней подробно останавливаться думаю не стоит. Так же я не буду комментировать процесс рисования объекта, так как это просто последовательный вызов графических функций безо всяких алгоритмов. Я бы хотел обратить ваше внимание на следующие строки: Size := ImageSize(ulx, uly, lrx, lry); - вычисляем память, которая нам нужна для хранения картинки НЛОтут придётся вспомнить логические операции (как неужели вы их забыли !!!). Если забыли, тогда вернитесь к выпуску "#0E А сила - она, брат, в правде". Так вот что будет, когда мы число xor'им само с собой (a xor a) ??? Правильно 0. То же самое происходит и когда мы вызываем PutImage(ulx, uly, Saucer^, XORput). Ведь в Saucer у нас находится изображение НЛО, когда же мы накладываем изображение само на себя используя операцию xor, то получается 0, т.е. изображение цвет всех точек которого равен 0. А так как цвет фона у нас чёрный (0) то получается эффект стирания, а не перерисовки. Продолжим: repeatвроде бы ничего сложного, но задумайтесь на минутку почему остаются звёзды ? Давайте поставим задержку (delay) совсем большой и присмотримся к НЛО .... через него просвечивают звёзды! (что бы лучше это рассмотреть увеличте количество звёзд ). Всё из-за того что способ применённый здесь не является правильным. Хотя согласитесь рассмотреть эти маленькие точки тяжело и искажения не очень заметны. Теперь рассмотрим внимательнее исходник. Когда мы рисуем НЛО мы опять применяем операцию xor - поэтому если под НЛО находится звёздочка, то она просвечивает, причём с искажением цвета. Однако когда мы стираем НЛО мы вновь применяем xor и НЛО стирается, а звездочке возвращается свой "первозданный" цвет. Давайте обратимся к цифрам. Пускай у нас будет звезда жёлтого (14) цвета. 14 = 1110bДанную анимацию можно применять для взаимно обратных цветов, т.е. по цветам с одинаковым отступом от концов палитры (палитра - набор цветов, каждому цвету соответствует номер в палитре, помните таблицу констант?). Такими цветами и являются: чёрный-белый (0 -15), синий-жёлтый (1-14) и т.д. Про правильную анимацию я раскажу как нибудь в другой раз.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ЗаданиеСегодня на дом вы получите стандартную школьную задачу: построить график функции. Давайте для примера возьмём х3 или sin или кому что нравится (роли это не играет). Естественно график должен быть нарисован в декартовой системе координат (лучше для проверки нарисовать и сами оси). Что бы сразу направить вас по верному пути скажу: задумайтесь как можно сделать так, что бы точка с вещественными координатами отобразилась на экране. Если возникнут затруднения - моя почта внизу. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Решение
что бы удалить элемент нам надо изменить указатель next предыдущего на следующий и указтель prev следующего на предыдущий. Т.е. как бы вычеркнем элемент из списка. После этого мы можем спокойно освободить занимаемаю им память. Обратите внимание на условия проверки list^^.next. Зачем мы это делаем? Давайте вспомним, что на входе у нас list - это текущий (т.е. удаляемый элемент). Поэтому нам надо изменить list, так как элемент мы удалим, а если list будет указывать на него, то список потеряется. Тут и возможны 4 случая: текущий элемент первый, текущий элемент из середины (они подчиняются list^^.next <> nil), текущий элемент последний (случай else) и текущий элемент единственный (это подходит под любой случай - всё равно надо присвоить nil). |