|
Начнем с определения общего понятия фигуры. Определение должно быть таким, чтобы им можно было воспользоваться (как базовым классом shape) в разных классах, представляющих все конкретные фигуры (окружности, квадраты и т.д.). Оно также должно позволять работать со всякой фигурой исключительно с помощью интерфейса, определяемого классом shape:
struct shape { static shape* list; shape* next; shape() { next = list; list = this; } virtual point north() const = 0; virtual point south() const = 0; virtual point east() const = 0; virtual point west() const = 0; virtual point neast() const = 0; virtual point seast() const = 0; virtual point nwest() const = 0; virtual point swest() const = 0; virtual void draw() = 0; virtual void move(int, int) = 0; };
Фигуры помещаются на экран функцией draw(), а движутся по нему
с помощью move(). Фигуры можно помещать относительно друг друга,
используя понятие точек контакта. Для обозначения точек контакта
используются названия сторон света в компасе: north - север, ... ,
neast - северо-восток, ... , swest - юго-запад. Класс каждой
конкретной фигуры сам определяет смысл этих точек и определяет,
как рисовать фигуру. Конструктор shape::shape() добавляет
фигуру к списку фигур shape::list. Для построения этого списка
используется член next, входящий в каждый объект shape. Поскольку
нет смысла в объектах типа общей фигуры, класс shape определен как
абстрактный класс.
Для задания отрезка прямой нужно указать две точки или точку
и целое. В последнем случае отрезок будет горизонтальным, а целое
задает его длину. Знак целого показывает, где должна находиться заданная
точка относительно конечной точки, т.е. слева или справа от нее:
class line : public shape { /* отрезок прямой ["w", "e" ] north() определяет точку - `` выше центра отрезка и так далеко на север, как самая его северная точка'' */ point w, e; public: point north() const { return point((w.x+e.x)/2,e.y<w.y?w.y:e:y); } point south() const { return point((w.x+e.x)/2,e.y<w.y?e.y:w.y); } point east() const; point west() const; point neast() const; point seast() const; point nwest() const; point swest() const; void move(int a, int b) { w.x +=a; w.y +=b; e.x +=a; e.y +=b; } void draw() { put_line(w,e); } line(point a, point b) { w = a; e = b; } line(point a, int l) { w = point(a.x+l-1,a.y); e = a; } };
Аналогично определяется прямоугольник:
class rectangle : public shape { /* nw ------ n ----- ne | | | | w c e | | | | sw ------ s ----- se */ point sw, ne; public: point north() const { return point((sw.x+ne.x)/2,ne.y); } point south() const { return point((sw.x+ne.x)/2,sw.y); } point east() const; point west() const; point neast() const { return ne; } point seast() const; point nwest() const; point swest() const { return sw; } void move(int a, int b) { sw.x+=a; sw.y+=b; ne.x+=a; ne.y+=b; } void draw(); rectangle(point,point); };
Прямоугольник строится по двум точкам. Конструктор усложняется, так как необходимо выяснять относительное положение этих точек:
rectangle::rectangle(point a, point b) { if (a.x <= b.x) { if (a.y <= b.y) { sw = a; ne = b; } else { sw = point(a.x,b.y); ne = point(b.x,a.y); } } else { if (a.y <= b.y) { sw = point(b.x,a.y); ne = point(a.x,b.y); } else { sw = b; ne = a; } } }
Чтобы нарисовать прямоугольник, надо нарисовать четыре отрезка:
void rectangle::draw()
{
point nw(sw.x,ne.y);
point se(ne.x,sw.y);
put_line(nw,ne);
put_line(ne,se);
put_line(se,sw);
put_line(sw,nw);
}
В библиотеке фигур есть определения фигур и функции для работы с ними:
void shape_refresh(); // нарисовать все фигуры void stack(shape* p, const shape* q); // поместить p над q
Функция обновления фигур нужна, чтобы работать с нашим примитивным представлением экрана; она просто заново рисует все фигуры. Отметим, что эта функция не имеет понятия, какие фигуры она рисует:
void shape_refresh() { screen_clear(); for (shape* p = shape::list; p; p=p->next) p->draw(); screen_refresh(); }
Наконец, есть одна действительно сервисная функция, которая рисует одну фигуру над другой. Для этого она определяет юг (south()) одной фигуры как раз над севером (north()) другой:
void stack(shape* p, const shape* q) // поместить p над q { point n = q->north(); point s = p->south(); p->move(n.x-s.x,n.y-s.y+1); }
Представим теперь, что эта библиотека является собственностью некоторой фирмы, продающей программы, и, что она продает только заголовочный файл с определениями фигур и оттранслированные определения функций. Все равно вы сможете определить новые фигуры, воспользовавшись для этого купленными вами функциями.