Шрифт:
Интервал:
Закладка:
Function(Fct f,double r1,double r2,Point orig);
};

struct Function:Shape {
Function(Fct f,double r1,double r2,Point orig,
int count = 100,double xscale,double yscale); // ошибка
};
Если аргумент имеет значение, заданное по умолчанию, то все последующие аргументы также должны их иметь.
struct Function:Shape {
Function(Fct f,double r1,double r2,Point orig,
int count = 100,double xscale=25,double yscale=25);
};
Иногда угадать удачные значения по умолчанию легко. Например, для строки хорошим выбором значения по умолчанию будет пустой объект класса string, а для вектора — пустой объект класса vector. В других ситуациях, например для класса Function, правильно выбрать значения по умолчанию значительно сложнее: для этого приходится применять метод проб и ошибок. Помните, что вы не обязаны задавать значения по умолчанию и, если вам трудно это сделать, просто предоставьте пользователю самому задать аргумент.
15.3.2. Новые примеры
Мы добавили еще несколько функций — косинус (cos) из стандартной библиотеки и — просто для того, чтобы продемонстрировать, как создать сложную функцию, — косинус с наклоном x/2.
double sloping_cos(double x) { return cos(x)+slope(x); }
Результат приведен ниже.

Соответствующий фрагмент кода выглядит так:
Function s4(cos,r_min,r_max,orig,400,20,20);
s4.set_color(Color::blue);
Function s5(sloping_cos, r_min,r_max,orig,400,20,20);
x.label.move(–160,0);
x.notches.set_color(Color::dark_red);
Кроме сложения этих двух функций, мы сместили метку оси x и (просто для иллюстрации) немного изменили цвет шкалы деления.
В заключение построим графики логарифма, экспоненты, синуса и косинуса.
Function f1(log,0.000001,r_max,orig,200,30,30); // ln()
Function f2(sin,r_min,r_max,orig,200,30,30); // sin()
f2.set_color(Color::blue);
Function f3(cos,r_min,r_max,orig,200,30,30); // cos()
Function f4(exp,r_min,r_max,orig,200,30,30); // exp()
Поскольку значение log(0) не определено (с математической точки зрения оно равно бесконечности), мы начали диапазон изменения функции log с небольшого положительного числа. Результат приведен ниже.

Вместо приписывания меток этим графикам мы изменили их цвет.
Стандартные математические функции, такие как cos(), sin() и sqrt(), объявлены в стандартном библиотечном заголовке <cmath>. Список стандартных математических функций приведен в разделах 24.8 и B.9.2.
15.4. Оси
Для представления данных мы используем класс Axis (например, как в разделе 15.6.4), поскольку график без информации о его масштабе выглядит подозрительно. Класс Axis состоит из линии, определенного количества делений оси и текстовой метки. Конструктор класса Axis вычисляет координаты линии оси и (при необходимости) линий, используемых как деления оси.
struct Axis:Shape {
enum Orientation { x, y, z };
Axis(Orientation d, Point xy, int length,
int number_of_notches=0, string label = "");
void draw_lines() const;
void move(int dx, int dy);
void set_color(Color c);
Text label;
Lines notches;
};
Объекты label и notches остаются открытыми, поэтому пользователи могут ими манипулировать, например приписывать делениям цвет, отличающийся от цвета линии, или перемещать объект label с помощью функции move() в более удобное место. Объект класса Axis — это пример объекта, состоящего из нескольких полунезависимых объектов.
Конструктор класса Axis размещает линии и добавляет на них деления, если значение number_ of_notches больше нуля.
Axis::Axis(Orientation d, Point xy, int length, int n, string lab)
:label(Point(0,0),lab)
{
if (length<0) error("bad axis length");
switch (d){
case Axis::x:
{
Shape::add(xy); // линия оси
Shape::add(Point(xy.x+length,xy.y));
if (0<n) { // добавляет деления
int dist = length/n;
int x = xy.x+dist;
for (int i = 0; i<n; ++i) {
notches.add(Point(x,xy.y),Point(x,xy.y–5));
x += dist;
}
}
label.move(length/3,xy.y+20); // размещает метку под линией
break;
}
case Axis::y:
{ Shape::add(xy); // ось y перемещаем вверх
Shape::add(Point(xy.x,xy.y–length));
if (0<n) { // добавляем деления
int dist = length/n;
int y = xy.y–dist;
for (int i = 0; i<n; ++i) {
notches.add(Point(xy.x,y),Point(xy.x+5,y));
y –= dist;
}
}
label.move(xy.x–10,xy.y–length–10); // размещает метку
// наверху
break;
}
case Axis::z:
error("ось z не реализована");
}
}
По сравнению с большинством реальных программ этот конструктор очень прост, но мы рекомендуем внимательно изучить его, поскольку он не настолько тривиален, как кажется, и иллюстрирует несколько полезных приемов. Обратите внимание на то, как мы храним линию в части класса Shape, унаследованной классом Axis (используя функцию Shape::add()), хотя деления хранятся в виде отдельного объекта (notches). Это позволяет нам манипулировать линией и делениями оси независимо друг от друга; например, мы можем раскрасить их в разные цвета. Аналогично метка была помещена в фиксированное положение, но, поскольку она является независимым объектом, мы всегда можем переместить ее в другое место. Для удобства используем перечисление Orientation.
Поскольку класс Axis состоит из трех частей, мы должны предусмотреть функции для манипулирования объектом класса Axis в целом. Рассмотрим пример.
void Axis::draw_lines() const
{
Shape::draw_lines();
notches.draw(); // цвет делений может отличаться от цвета линии
label.draw(); // цвет метки может отличаться от цвета линии
}
Для рисования объектов notches и label мы используем функцию draw() а не draw_lines(), чтобы иметь возможность использовать информацию о цвете, которая в них хранится. Объект класса Lines хранится в разделе Axis::Shape и использует информацию о цвете, хранящуюся там же.
Мы можем задать цвет линии, деления и метки по отдельности, но с точки