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

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

Примеры в главе написаны для компьютеров семейства РС с цветным графическим адаптером. Используется четвертый видеорежим, так как он поддерживает все виды цветных адаптеров. Однако, вы можете легко изменить предложенные функции для того, чтобы работать с другими типами адаптеров.

Нормализация данных.

Перед разработкой программы отображения данных на экране вам следует уяснить, как численные значения переводятся в соответствующие координаты экрана. Как вы помните, размерность экрана в четвертом видеорежиме 320*200, причем 320 - горизонтальная размерность и 200 - вертикальная. Учитывая, что диаграммы изображаются вертикальными полосами, данные должны быть преобразованы таким образом, чтобы они принимали значения в диапазоне от 0 до 199. Данный процесс преобразования называется нормализацией.

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

нормирующий_множитель = 200 / (мах - min)

Таким образом, каждый элемент данных нормализуется по формуле:

нормализованное_данное = необработанное_данное * норм_множитель

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

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

Программа    функции    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

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

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

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

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

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

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

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

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

 

Программа вычерчивания диаграмм.

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

Главная программа.

Здесь приводится основная функция main(), описывающая алгоритм построения диаграмм и содержащая несколько макросов.

#define MAX_SETS 3

#define MAX_ENTRIES 50

#define MAX_LABELS 20

#define MAX_NAMES 20

main()

 

double v[MAX_SETS][MAX_ENTRIES]; /* размещение данных */

int num_entries;

int num_sets;

int min,max,i;

int lines,offset;

char save = 0; /* признак сохранения диаграммы */

char names[MAX_NAMES][20];

char lab[MAX_LABELS][20];

/* считывание данных */

enter(v,&num_entries,&num_sets);

/* поиск минимального и максимального значения */

min_max(v,num_entries,num_sets,&min,&max);

/* ввод наименований данных */

get_names(names,num_sets);

/* ввод меток для диаграммы */

get_labels(lab,num_entries);

/* ввод толщины линии */

lines = get_line_size();

/* ввод интервала между диаграммами */

offset = get_offset();

/* сохранить диаграмму в файле ? */

printf(" сохранить диаграмму в файле ? (y/n) ");

if (tolower(getche()) == 'y') save = 1;

mode(4); /* графический режим 320*200 */

palette(0);

grid(min,max); /* вывод линии нулевого уровня */

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

label(lab,num_entries); /* вывод меток диаграммы */

legend(names,num_sets); /* вывод пояснительных надписей */

/* вывод значений в виде диаграммы */

for (i=0;i

bargraph(v[i],num_entries,i*offset,min,max,lines);

if (save) save_pic();

getch();

mode(3);

 

Как вы видите, функция main() начинается описанием переменных, значения которых устанавливает пользователь. Массив v определен достаточно большим, чтобы содержать до трех наборов данных до 50 элементов каждый. (Эти размеры являются произвольными и при желании вы можете их изменить.) Затем функция считывает выводимые пользователем в виде диаграмм данные и определяет минимальное и максимальное значение данных. После этого на экран выводятся линия нулевого уровня, пунктирные линии уровня, метки диаграммы и наименование наборов. В завершение вычерчивается сама диаграмма. Перед выходом происходит сохранение диаграммы при помощи функции save_pic(). Давайте рассмотрим некоторые используемые в программе main() функции, которые не входят в описанные выше инструментарии построения диаграмм.

Функция enter().

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

/* Считывание данных */

enter(v,entries,sets)

double v[][MAX_ENTRIES]; /*  массив данных */

int *entries; /* количество элементов в каждом наборе данных */

int *sets; /* количество наборов данных */

 

int i,j,count,num;

char s[80];

printf("Введите число наборов данных (от 1 до %d)",MAX_SETS);

scanf("%d%c",&count,&j);

if (count>MAX_SETS) count = MAX_SETS; /* выход за границы

массива */

*sets = count;

printf("Введите количество элементов (от 1 до %d) ",MAX_ENTRIES);

scanf("%d%c",&num,&j);

if (num>MAX_SETS) num = MAX_ENTRIES; /* выход за границы

массива */

*entries = num;

j = 0;

/* считывание значений данных */

while((j

printf("Набор данных %d ",j+1);

for (i=0;i

printf("%d:",i+1);

gets(s);

sscanf(s,"%lf",&v[j][i]);

 

j++;

 

return count;

 

Функция min_max().

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

/* Поиск наименьшего минимума и наибольшего максимума

среди всех наборов данных */

void min_max(v,entries,sets,min,max)

double v[][MAX_ENTRIES]; /*  значения */

int entries; /* количество входов для каждого набора

данных */

int sets; /* количество наборов данных */

int *min,*max; /* возвращает минимальное и максимальное

значение */

 

int i,j;

int tmin,tmax;

*min = *max = 0;

for (i=0;i

tmax = getmax(v[i],entries);

tmin = getmin(v[i],entries);

if (tmax>*max) *max = tmax;

if (tmin <*min) *min = tmin;

 

 

/* Возврат максимального значения данных */

getmax(data,num)

double *data;

int num;

 

int t,max;

max = (int)data[0];

for (t=1;t

if (data[t]>max) max = (int)data[t];

return max;

 

/* Возврат минимального значения данных */

getmin(data,num)

double *data;

int num;

 

int t,min;

min = (int)data[0];

for (t=1;t

if (data[t]

return min;

 

Полный текст программы вычерчивания диаграмм.

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

/* Программа генерации диаграмм */

#include "dos.h"

#include "stdio.h"

#define MAX_SETS 3

#define MAX_ENTRIES 50

#define MAX_LABELS 20

#define MAX_NAMES 20

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

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

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

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

void get_labels(),get_names(),min_max();

void save_pic();

main()

 

double v[MAX_SETS][MAX_ENTRIES]; /* размещение данных */

int num_entries;

int num_sets;

int min,max,i;

int lines,offset;

char save = 0; /* признак записи диаграммы */

char names[MAX_NAMES][20];

char lab[MAX_LABELS][20];

/* считывание данных */

enter(v,&num_entries,&num_sets);

/* поиск минимального и максимального значения */

min_max(v,num_entries,num_sets,&min,&max);

/* ввод наименований данных */

get_names(names,num_sets);

/* ввод меток для диаграммы */

get_labels(lab,num_entries);

/* ввод толщины линии */

lines = get_line_size();

/* ввод интервала между диаграммами */

offset = get_offset();

/* сохранить диаграмму в файле ? */

printf(" сохранить диаграмму в файле ? (y/n) ");

if (tolower(getche()) == 'y') save = 1;

mode(4); /* графический режим 320*200 */

palette(0);

grid(min,max); /* вывод линии нулевого уровня */

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

label(lab,num_entries); /* вывод меток диаграммы */

legend(names,num_sets); /* вывод пояснительных надписей */

/* вывод значений в виде диаграммы */

for (i=0;i

bargraph(v[i],num_entries,i*offset,min,max,lines);

if (save) save_pic();

getch();

mode(3);

 

/* считывание данных */

enter(v,entries,sets)

double v[][MAX_ENTRIES]; /*  массив данных */

int *entries;                             /* количество элементов данных в каждом наборе

данных */

int *sets; /* количество наборов данных */

 

int i,j,count,num;

char s[80];

printf("Введите число наборов данных (от 1 до %d)",MAX_SETS);

scanf("%d%c",&count,&j);

if (count>MAX_SETS) count = MAX_SETS; /* выход за границы

массива */

*sets = count;

printf("Ведите число элементов данных (от 1 до %d)",MAX_ENTRIES);

scanf("%d%c",&num,&j);

if (num>MAX_ENTRIES) num = MAX_ENTRIES; /* выход за границы

массива */

*entries = num;

j = 0;

/* считывание значений */

while((j

printf(" Набор данных %d ",j+1);

for (i = 0;i

printf("%d:",i+1);

gets(s);

sscanf(s,"%lf",&v[j][i]);

 

j++;

 

return count;

 

/* Ввод имен наборов */

void get_names(n,num)

char n[][20];                                     /* массив для имен */

int num;                                            /* число наборов */

 

int i;

for (i=0;i

printf(" Введите имя: ");

gets(n[i]);

 

/* Ввод метки каждого входа */

void get_labels(l,num)

char l[][20]; /* массив для меток */

int num; /* число входов */

 

int i;

for (i=0;i

printf(" Введите имя метки: ");

gets(l[i]);

 

 

/* Ввод интервала между диаграммами в единицах растра */

get_offset()

 

int i;

printf(" Введите интервал между диаграммами в единицах растра");

scanf("%d%*c",&i);

return i;

 

/* Ввод толщины диаграмм в единицах растра */

get_line_size()

 

int i;

printf("Введите толщину диаграммы в единицах растра : ");

scanf("%d",&i);

return i;

 

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

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

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

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

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

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 min_max(v,entries,sets,min,max)

double v[][MAX_ENTRIES]; /*  значения */

int entries; /* количество входов для каждого набора

данных */

int sets; /* количество наборов данных */

int *min,*max; /* возвращает минимальное и максимальное

значение */

 

int i,j;

int tmin,tmax;

*min = *max = 0;

for (i=0;i

tmax = getmax(v[i],entries);

tmin = getmin(v[i],entries);

if (tmax>*max) *max = tmax;

if (tmin <*min) *min = tmin;

 

 

/* Возврат максимального значения данных */

getmax(data,num)

double *data;

int num;

 

int t,max;

max = (int)data[0];

for (t=1;t

if (data[t]>max) max = (int)data[t];

return max;

 

/* Возврат минимального значения данных */

getmin(data,num)

double *data;

int num;

 

int t,min;

min = (int)data[0];

for (t=1;t

if (data[t]

return min;

 

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

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

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

 

/* сохранение выведенного видеографика */

void save_pic()

 

char fname[80];

FILE *fp; register int i,j;

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

char far *temp;

unsigned char buf[14][80]; /* для размещения содержимого

экрана */

temp = ptr;

/* сохранение верхней части текущего экрана */

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

for (j=0;j<80;j+=2)

buf[i][j] = *temp; /* четный байт */

buf[i][j+1] = *(temp+8152); /* нечетный байт*/

*temp = 0;

*(temp+8152) = 0; /* чистка верхней части

экрана */

temp++;

 

goto_xy(0,0);

printf(" Имя файла : ");

gets(fname);

if (!(fp=fopen(fname,"wb")))

printf(" Невозможно открыть файл ");

return;

 

temp = ptr;

/* восстановление верхней части экрана */

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

for (j=0;j<80;j+=2)

*temp = buf[i][j];

*(temp+8152) = buf[i][j+1];

temp++;

 

/* сохранение рисунка в файле */

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

putc(*ptr,fp); /* четный байт */

putc(*(ptr+8152),fp); /* нечетный байт */

ptr++;

 

fclose(fp);

 

Отображение диаграмм на экране дисплея.

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

show backlog

Программа show                     использует                      функцию                 load_pic(),

предназначенную  для  изображения  диаграмм на экране.  (Вы также

можете использовать  эту  программу  для  отображения  на  экране

дисплея  других  графических образов,  предварительно созданных и

записанных в файл.)

/* Простейшая программа восстановления графических образов */

#include "stdio.h"

#include "dos.h"

void load_pic(),mode(),palette(),goto_xy();

main(argc,argv)

int argc;

char *argv[];

 

if (argc != 2)

printf(" Обращение: показать <имя файла>");

exit(1);

 

mode(4);

palette(0);

load_pic(argv[1]);

getch();

mode(3);

 

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

void load_pic(fname)

char *fname;

 

FILE *fp; register int i,j;

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

char far *temp;

unsigned char buf[14][80]; /* для размещения содержимого

экрана */

if (!(fp=fopen(fname,"rb")))

goto_xy(0,0);

printf(" невозможно открыть файл ");

return;

 

/* загрузка изображения из файла */

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

*ptr = getc(fp); /* четный байт */

*(ptr+8152) = getc(fp); /* нечетный байт */

ptr++;

 

fclose(fp);

 

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

void mode(mode_code)

int mode_code;

 

union REGS r;

r.h.al = mode_code;

r.h.ah = 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);

 

/* установка курсора в координаты 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);

Некоторые интересные идеи по модификации программ.

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