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

Разработка функций построения диаграмм.


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

Программа    функции    bargraph(),                                         приведенная    ниже,

удовлетворяет этим требованиям.

/* Вывод диаграммы */

void bargraph(data,num,offset,min,max,width)

double *data;                          /*    массив данных */

int num;                                    /*    количество элементов в массиве */

int offset;                                 /*    расстояние между диаграммами */

int min,max;                              /*    мин. и мак. выводимые значения */

int width;                                  /*    толщина линий */

int y,t,incr;

double norm_data,norm_ratio,spread;

char s[80];



static int color = 0;

int tempwidth;

/* всегда используйте различные цвета */

color++;

if ( color > 3 ) color = 1;

/* определение нормирующего множителя */

spread = (double)max-min;

norm_ratio = 180/spread;

incr = 280/num;/* определение промежутка между значениями */

tempwidth = width;

for (t=0;t<num;++t)

norm_data = data[t];

/* подгонка отрицательных значений */

norm_data = norm_data-(double)min;

norm_data *= norm_ratio; /* нормирование */

y = (int)norm_data; /* преобразование типа */

do

Line(179,((t*incr)+20+offset+width),179-y,

((t*incr)+20+offset+width),color);

width--;

 while(width);

width = tempwidth;

Давайте тщательно разберем данную программу. Функция bargraph() получает через входные параметры: массив данных, число элементов в массиве, расстояние между диаграммами (для случая одновременного вывода нескольких диаграмм), минимальное и максимальное значения данных и ширину линий диаграмм (ширина линии задается в единицах растра). Статическая переменная color определяет новый цвет при повторных обращениях к bargraph(). Таким образом, различные последовательности данных при их одновременном выводе будут изображены диаграммами различного цвета. При вычислении нормирующего множителя вместо максимальной высоты экрана (200 для 4-го видеорежима) использовано меньшее число - 180, что в последующем позволит использовать две строки экрана для вывода поясняющей информации. Обычно удобнее, если диаграмма полностью занимает экран независимо от количества выводимых чисел. Например, диаграмма, отражающая малые наборы данных, выглядит более привлекательной, если она занимает весь экран, а не совокупность сбившихся в кучу вертикальных полос в одном из углов экрана. Для размещения диаграммы относительно ширины экрана последняя (здесь также целесообразнее использовать меньшее число 280 вместо 300) делится на количество выводимых элементов, полученный результат затем используют при определении горизонтальных координат стержней диаграммы. В конце программы выполняется циклическая нормализация данных и вычерчивание линий заданной толщины с указанным смещением.


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

Вычерчивание линии нулевого уровня.

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

/* Вывод линии нулевого уровня */

void grid(min,max)

int min,max;

register int t;

goto_xy(22,0); printf("%d",min);

goto_xy(0,0); printf("%d",max);

line(180,10,180,300,1);

 

Вы видите, что функция grid() так же, как и bargraph() оставляет внизу две строки для вывода поясняющих меток и другой справочной информации.

Вывод меток элементов диаграмм.

Часто пользователю необходимо помечать значения, выводимые диаграммой. Например, на диаграмме, показывающей изменение прибыли корпорации за пять лет, целесообразно каждый стержень диаграммы пометить соответствующим годом. Конечно, вы всегда можете это сделать вручную, используя функции goto_xy() и printf(); функция label(), представленная ниже, освободит вас от этой рутинной работы, она автоматически выводит необходимые метки в нужном месте экрана. Входными параметрами функции label() являются: массив меток и их количество. Длина каждой метки ограничена 20 символами ( включая указатель конца ), но это не является жестким ограничением и при небходимости вы можете легко изменить максимальную длину меток.

/* Вывод меток на экран */

void label(str,num)

char str[][20]; /* массив меток */

int num; /* количество меток */

int i,j,inc;

inc = 38/num;

i = 2; /* определение начальной точки */

for (j=0;j<num;j++)

goto_xy(23,i);

printf(str[j]);

i += inc;

 

Вычерчивание вспомогательных линий.

В некоторых случаях полезно выводить горизонтальные полосы для сравнения высот стержней диаграммы. Так как сплошные линии могут отвлекать пользователя, то для этой цели лучше использовать пунктирные линии. Функция hashlines(), приведенная ниже, рисует требуемые пунктирные линии.



/* Вывод пунктирных линий */

void hashlines()

int i,j;

for (i=10;1<180;i+=10)

for (j=10;j<300;j+=5)

mempoint(i,j,3); /* одна точка на каждые 5 единиц

растра */

Вывод надписей.

При одновременном выводе нескольких наборов в виде диаграмм полезно определить цвет диаграммы, соответствующий каждому набору. Это можно сделать, например, выдав надпись, содержащую наименование набора и используемый для него цвет диаграммы. Функция legend(), приведенная здесь, выводит наименования наборов и прямоугольник соответствующего цвета, в качестве входных параметров она использует список наименований и их количество. Функция legend() использует функцию fill_box(), описанную ранее, для вывода цветного прямоугольника.

/* Вывод надписи */

void legend(names,num)

char names[][20];

int num;                                       /* количество наименований */

int color = 1,i,j;

goto_xy(24,0); /* надпись производится в последней строке */

j = 0;

for (i=0;i<num;i++)

/* вывод наименования */

printf("%s   ",names[i]);

/* определение координаты цветного прямоугольника. В 4

режиме каждому литерному символу отводится 8 единиц

растра (в ширину)  */

j += strlen(names[i]) * 8 + 4;

fill_box(192,j,198,j+12,color);

j += 28; /* продвижение к следующему полю вывода */

color ++;

if ( color>3 ) color = 1;

_________________________________________________________________

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

_________________________________________________________________

Рис.10-1. Результат работы программы построения диаграмм

Простейшая программа вывода диаграмм.

Следующая программа показывает все описанные функции в действии. Результат ее работы представлен на рис.10-1. Программа выводит среднюю стоимость акций трех мнимых корпораций за пять лет.

/* Программная демонстрация построения диаграмм */

#include "dos.h"

void bargraph(),mode(),mempoint();



void line(),goto_xy(),grid(),label();

void hashlines(),legend(),read_cursor_xy();

void palette(),color_puts(),fill_box();

main()

double widget[] =

10.1,20,30,35.34,50

;

double global[] =

19,20,8.8,30,40

;

double tower[] =

25.25,19,17.4,33,29

;

int min,max;

char n[][20] =

"widget",

"global",

"tower"

;

char lab[][20] =

"1983",

"1984",

"1985",

"1986",

"1987"

;

mode(4);                        /* выбор режима 320*200 */

palette(0);

grid(0,50);    /* построение линии нулевого уровня */

hashlines();   /* вывод пунктирных линий */

label(lab,5);  /* вывод чисел */

legend(n,3);   /* вывод надписей */

/* вывод курса акций трех кампаний */

bargraph(widget,5,0,0,50,4);

bargraph(global,5,10,0,50,4);

bargraph(tower,5,20,0,50,4);

getch();

mode(3);

/* Вывод линии нулевого уровня диаграммы */

void grid(min,max)

int min,max;

register int t;

goto_xy(22,0); printf("%d",min);

goto_xy(0,0); printf("%d",max);

line(180,10,180,300,1);

/* вывод меток на экран */

void label(str,num)

char str[][20]; /* массив меток */

int num; /* количество меток */

int i,j,inc;

inc = 38/num;

i = 2; /* определение начальной точки */

for (j=0;j<num;j++)

goto_xy(23,i);

printf(str[j]);

i += inc;

/* Вывод пунктирных линий на экран */

void hashlines()

int i,j;

for (i=10;1<180;i+=10)

for (j=10;j<300;j+=5)

mempoint(i,j,3); /* одна точка на каждые 5 единиц

растра */

/* вывод надписи */

void legend(names,num)

char names[][20];

int num; /* количество наименований */

int color = 1,i,j;

goto_xy(24,0); /* надпись производится в последней строке */

j = 0;

for (i=0;i<num;i++)

/* вывод наименования */

printf("%s   ",names[i]);

/* определение координаты цветного прямоугольника. В 4

режиме каждому литерному символу отводится 8 единиц

растра ( в ширину )  */



j++ = strlen(names[i]*8+4);

fill_box(192,j,198,j+12,color);

j++ = 28; /* продвижение к следующему полю вывода */

color ++;

if ( color>3 ) color = 1;

/* Вычерчивание диаграммы */

void bargraph(data,num,offset,min,max,width)

double *data; /* массив данных */

int num; /* количество элементов в массиве */

int offset; /* расстояние между диаграммами */

int min,max; /* минимальное и максимальное выводимые значения */

int width; /* толщина линий */

int y,t,incr;

double norm_data,norm_ratio,spread;

char s[80];

static int color = 0;

int tempwidth;

/* всегда используйте различные цвета */

color++;

if ( color > 3 ) color = 1;

/* определение нормирующего множителя */

spread = (double)max-min;

norm_ratio = 180/spread;

incr = 280/num; /* определение промежутка между значениями*/

tempwidth = width;

for (t=0;t<num;++t)

norm_data = data[t];

/* подгонка отрицательных значений */

norm_data = norm_data-(double)min;

norm_data *= norm_ratio; /* нормирование */

y = (int)norm_data; /* преобразование типа */

do

line(179,((t*incr)+20+offset+width),179-y,

((t*incr)+20+offset+width),color);

width--;

 while(width);

width = tempwidth;

/* Вывод линии заданного цвета, используя базовый алгоритм

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

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

int startx,starty,endx,endy,color;

register int t,distance;

int x=0,y=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);

x+= delta_x;

y+= delta_y;

if (x>distance)

x-=distance;

startx+=incx;

if (y>distance)

y-=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);

/* запись точки в CGA/EGA память */

void mempoint(x,y,color_code)

int x,y,color_code;

union mask

char c[2];

int i;

 bit_mask;

int i,index,bit_position;

unsigned char t;

char xor; /* xor - цвет или наложение */

char far *ptr = (char far *) 0xB8000000; /* указатель на

CGA */ bit_mask.i = 0xFF3F; /* 11111111 00111111 в двоичном коде */

/* контроль координат для 4 режима */

if (x<0 || x>199 || y<0 || y>319) return;

xor = color_code & 128; /* проверка установки режима xor */

color_code = color_code & 127; /* маска 7 старших бит */

/* установка bit_mask и color_code в правильное положение */

bit_position = y%4;

color_code <<= 2*(3-bit_position);

bit_mask.i >>= 2*bit_position;

/* поиск соответствующего байта в памяти экрана */

index = x*40 + (y>>2);

if (x%2) index+=8152; /* если нечетный, использовать второй

байт */

/* запись цвета */

if (!xor)  /* режим наложения */

t = *(ptr + index) & bit_mask.c[0];

*(ptr + index) = t | color_code;

else  /* режим xor */

t = *(ptr + index) | (char)0;

*(ptr + index) = t | color_code;

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

void mode(mode_code)

int mode_code;

union REGS r;

r.h.al = mode_code;

r.h.ah = 0;

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

/* установка курсора в координаты x,y */

void goto_xy(x,y)

int x,y;

union REGS r;

r.h.ah = 2; /* функция адресации курсора */

r.h.dl = y; /* горизонтальная координата */

r.h.dh = x; /* вертикальная координата */

r.h.bh = 0; /* видеостраница */

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

/* установка цветов диаграмм */

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);

 


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