в разделах 12.7.7 и 12.7.8), очень полезны строковые потоки (см. раздел 11.4).
struct Text:Shape {
// точка в левом нижнем углу первой буквы
Text(Point x, const string& s)
:lab(s), fnt(fl_font()), fnt_sz(fl_size()) { add(x); }
void draw_lines() const;
void set_label(const string& s) { lab = s; }
string label() const { return lab; }
void set_font(Font f) { fnt = f; }
Font font() const { return fnt; }
void set_font_size(int s) { fnt_sz = s; }
int font_size() const { return fnt_sz; }
private:
string lab; // label
Font fnt;
int fnt_sz;
};
Класс Text имеет свою собственную функцию-член draw_lines(), поскольку только он знает, как хранится его строка.
void Text::draw_lines() const
{
fl_draw(lab.c_str(),point(0).x,point(0).y);
}
Цвет символов определяется точно так же, как в фигурах, состоящих из линий (например, Open_polyline и Circle), поэтому можем выбирать новый цвет с помощью функции set_color(), а определять текущий цвет — с помощью функции color(). Размер и шрифт символов выбираются аналогично. В классе предусмотрено небольшое количество заранее определенных шрифтов.
class Font { // шрифт символа
public:
enum Font_type {
helvetica=FL_HELVETICA,
helvetica_bold=FL_HELVETICA_BOLD,
helvetica_italic=FL_HELVETICA_ITALIC,
helvetica_bold_italic=FL_HELVETICA_BOLD_ITALIC,
courier=FL_COURIER,
courier_bold=FL_COURIER_BOLD,
courier_italic=FL_COURIER_ITALIC,
courier_bold_italic=FL_COURIER_BOLD_ITALIC,
times=FL_TIMES,
times_bold=FL_TIMES_BOLD,
times_italic=FL_TIMES_ITALIC,
times_bold_italic=FL_TIMES_BOLD_ITALIC,
symbol=FL_SYMBOL,
screen=FL_SCREEN,
screen_bold=FL_SCREEN_BOLD,
zapf_dingbats=FL_ZAPF_DINGBATS
};
Font(Font_type ff):f(ff) { }
Font(int ff) :f(ff) { }
int as_int() const { return f; }
private:
int f;
};
Стиль определения класса Font совпадает со стилями определения классов Color (см. раздел 13.4) и Line_style (см. раздел 13.5).
13.12. Класс Circle
Просто для того чтобы показать, что не все фигуры в мире являются прямоугольными, мы создали классы Circle и Ellipse. Объект класса Circle определяется центром и радиусом.
struct Circle:Shape {
Circle(Point p, int rr); // центр и радиус
void draw_lines() const;
Point center() const;
int radius() const { return r; }
void set_radius(int rr) { r=rr; }
private:
int r;
};
Использовать класс Circle можно следующим образом:
Circle c1(Point(100,200),50);
Circle c2(Point(150,200),100);
Circle c3(Point(200,200),150);
Эти инструкции рисуют три окружности разных радиусов, центры которых лежат на горизонтальной линии.
Основной особенностью реализации класса Circle является то, что в нем хранится не центр, а левая верхняя точка угла квадрата, окаймляющего окружность. Можно было бы хранить и центр окружности, но мы выбрали вариант, позволяющий библиотеке FLTK оптимизировать процесс рисования окружности. Это еще один пример того, как с помощью класса можно создать другое (предположительно, более точное) представление понятия, для реализации которого он предназначен.
Circle::Circle(Point p, int rr) // центр и радиус
:r(rr)
{
add(Point(p.x–r,p.y–r)); // хранит левый верхний угол
}
Point Circle::center() const
{
return Point(point(0).x+r, point(0).y+r);
}
void Circle::draw_lines() const
{
if (color().visibility())
fl_arc(point(0).x,point(0).y,r+r,r+r,0,360);
}
Обратите внимание на использование функции fl_arc(), рисующей окружность. Первые два аргумента задают левый верхний угол, вторые два — ширину и высоту наименьшего прямоугольника, окаймляющего окружность, а последние два аргумента задают начальный и последний углы. Для того чтобы нарисовать окружность, нужно обойти вокруг ее центра все 360 градусов, но с помощью функции fl_arc() можно нарисовать только часть окружности (и часть эллипса); см. упр. 1.
13.13. Класс Ellipse
Эллипс похож на окружность, но он определяется большой и малой осями, а не радиусом. Иначе говоря, для того чтобы определить эллипс, мы должны задать координаты центра, а также расстояние от центра до точки на оси x и расстояние от центра до точки на оси y.
struct Ellipse:Shape {
// центр, минимальное и максимальное расстояние от центра
Ellipse(Point p, int w, int h);
void draw_lines() const;
Point center() const;
Point focus1() const;
Point focus2() const;
void set_major(int ww) { w=ww; }
int major() const { return w; }
void set_minor(int hh) { h=hh; }
int minor() const { return h; }
private:
int w;
int h;
};
Класс Ellipse можно использовать следующим образом:
Ellipse e1(Point(200,200),50,50);
Ellipse e2(Point(200,200),100,50);
Ellipse e3(Point(200,200),100,150);
Этот фрагмент программы рисует три эллипса с общим центром и разными осями.
Объект класса Ellipse, для которого выполняется условие major()==minor(), выглядит как окружность. Эллипс можно также задать с помощью двух фокусов и суммы расстояний от точки до фокусов. Имея объект класса Ellipse, можем вычислить фокус. Рассмотрим пример.
Point Ellipse::focus1() const
{
return Point(center().x+sqrt(double(w*w–h*h)),center().y);
}
Почему класс Circle не является наследником класса Ellipse? С геометрической точки зрения каждая окружность является эллипсом, но не каждый эллипс является окружностью. В частности, окружность — это эллипс, у которого оба фокуса совпадают. Представьте себе, что мы определили класс Circle как разновидность класса Ellipse. В этом случае нам пришлось включать в представление дополнительные величины (окружность определяется центром и радиусом; для определения эллипса необходимы центр и пара осей). Мы не приветствуем излишние затраты памяти там, где они не нужны, но основная причина, по которой класс Circle не сделан наследником класса Ellipse, заключается в том, что мы не можем определить его, не заблокировав каким-то образом функции set_major() и set_minor(). Кроме того, фигура не была бы окружностью (что легко распознают математики), если бы мы использовали функцию set_major(), чтобы обеспечить выполнение условия major()!=minor(), — по крайней мере, после