Н. Вирт - Программирование на языке Модула-2 (1160777), страница 22
Текст из файла (страница 22)
Для таких приложений характерно, что каждый элемент несет информацию освоей позиции. Например, дисковая память допускает избирательное чтение и запись отдельныхблоков данных, так называемых секторов. Часто большие наборы данных пишутсяпоследовательно, а читаются выборочно. (Эти возможности были отмечены в модулях Files иFileSystem из предыдущего раздела.)С недавних пор важность вывода, не являющегося последовательным, возросла из-зараспространения визуальных средств вывода, т.е. дисплеев, высвечивающих данные на экране.Большинство дисплейных терминалов до сих пор работает в последовательном режиме лишьпотому, что данные - большей частью просто текст, а также и потому, что последовательныйспособ обработки привычен и многие пользователи просто не представляют себе преимуществнепоследовательной обработки.Видится два основных мотива использования вывода, отличного от последовательного.1. Данные включают элементы, не имеющие последовательной природы, такие, как линии,таблицы, схемы или рисунки, т.е.
так называемую графику.2. Экран должен использоваться для вывода нескольких последовательностей выходныхданных независимо и параллельно, т.е. экран используется для имитации нескольких дисплеев,причем каждый из них несет информацию о своей позиции относительно всего экрана.113Далее мы опишем два модуля, которые предоставляют названные возможности. Посколькунепоследовательные операции обеспечивают существенно большую гибкость, они используются вочень широком диапазоне приложений.
Поэтому эта область гораздо труднее для стандартизации.Таким образом, модули, приводимые далее, следует рассматривать лишь как предварительныепредложения, а не как "окончательные решения". Но тем не менее они оказались очень полезнымии удобными во многих практических приложениях.Важное подмножество универсальных графических средств - вычерчивание прямых. Еслик этому добавить возможность вывода текста (короткие строки для заголовков и т.д.), товычерчивание прямых может оказаться во многих случаях, таких, как вывод таблиц и диаграмм,вполне достаточным. Для этих приложений мы предлагаем модуль LineDrawing (вычерчиваниепрямых). В этом модуле предполагается наличие прямоугольной области экрана.
Из модуляэкспортируется ширина (width) и высота (height) экрана, заданные в единицах горизонтальных ивертикальных координат соответственно. Экран считается матрицей точек, называемой растром.Возможен доступ к любой точке (элементу растра); здесь мы предполагаем, что любая из нихможет быть закрашена в черный или белый цвет. Распространение этого понятия на многозначныеточки, описывающие либо тона серого цвета разной яркости, либо различные цвета, сконцептуальной точки зрения очевидно.Наиболее важные процедуры, содержащиеся в модуле LineDrawing, называются dot (точка)и area (область).
Вызовdot(c,x,y)закрашивает элемент растра с координатами х,у, шириной и высотой w и h соответственнов "цвет" с, гда с = 0 будет обозначать белый цвет, а с = 1 - черный. Конечно, х и у должны лежатьвнутри области экрана, т.е. 0 <= х < width, 0 <= у < height. Мы предполагаем, что координаты 0,0обозначают левый нижний угол прямоугольной области экрана. Вызовarea(c,x,y,w,h)закрашивает прямоугольник с координатами левого нижнего угла х,у в "цвет" с.
Можносчитать, что с = 0 обозначает белый, с = 1 -светло-серый, с = 2 - темно-серый и с = 3 - черный цвет:однако другие реализации могут предлагать более широкий набор значений или даже настоящиецвета. Способ представления диапазона серого тоже не Фиксирован. Например, значение с можетнепосредственно управлять интенсивностью электронного пучка либо же использоваться длявыбора комбинации точек, размножаемой в задаваемом прямоугольнике.
Использованиепроцедуры area продемонстрировано в модуле Ферзи, приведенном в разд. 14.DEFINITION MODULE LineDrawing;TYPE PaintMode = (replace,add,invert,erase);VAR Px,Py: INTEGER; (* текущие координаты пера *)mode: PaintMode;(* текущий режим рисования и копирования *)width: INTEGER; (* ширина картинки, только чтение *)height: INTEGER; (* высота картинки, только чтение *)CharWidth: INTEGER; (* ширина литеры *)CharHeight: INTEGER; (* высота литеры *)114PROCEDURE dot(c: CARDINAL; x,y: INTEGER);(* поставить точку с координатами х,у *)PROCEDURE line(d,n: CARDINAL);(* нарисовать прямую длины nв направлении d (угол = 45*d градусов) *)PROCEDURE area(c: CARDINAL: x,y,w,h: INTEGER);(* закрасить прямоугольную область с координатами левогонижнего угла х,у, шириной w и высотой h в цвет с0=белый,1=светло-серый,2=темно-серый,З=черный *)PROCEDURE copyArea(sx,sy,dx,dy,dw,dh: INTEGER);(* скопировать прямоугольную область sx,sy,dw,dhв область dx,dy,dw,dh *)PROCEDURE clear; (* очистить экран *)PROCEDURE Write(ch: CHAR);(* поставить символ ch в позиции пера *)PROCEDURE WriteString(s: ARRAY OF CHAR);END LineDrawing.Процедура line показывает, что даже в графике последовательный режим иногда бываетвесьма желательным из-за удобства работы с ним.
При использовании этой процедуры мыпредполагаем существование некоторого воображаемого пера, которое чертит прямые линии.Это перо имеет перед вызовом процедуры некоторую подразумеваемую позицию. Вызовline(d,n)передвигает его в направлении d на п единиц растра. Таким образом, последовательностьтаких вызовов прочертит ломаную линию и при этом не потребуется каждый раз непосредственноуказывать положение пера.
В предлагаемой версии процедуры мы допускаем лишь нескольковыделенных направлений, а именно лишь прямые с углами вида d*45^0, где d = 0,1,...,7, причем d= 0 означает движение в положительном направлении по оси х, т.е. вправо. Такой режимвычерчивания прямых иногда называется черепашьей графикой.Положение пера задается экспортируемыми переменными Рх и Ру. Они используются приустановке начала первого сегмента ломаной и при переустановке пера для вычерчивания другихломаных.
Использование черепашьей графики из модуля LineDrawing демонстрируетсяследующим примером программы. Эта программа рисует кривую, изобретенную математиком115Серпински, которая заполняет всю плоскость. Эта программа также представляет собой изящныйпример использования рекурсивных процедур для построения рекурсивно определенногоизображения.MODULE Серпински;FROM Terminal IMPORT Read;FROM LineDrawing IMPORT widtn,height,Px,Py,clear,line;CONST РазмерКвадрата = 512;VAR i,h,x0,y0: CARDINAL; ch: CHAR;PROCEDURE A(k: CARDINAL);BEGINIF k > 0 THENA(k-l); line(7,h); B(k-l); line(0,2*h);D(k-l); line(1,h); A(k-l)ENDEND A;PROCEDURE B(k: CARDINAL);BEGINIF k > 0 THENB(k-1); line(5,h); C(k-1): line(6,2*h);A(k-1); line(7,h); B(k-1)ENDEND B;PROCEDURE C(k: CARDINAL);BEGINIF k > 0 THENC(k-1); Une(3,h); D(k-1); line(4,2*h);B(k-1); llne(5,h); C(k-1)END116END C;PROCEDURE D(k: CARDINAL);BEGINIF k > 0 THEND(k-l); line(1,h); A(k-1); line(2,2*h);C(k-l); line(3,h); D(k-1)ENDEND D;BEGIN clear; i := 0; h := РазмерКвадрата DIV 4;x0 := CARDINAL(width) DIV 2;y0 := CARDINAL(helght) DIV 2 + h;REPEAT i := i + 1; X0 := X0 - h; h := h DIV 2; y0 := y0 + h; Рх := x0; Py := У0;A(i): line(7,h); B(i); llne(5,h):C(i); line(3.h); D(i); line(1,h); Read(ch)UNTIL (i = 6) OR (ch = 33C);clearEND Серпински.Модуль LineDrawing содержит процедуры для вычерчивания прямых линий ипрямоугольников.
Более сложные конфигурации можно получить закрашиванием отдельныхточек с помощью процедуры dot. Окружность является довольно важным геометрическимэлементом. Поэтому мы опишем здесь процедуру, рисующую окружности с координатами центрах,у и радиусом r. Любопытная особенность этого метода состоит в том, что он не использует чиселтипа REAL, а потому сравнительно эффективен.117Уравнение окружности имеет вид х^2 + у^2 = r^2.
После того как нарисована точка впозиции х,у, мы хотим вычислить координаты следующей точки, а именно x+dx и y+dy.Отношение dy/dx можно найти, продифференцировав уравнение кривой; получим dy/dx = -х/у.Следовательно, мы можем задать dx = -k*y и dy = k*x, где k - достаточно малая константа,определяющаястепеньгрубостиизображенияокружности,аппроксимируемойпоследовательностью сторон многоугольника. Будем использовать лишь дробные числа сФиксированной точкой, представленные в виде масштабированных целых чисел.
Возьмемвеличину c1 = 1/к и вычислим очередные значения х и у по Формуламx := х - у DIV c1; у := у + х DIV c1Если предположить, что в нашем распоряжении имеется экран, имеющий ширину и высотуне более 512 точек, то для представления координат потребуется 9 бит. Предполагая далее, чтодлина машинного слова составляет 16 бит, получаем, что 7 бит остается для дробной части. Такимобразом, можно считать, что наши целые координаты имеют на самом деле двоичную точку,сдвинутую на 7 разрядов влево. Усеченная целая часть координаты х получается делением ее на с2= 2^7 = 200В. Константа c1 выбирается настолько большой, чтобы при максимальных значениях хи у вычисляемые приращения были равны единице.PROCEDURE Окружность(х0,у0,r: INTEGER);CONST с1 = 400В; с2 = 200В;VAR x,y: INTEGER;BEGINr := r*с2; х:=r; у:=0; r:=r-1;REPEAT dot(1,x DIV c2 + x0,y DIV c2 + у0);x := х - у DIV c1; У := У + X DIV c1UNTIL (х >= r) & (у <= 0)END ОкружностьПримечание: Этот алгоритм в действительности более хитроумен, чем может показаться, идля доказательства правильности его Функционирования требуется тщательный численный анализэффектов округления.Средства графического вывода становятся значительно полезнее, если их объединить сподходящим устройством ввода.
Допустим, что имеется некоторое устройство, позволяющеерегистрировать перемещения по плоскости. С его помощью можно вводить позицию в терминахкоординат х и у. Более того, мы предполагаем, что с этим устройством связано несколько кнопок,состояние которых может быть считано.Мы называем это устройство ввода мышью. Название связано с конкретным оформлением ввиде устройства, перемещаемого рукой по столу. У него три кнопки (глаза), и оно подсоединяетсяк клавиатуре тонким, возможно серым, кабелем.