C++
Шрифт:
7.6.1 Администратор экрана
Вначале было намерение написать администратор экрана на C (а не на С++), чтобы подчеркнуть разделение уровней реалзации. Это оказалось слишком утомительным, поэтому пришлось пойти на компромисс: используется стиль C (нет функций члнов, виртуальных функций, определяемых пользователем операций и т.п.), однако применяются конструкторы, надлежащим образом описываются и проверяются параметры функций и т.д. Оглядывясь назад, можно сказать, что администратор экрана очень пхож на C программу, которую потом модифицировали, чтобы вопользоваться средствами С++ не переписывая
Экран представляется как двумерный массив символов, работу с которым осуществляют функции put_point и put_line, использующие при обращении с экраном структуру point:
// файл screen.h
const XMAX=40, YMAX=24;
struct point (* int x,y; point (**) point(int a, int b) (* x=a; y=b; *) *);
overload put_point; extern void put_point(int a, int b); inline void put_point(point p) (* put_point(p.x,p.y); *)
overload put_line; extern void put_line(int, int, int, int); inline void put_line(point a, point b) (* put_line(a.x,a.y,b.x,b.y); *)
extern void screen_init; extern void screen_refresh; extern void screen_clear;
#include «stream.h»
Перед первым использованием функции put экран надо инциализировать с помощью screen_init, а изменения в структре данных экрана отображаются на экране только после вызова screen_refresh. Как увидит пользователь, это «обновление» («refresh») осуществляется просто посредством печати новой копии экрана под его предыдущим вариантом. Вот функции и оределения данных для экрана:
#include «screen.h» #include «stream.h»
enum color (* black='*', white=' ' *);
char screen[XMAX][YNAX];
void screen_init (* for (int y=0; y«YMAX; y++) for (int x=0; x«XMAX; x++) screen[x][y] = white; *)
Точки печатаются, только если они есть на экране:
inline int on_screen(int a, int b) (* return 0«=a amp; amp; a«XMAX amp; amp; 0«=b amp; amp; b«YMAX; *)
void put_point(int a, int b) (* if (on_screen(a,b)) screen[a][b] = black; *)
Для рисования линий используется функция put_line:
void put_line(int x0, int y0, int x1, int y1) /* Строит линию от (x0,y0) до (x1,y1). Строится линия b(x-x0) + a(y-y0) = 0. Минимизирует abs(eps), где eps = 2*(b(x-x0)+ a(y-y0)). См. Newman and Sproull: ``Principles of Interactive Computer Graphics'' McGraw-Hill, New York, 1979, pp 33-44. */ (* register dx = 1; int a = x1 – x0; if (a « 0) dx = -1, a = -a; register dy = 1; int b = y1 – y0;
if (b « 0) dy = -1, b = -b; int two_a = 2*a; int two_b = 2*b; int xcrit = -b + two_a; register eps = 0; for (;;) (* put_point(x0,y0); if(x0==x1 amp; amp; y0==y1) break; if(eps „= xcrit) x0 += dx, eps += two_b; if(eps“=a !! a«=b) y0 += dy, eps -= two_a; *) *)
Предоставляются функции для очистки экрана и его обноления:
void screen_clear (* screen_init; *) // очистка
void screen_refresh // обновление (* for (int y=YMAX-1; 0«=y; y–) (* // сверху вниз for (int x=0; x«XMAX; x++) // слева направо cout.put(screen[x][y]); cout.put('\n'); *) *)
Функция ostream::put применяется для печати символов как символов; ostream::operator«« печатает символы как млые целые. Пока что вы может представлять себе, что эти опрделения доступны только в откомпилированном виде, который вы изменить не можете.
7.6.2 Библиотека фигур
Нам нужно определить общее понятие фигуры (shape). Это надо сделать таким образом,
struct shape (* shape (* shape_list.append(this); *)
virtual point north (*return point(0,0);*) // север virtual point south (*return point(0,0);*) // юг virtual point east (*return point(0,0);*) // восток virtual point neast (*return point(0,0);*)//северо-восток virtual point seast (*return point(0,0);*) // юго-восток
virtual void draw (**); // нарисовать virtual void move(int, int) (**); // переместить *);
Идея состоит в том, что расположение фигуры задается с помощью move, и фигура помещается на экран с помощью draw. Фигуры можно располагать относительно друг друга, ипользуя понятие точки соприкосновения, и эти точки перечислются после точек компаса (сторон света). Каждая конкретная фигура определяет свой смысл этих точек, и каждая определяет способ, которым она рисуется. Для экономии места здесь на смом деле определяются только необходимые в этом примере строны света. Конструктор shape::shape добавляет фигуру в список фигур shape_list. Этот список является gslist, то есть, одним из вариантов обобщенного односвязанного списка, определенного в #7.3.5. Он и соответствующий итератор были
сделаны так:
typedef shape* sp; declare(gslist,sp);
typedef gslist(sp) shape_lst; typedef gslist_iterator(sp) sp_iterator;
поэтому shape_list можно описать так:
shape_lst shape_list;
Линию можно построить либо по двум точкам, либо по точке и целому. В последнем случае создается горизонтальная линия, длину которой определяет целое. Знак целого указывает, каким концом является точка: левым или правым. Вот определение:
class line : public shape (* /* линия из 'w' в 'e' north определяется как ``выше центра и на север как до самой северной точки'' */ point w,e; public: point north (* return point((w.x+e.x)/2,e.y«w.y?w.y:e.y); *)
point south (* return point((w.x+e.x)/2,e.y«w.y?e.y:w.y); *)
void move(int a, int b) (* w.x += a; w.y += b; e.x += a; e.x += 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; *) *);
Аналогично определяется прямоугольник rectangle:
class rectangle : public shape (* /* nw – n – ne ! ! ! ! w c e ! ! ! ! sw – s – se */ point sw,ne; public: point north (* return point((sw.x+ne.x)/2,ne.y); *) point south (* return point((sw.x+ne.x)/2,sw.y); *) point neast (* return ne; *) point swest (* 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) (* (* 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); *)