Читаем Программирование полностью

Что мы хотим увидеть на экране? Этот ответ можно найти в начале раздела 15.6. На первый взгляд, для изображения данных нужны три объекта класса Open_polyline — по одному на каждую возрастную группу. Каждый график должен быть помечен. Для этого мы решили в левой части окна записать “название” каждой линии. Этот выбор кажется удачнее, чем обычная альтернатива clearer, — поместить метку где-то на самой линии. Кроме того, для того чтобы отличать графики друг от друга, мы используем разные цвета и связываем их с метками.

Мы хотим пометить ось x, указав годы. Вертикальная линия, проходящая через отметку 2008, означает год, после которого данные являются результатом экстраполяции.

В качестве названия изображения мы решили просто использовать метку окна.

  Сделать так, чтобы графический код был правильным и красиво выглядел, — довольно сложная задача. Основная причина заключается в том, что нам придется выполнить множество кропотливых вычислений, связанных с определением размеров и смещений. Для их упрощения мы начали с определения символических констант, определяющих способ использования экрана.

const int xmax = 600;   // размер окна

const int ymax = 400;

const int xoffset = 100;// расстояние от левого края окна до оси y

const int yoffset = 60; // расстояние от нижнего края окна до оси х

const int xspace = 40;  // пространство между осями

const int yspace = 40;

const int xlength = xmax–xoffset–xspace; // длина осей

const int ylength = ymax–yoffset–yspace;

В принципе эти инструкции определяют прямоугольную область (окно) и вложенный в него прямоугольник (определенный осями).

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

<p id="AutBody_Root276"><strong>15.6.3. Масштабирование данных</strong></span><span></p>

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

const int base_year = 1960;

const int end_year = 2040;

const double xscale = double(xlength)/(end_year–base_year);

const double yscale = double(ylength)/100;

Мы объявили наши масштабирующие множители (xscale и yscale) как числа с плавающей точкой — иначе в наших вычислениях возникли бы серьезные ошибки, связанные с округлением. Для того чтобы избежать целочисленного деления, перед делением преобразовываем наши длины в тип double (см. раздел 4.3.3).

Теперь можно поместить точки на ось x, вычитая их базовое значение (1960), масштабируя с помощью множителя xscale и добавляя смещение xoffset. Значение y обрабатывается аналогично. Эти операции тривиальны, но кропотливы и скучны. Для того чтобы упростить код и минимизировать вероятность ошибок (а также, чтобы не приходить в отчаяние), мы определили небольшой класс, в который включили эти вычисления.

class Scale { // класс для преобразования координат

  int cbase; // координатная база

  int vbase; // база значений

  double scale;

public:

  Scale(int b,int vb,double s):cbase(b),vbase(vb),scale(s) { }

  int operator()(int v) const

  { return cbase + (v–vbase)*scale; } // см. раздел 21.4

};

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

Scale xs(xoffset,base_year,xscale);

Scale ys(ymax–yoffset,0,–yscale);

Обратите внимание на то, что мы сделали масштабирующий множитель ys отрицательным, чтобы отразить тот факт, что координаты y возрастают в направлении вниз, хотя мы привыкли, что они возрастают в направлении вверх. Теперь можем использовать функцию xs для преобразования лет в координату x. Аналогично можно использовать функцию ys для преобразования процентов в координату y

<p id="AutBody_Root277"><strong>15.6.4. Построение графика</strong></span><span></p>

Итак, у нас есть все предпосылки для создания элегантной программы. Начнем с создания окна и размещения осей.

Window win(Point(100,100),xmax,ymax,"Aging Japan");

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже