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()
, — по крайней мере, после этого фигура перестанет быть окружностью. Нам не нужен объект, который иногда относится к одному типу (когда
major()!=minor()
), а иногда к другому (когда
major()==minor()
). Нам нужен объект (класса
Ellipse
), который иногда выглядит как окружность. С другой стороны, объект класса
Circle
никогда не превратится в эллипс с двумя неравными осями.
Разрабатывая класс, мы должны быть осторожными: не слишком умничать и не слишком полагаться на интуицию. И наоборот, должны быть уверены, что наш класс представляет некое осмысленное понятие, а не является просто коллекцией данных и функций-членов.
Механическое объединение фрагментов кода без размышлений об идеях и понятиях, которые они представляют, — это хакерство. Оно приводит к программам, которые невозможно объяснить и эксплуатировать без участия автора. Если вы не альтруист, то помните, что в роли ничего не понимающего пользователя через несколько месяцев можете оказаться вы сами. Кроме того, такие программы труднее отлаживать.
13.14. Класс Marked_polyline
Часто возникает необходимость пометить точки графика. График можно изобразить в виде ломаной, поэтому нам нужна ломаная, точки которой имели бы метки. Для этого предназначен класс
Marked_polyline
. Рассмотрим пример.
Marked_polyline mpl("1234");
mpl.add(Point(100,100));