С для профессиональных программистов

       

Сборка подпрограмм


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

В "рисующих" программах вам необходимо контролировать текущее положение координат X и Y (в графическом режиме текущие координаты индицируются курсором не совсем обычной формы). Для простоты дальнейшего изложения материала будем называть "графический" курсор "перекрестьем", принимая во внимание и тот факт, что в графическом режиме его форма действительно напоминает крест. Функция xhairs() размещает графический курсор в позиции, специфицированной значениями ее аргументов X и Y.

Напоминаем, что двоичный код цвета складывается по схеме "ИЛИ" со 128 с целью установки 7 бита в 1. Это позволяет функции mempoint() складывать по схеме "исключающего ИЛИ" двоичные коды старого и нового цвета на экране вместо его изменения. Такая возможность позволяет достичь двух важных моментов. Во-первых, графический курсор всегда видим, т.к. всегда имеет цвет, отличный от окружающего. Во-вторых, значительно упрощается процесс возврата точки растра, занимаемой курсором, в исходное положение. Эта операция выполняется путем повторного обращения к этим точкам (напомним, что последовательное выполнение двух операций по схеме "исключающего ИЛИ" всегда приводит к первоначальному значению). Ниже приведен текст функции отображения графического курсора.

/* отображение графического курсора */

void xhairs(x,y)

int x,y;

line(x-4,y,x+3,y,1|128);

line(x,y+4,x,y-3,1|128);

 

Программа рисования, описанная в данном разделе, позволит вам:

- рисовать линии;

- рисовать прямоугольники;

- закрашивать прямоугольники;

- рисовать окружности;


- закрашивать окружности;



- выбирать цвет;

- выбирать палитру;

- устанавливать скорость изменения параметров;

- сохранять графические изображения;

- загружать графические изображения;

- вращать объекты вокруг любой точки;

- копировать и пересылать графические изображения.

Приведем ниже текст главной программы :

main()

union k

char c[2];

int i;

 key ;

int x=10, y=10; /* текущая позиция экрана */

int cc=2;   /* текущий цвет */

int on_flag=1; /* признак использования карандаша */

int pal_num=1; /* номер палитры */

/* конечная точка определения линий,

прямоугольников, окружностей */

int startx=0, starty=0, endx=0, endy=0;

int first_point=1;

int inc=1; /* шаг пересылки */

int sides=0; /* количество сторон выбранного объекта */

int i;

mode(4); /* переключатель режима CGA/EGA */

palette(0); /* палитра 0 */

xhairs(x, y); /* указатель курсора */

do

key.i = bioskey(0);

xhairs(x, y); /* графический курсор */

if(!key.c[0]) switch(key.c[1])

case 75: /* влево */

if(on_flag) line(x, y, x, y-inc, cc);

y -= inc;

break;

case 77: /* вправо */

if(on_flag) line(x, y, x, y+inc, cc);

y += inc;

break;

case 72: /* вверх */

if(on_flag) line(x, y, x-inc, y, cc);

x -= inc;

break;

case 80: /* вниз */

if(on_flag) line(x, y, x+inc, y, cc);

x += inc;

break;

case 71: /* вверх и влево */

if(on_flag) line(x, y, x-inc, y-inc, cc);

x -= inc;

y -= inc;

break;

case 73: /* вверх и вправо */

if(on_flag) line(x, y, x-inc, y+inc, cc);

x -= inc;

y += inc;

break;

case 79: /* вниз и влево */

if(on_flag) line(x, y, x+inc, y-inc, cc);

x += inc;

y -= inc;

break;

case 81: /* вниз и вправо */

if(on_flag) line(x, y, x+inc, y+inc, cc);

x += inc;

y += inc;

break;

case 59: /* F1 - медленно */                                                           inc=1;

break;

case 60: /* F2 - быстро   */

inc=5;

break;

else switch(tolower(key.c[0]))

case 'o': /* переключение шаблона */

on_flag = !on_flag;

break;

case '1': cc=1; /* цвет 1 */



break;

case '2': cc=2; /* цвет 2 */

break;

case '3': cc=3; /* цвет 3 */

break;

case '0': cc=0; /* цвет 0 */

break;

case 'b': box(startx, starty, endx, endy, cc);

break;

case 'f':

fill_box(startx, starty, endx, endy, cc); break;

case 'l':

line(startx, starty, endx, endy, cc);                                                    break;

case 'c':

circle(startx, starty, endy-starty, cc);  break;

case 'h':

fill_circle(startx, starty, endy-starty, cc); break; case 's':

save_pic();                                                                                            break;

case 'r':

load_pic();                                                                                             break;

case 'm': /* пересылка фрагмента */

move(startx, starty, endx, endy, x, y);   break;

case 'x': /* копирование фрагмента */

copy(startx, starty, endx, endy, x, y);   break;

case 'd': /* определить поворот(cдвиг) объекта */

/* Внимание!! Во время трансляции программы идентификатор object

был помечен как "неопределенный". Его описание действительно

отсутствует в этой программе. (Ред. пер. И.Бычковский)

*/

sides = define_objekt(object, x, y);                                                   break;

case 'a': /* поворот(сдвиг) объекта */

rotate_objekt(object, 0.05, x, y, sides); break;

case '\r': /* набор конечных точек для линий, кругов

или прямоугольников */

if(first_point)

 startx = x, starty = y;

else

 endx = x, endy = y;

first_point = !first_point;break;

case 'p':

pal_num = pal_num==1 ? 2:1;

palette(pal_num);

xhairs(x, y);

while (key.c[0]!='q');

getchar();

mode(2);

 

Опишем кратко алгоритм работы программы рисования. Вначале экран терминала устанавливается в 4 графический режим. Затем устанавливается палитра 0, и графический курсор перемещается в верхний левый угол. Шаблон цвета по умолчанию устанавливается в соответствии с кодом 2 (красный в палитре 0). При перемещении графического курсора на экране остается след, который окрашивается в соответствии с текущим цветом шаблона. Если нажимать клавиши перемещения курсора, графический курсор перемещается на одну точку растра в заданном направлении. Такая скорость перемещения может не удовлетворять пользователя, поэтому в программе предусмотрена возможность смещения на 5 точек растра путем нажатия клавиши F2. Отменить режим ускоренного перемещения можно путем нажатия клавиши F1. Изменение цвета осуществляется при нажатии цифровых клавиш от 0 до 3. В палитре 0 цифра 0 зарезервирована, 1 определяет зеленый цвет, 2 - красный, 3 - желтый. Шаблон цвета может быть изменен путем нажатия клавиши 0. Клавиши <Курсор в левый верхний угол> (<HOME>), <Страница вверх> (<PGUP>), <Страница вниз> (<PGDN>) и <Кон> (<END>) перемещают графический курсор в указанном направлении и под углом в 45 градусов.



Для анализа кодов операций чтения в программах используется функция bioskey(). Порядок подключения этой функции к программе

при компиляции описан в главе 1. В программу включены обращения к

функциям, позволяющим вам рисовать и закрашивать прямоугольники и

окружности,  рисовать линии,  копировать и перемещать изображение

на экране,  сохранять на диске  и  загружать  с  него  содержимое

экрана, отображать и вращать объекты.

При изображении линий, прямоугольников и окружностей вам необходимо определить координаты двух точек. Для прямоугольников

- это координаты двух противоположных углов. Для линий задается начальная и конечная точки, а для окружности - координаты центра и точки, через которую она будет проходить.

Процесс выбора этих точек выполняется путем нажатия клавиши <ВВОД> в момент, когда графический курсор находится в требуемой области. Например, для изображения линии вы перемещаете графический курсор в точку, где она должна начинаться и нажимаете клавишу <ВВОД> . Затем вы устанавливаете курсор в точку, где линия заканчивается, и нажимаете <ВВОД> снова. При нажатии клавиши <ВВОД> выполняется загрузка переменных startx, starty, endx и endy, которые потом используются в качестве параметров вызываемых функций. После того, как координаты точек будут определены, при нажатии клавиши <В> рисуется квадрат, а <F> - квадрат закрашивается, при нажатии <L> рисуется линия, при нажатии <С> рисуется окружность, а <Н> - окружность закрашивается.

Для копирования или перемещения части экрана вы должны определить верхний левый и нижний правый углы области, которую вы хотите переместить (нажатием клавиши <ВВОД> ). Затем вы перемещаете курсор в верхний левый угол области, куда вы хотите переместить изображение. Для пересылки изображения требуется нажать клавишу <М>, а для копирования - <Х>. Запомните, что старое изображение в области, куда осуществляется копирование, будет уничтожено.

Для вращения объекта вам необходимо определить сам объект, путем нажатия клавиши <D>. Затем, используя клавишу <ВВОД>, вы должны определить начальные и конечные координаты точек для отрезков по периметру выбранного объекта. Процесс выбора объекта вращения и определения его границ реализуется функцией define_object(). Вращение объекта начинается после нажатия клавиши <А>. Для определения направления вращения используются клавиши <L> (по часовой стрелке) или <R> (против часовой стрелки). Остановить процесс вращения можно нажатием любой клавиши, кроме <L> или <А>.



Для остановки работы программы используется клавиша <Q>. При желании вы можете включить в программу функции для работы с "мышью". Пример выходных данных программы показан на рисунке 4-5.

_________________________________________________________________

Рисунок 4-5 на стр. 163 не может быть воспроизведен имеющимися средствами. (Ред. пер. И.Бычковский)

_________________________________________________________________

Рис. 4-5. Простейшие результаты работы программы рисования.

А теперь приведем всю программу рисования целиком.

/* Программа для CGA/EGA, позволяющая рисовать линии, прямоугольники и окружности. Вы можете нарисовать какой-либо объект и вращать его по часовой или против часовой стрелки. Вы так же можете копировать графическое изображение на диск и загружать его с диска. */

#define NUM_SIDES 20 /* число сторон объекта;

при необходимости увеличивается */ #include "dos.h"

#include "stdio.h"

#include "math.h"

void mode(), line(), box(), fill_box();

void mempoint(), palette(), xhairs();

void circle(), plot_circle(), fill_circle();

void rotate_point(), rotate_object(), goto_xy();

void display_object(), copy(), move();

void save_pic(), load_pic();

unsigned char read_point();

/*  Этот массив содержит динамически меняющиеся

координаты объекта.

*/

double object[NUM_SIDES][4];

double asp_ratio; /* содержит коэффициент сжатия для

окружностей */

main()

union k

char c[2];

int i;

 key ;

int x=10, y=10; /* текущая позиция экрана */

int cc=2;   /* текущий цвет */

int on_flag=1; /* признак использования карандаша */

int pal_num=1; /* номер палитры */

int startx=0, starty=0, endx=0, endy=0;

int first_point=1;

int inc=1; /* шаг пересылки */

int sides=0; /* количество сторoн выбранного объекта */

int i;

mode(4); /* переключатель режима CGA/EGA */

palette(0); /* палитра 0 */

xhairs(x, y); /* указатель курсора */

do

key.i = bioskey(0);

xhairs(x, y);

if(!key.c[0]) switch(key.c[1])



case 75: /* влево */

if(on_flag) line(x, y, x, y-inc, cc);

y -= inc;

break;

case 77: /* вправо */

if(on_flag) line(x, y, x, y+inc, cc);

y += inc;

break;

case 72: /* вверх */

if(on_flag) line(x, y, x-inc, y, cc);

x -= inc;

break;

case 80: /* вниз */

if(on_flag) line(x, y, x+inc, y, cc);

x += inc;

break;

case 71: /* вверх и влево */

if(on_flag) line(x, y, x-inc, y-inc, cc);

x -= inc;

y -= inc;

break;

case 73: /* вверх и вправо */

if(on_flag) line(x, y, x-inc, y+inc, cc);

x -= inc;

y += inc;

break;

case 79: /* вниз и влево */

if(on_flag) line(x, y, x+inc, y-inc, cc);

x += inc;

y -= inc;

break;

case 81: /* вниз и вправо */

if(on_flag) line(x, y, x+inc, y+inc, cc);

x += inc;

y += inc;

break;

case 59: /* F1 - медленно */

inc=1;

break;

case 60: /* F2 - быстро   */

inc=5;

break;

else switch(tolower(key.c[0]))

case 'o': /* переключение шаблона */

on_flag = !on_flag;

break;

case '1': cc=1; /* цвет 1 */

break;

case '2': cc=2; /* цвет 2 */

break;

case '3': cc=3; /* цвет 3 */

break;

case '0': cc=0; /* цвет 0 */

break;

case 'b':

box(startx, starty, endx, endy, cc);                                                    break;

case 'f':

fill_box(startx, starty, endx, endy, cc); break;

case 'l':

line(startx, starty, endx, endy, cc);                                                    break;

case 'c':

circle(startx, starty, endy-starty, cc);  break;

case 'h':

fill_circle(startx, starty, endy-starty, cc); break; case 's':

save_pic();                                                                                            break;

case 'r':

load_pic();                                                                                             break;

case 'm': /* пересылка фрагмента */

move(startx, starty, endx, endy, x, y);   break;

case 'x': /* копирование фрагмента */

copy(startx, starty, endx, endy, x, y);   break;

case 'd': /* определить объект вращения */

sides = define_objekt(object, x, y);                                                   break;



case 'a': /* вращение объекта */

rotate_objekt(object, 0.05, x, y, sides); break;

case '\r': /* набор конечных точек для линий, кругов

или прямоугольников */

if(first_point)

 startx = x, starty = y;

else

 endx = x, endy = y;

first_point = !first_point;                                                                    break;

case 'p':

pal_num = pal_num==1 ? 2:1;

palette(pal_num);

xhairs(x, y);

while (key.c[0]!='q');

getchar();

mode(2);

/* установка палитры */

void palette(pnum)

int pnum;

union REGS r;

r.h.bh = 1; /* код 4 режима графики */

r.h.bl = pnum;

r.h.ah = 11; /* установка палитры */

int86(0x10, &r, &r);

/* установка видео-режима */

void mode (mode_code)

int mode_code;

union REGS r;

r.h.al = mode_code;

r.h.ah = 0;

int86(0x10,&r, &r);

/* изображение прямоугольника */

void box(sx, sy, ex, ey, c)

int sx, sy, ex, ey, c;

line(sx, sy, ex, sy, c);

line(sx, sy, sx, ey, c);

line(sx, ey, ex, ey, c);

line(ex, sy, ex, ey, c);

/* изображение линии заданного цвета с использованием

алгоритма Брезенхама */

void line(startx,starty,endx,endy,color)

int startx,starty,endx,endy,color;

register int t,distance;

int xerr=0,yerr=0,delta_x,delta_y;

int incx,incy;

/* вычисление расстояния в обоих направлениях                                                      */

delta_x=endx-startx;

delta_y=endy-starty;

/* определение направления шага,

шаг вычисляется либо по вертикальной, либо горизонтальной

линии                                                     */

if (delta_x>0) incx=1;

else  if (delta_x==0) incx=0;

else  incx= -1;

if (delta_y>0) incy=1;

else  if (delta_y==0) incy=0;

else  incy= -1;

/* определение какое расстояние больше */

delta_x=abs(delta_x);

delta_y=abs(delta_y);

if (delta_x>delta_y) distance=delta_x;

else distance=delta_y;

/* изображение линии */

for (t=0; t<=distance+1; t++)

mempoint(startx,starty,color);

xerr+=delta_x;



yerr+=delta_y;

if (xerr>distance)

xerr-=distance;

startx+=incx;

if (yerr>distance)

yerr-=distance;

starty+=incy;

 

/* закрашивание прямоугольника в заданный цвет */

void fill_box(startx,starty,endx,endy,color_code)

int startx,starty,endx,endy,color_code;

register int i,begin,end;

begin=startx<endx ? startx:endx;

end=startx>endx ? startx:endx;

for (i=begin;i<=end;++i)

line(i,starty,i,endy,color_code);

 

/* изображение   окружности   с    использованием    алгоритма

Брезенхама */

void circle(x_center,y_center,radius,color_code)

int x_center,y_center,radius,color_code;

register x,y,delta;

asp_ratio=1.0; /* это число меняется в различных

случаях */

y=radius;

delta=3-2*radius;

for (x=0;x<y; )

plot_circle(x,y,x_center,y_center,color_code);

if (delta<0)

delta+=4*x+6;

else

delta+=4*(x-y)+10;

y--;

x++;

x=y;

if (y) plot_circle(x,y,x_center,y_center,color_code);

/* plot_circle печатает точки, определяющие окружность */

void plot_circle(x, y, x_center, y_center, color_code)

int x_center,y_center,radius,color_code;

int x, y, startx, starty, endx, endy, x1, y1;

starty=y*asp_ratio;

endy=(y+1)*asp_ratio;

startx=x*asp_ratio;

endx=(x+1)*asp_ratio;

for (x1=startx;x1<endx;++x1)

mempoint(x1+x_center,y+y_center,color_code);

mempoint(x1+x_center,y_center-y,color_code);

mempoint(x_center-x1,y+y_center,color_code);

mempoint(x_center-x1,y_center-y,color_code);

for (y1=starty;y1<endy;++y1)

mempoint(y1+x_center,x+y_center,color_code);

mempoint(y1+x_center,y_center-x,color_code);

mempoint(x_center-y1,x+y_center,color_code);

mempoint(x_center-y1,y_center-x,color_code);


Содержание раздела