ПК 2102. ПК(2102). П. Г. Колинько пользовательские контейнеры
Скачать 0.78 Mb.
|
1.1. Учебная программа «Библиотека фигур»//=== Файл screen.h -- поддержка работы с экраном const int XMAX=120; //Размер экрана const int YMAX=50; class point { //Точка на экране public: int x, y; point(int a = 0, int b = 0) : x(a), y(b) { } }; // Набор утилит для работы с экраном void put_point(int a, int b); // Вывод точки (2 варианта) void put_point(point p) { put_point(p.x, p.y); } // void put_line(int, int, int, int); // Вывод линии (2 варианта) void put_line(point a, point b) { put_line(a.x, a.y, b.x, b.y); } void screen_init( ); // Создание экрана void screen_destroy( ); // Удаление void screen_refresh( ); // Обновление void screen_clear( ); // Очистка //======== Файл shape.h — библиотека фигур ========= #include //==1. Поддержка экрана в форме матрицы символов == char screen[YMAX] [XMAX]; enum color { black = '*', white = '.' }; void screen_init( ) { for (auto y = 0; y < YMAX; ++y) for (auto &x : screen[y]) x = white; } void screen_destroy( ) { for (auto y = 0; y < YMAX; ++y) for (auto &x : screen[y]) x = black; } bool on_screen(int a, int b) // проверка попадания точки на экран { return 0 <= a && a < XMAX && 0 <= b && b < YMAX; } void put_point(int a, int b) { if (on_screen(a,b)) screen[b] [a] = black; } 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). */ { int dx = 1; int a = x1 - x0; if (a < 0) dx = -1, a = -a; int 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; int eps = 0; for (;;) { //Формирование прямой линии по точкам put_point(x0, y0); if (x0 == x1 && 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 (auto x : screen[y]) // от левого столбца до правого std::cout << x; std::cout << '\n'; } } //== 2. Библиотека фигур == struct shape { // Виртуальный базовый класс "фигура" static list shape( ) { shapes.push_back(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; //Перемещение virtual void resize(int) = 0; //Изменение размера }; list void shape_refresh( ) // Перерисовка всех фигур на экране { screen_clear( ); for (auto p : shape :: shapes) p->draw( ); //Динамическое связывание!!! screen_refresh( ); } class rotatable : virtual public shape { //Фигуры, пригодные к повороту public: virtual void rotate_left( ) = 0; //Повернуть влево virtual void rotate_right( ) = 0; //Повернуть вправо }; class reflectable : virtual public shape { // Фигуры, пригодные public: // к зеркальному отражению virtual void flip_horisontally( ) = 0; // Отразить горизонтально virtual void flip_vertically( ) = 0; // Отразить вертикально }; class line : public shape { /* отрезок прямой ["w", "e"]. north( ) определяет точку "выше центра отрезка и так далеко на север, как самая его северная точка", и т. п. */ protected: point w, e; public: line(point a, point b) : w(a), e(b) { }; line(point a, int L) : w(point(a.x + L - 1, a.y)), e(a) { }; point north( ) const { return point((w.x+e.x)/2, e.y point south( ) const { return point((w.x+e.x)/2, e.y point east( ) const { return point(e.x point west( ) const { return point(e.x point neast( ) const { return point(w.x point seast( ) const { return point(w.x point nwest( ) const { return point(w.x point swest( ) const { return point(w.x void move(int a, int b) { w.x += a; w.y += b; e.x += a; e.y += b; } void draw( ) { put_line(w, e); } void resize(int d) // Увеличение длины линии в (d) раз { e.x += (e.x - w.x) * (d - 1); e.y += (e.y - w.y) * (d - 1); } }; // Прямоугольник class rectangle : public rotatable { /* nw ------ n ------ ne | | | | w c e | | | | sw ------- s ------ se */ protected: point sw, ne; public: rectangle(point a, point b) : sw(a), ne(b) { } 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 { return point(ne.x, (sw.y + ne.y) / 2); } point west( ) const { return point(sw.x, (sw.y + ne.y) / 2); } point neast( ) const { return ne; } point seast( ) const { return point(ne.x, sw.y); } point nwest( ) const { return point(sw.x, ne.y); } point swest( ) const { return sw; } void rotate_right() // Поворот вправо относительно se { int w = ne.x - sw.x, h = ne.y - sw.y; //(учитывается масштаб по осям) sw.x = ne.x - h * 2; ne.y = sw.y + w / 2; } void rotate_left() // Поворот влево относительно sw { int w = ne.x - sw.x, h = ne.y - sw.y; ne.x = sw.x + h * 2; ne.y = sw.y + w / 2; } void move(int a, int b) { sw.x += a; sw.y += b; ne.x += a; ne.y += b; } void resize(int d) { ne.x += (ne.x - sw.x) * (d - 1); ne.y += (ne.y - sw.y) * (d - 1); } void draw( ) { put_line(nwest( ), ne); put_line(ne, seast( )); put_line(seast( ), sw); put_line(sw, nwest( )); } }; void up(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); } //========== Файл shape.cpp (прикладная программа) ========== // Пополнение и использование библиотеки фигур #include "pch.h" //связь с ОС (пример для Visual C++2017) #include "screen.h" #include "shape.h" // ПРИМЕР ДОБАВКИ: дополнительный фрагмент - полуокружность class h_circle: public rectangle, public reflectable { bool reflected; public: h_circle(point a, point b, bool r=true) : rectangle(a, b), reflected(r) { } void draw(); void flip_horisontally( ) { }; // Отразить горизонтально (пустая функция) void flip_vertically( ) { reflected = !reflected; }; // Отразить вертикально }; void h_circle :: draw() //Алгоритм Брезенхэма для окружностей { //(выдаются два сектора, указываемые значением reflected) int x0 = (sw.x + ne.x)/2, y0 = reflected ? sw.y : ne.y; int radius = (ne.x - sw.x)/2; int x = 0, y = radius, delta = 2 - 2 * radius, error = 0; while(y >= 0) { // Цикл рисования if(reflected) { put_point(x0 + x, y0 + y*0.7); put_point(x0 - x, y0 + y*0.7); } else { put_point(x0 + x, y0 - y*0.7); put_point(x0 - x, y0 - y*0.7); } error = 2 * (delta + y) - 1; if(delta < 0 && error <= 0) { ++x; delta += 2 * x + 1; continue; } error = 2 * (delta - x) - 1; if(delta > 0 && error > 0) { --y; delta += 1 - 2 * y; continue; } ++x; delta += 2 * (x - y); --y; } } // ПРИМЕР ДОБАВКИ: дополнительная функция присоединения… void down(shape &p, const shape &q) { point n = q.south( ); point s = p.north( ); p.move(n.x - s.x, n.y - s.y - 1); } // Cборная пользовательская фигура - физиономия class myshape : public rectangle { // Моя фигура ЯВЛЯЕТСЯ int w, h; // прямоугольником line l_eye; // левый глаз – моя фигура СОДЕРЖИТ линию line r_eye; // правый глаз line mouth; // рот public: myshape(point, point); void draw( ); void move(int, int); void resize(int) { } }; myshape :: myshape(point a, point b) : rectangle(a, b), //Инициализация базового класса w(neast( ).x - swest( ).x + 1), // Инициализация данных h(neast( ).y - swest( ).y + 1), // - строго в порядке объявления! l_eye(point(swest( ).x + 2, swest( ).y + h * 3 / 4), 2), r_eye(point(swest( ).x + w - 4, swest( ).y + h * 3 / 4), 2), mouth(point(swest( ).x + 2, swest( ).y + h / 4), w - 4) { } void myshape :: draw( ) { rectangle :: draw( ); //Контур лица (глаза и нос рисуются сами!) int a = (swest( ).x + neast( ).x) / 2; int b = (swest( ).y + neast( ).y) / 2; put_point(point(a, b)); // Нос – существует только на рисунке! } ......................................... ................***********.............. ................*.........*.............. ................*.........*.............. ................*.........*.............. ................*.........*.............. ................*.........*.............. ................*.........*.............. ................***********.............. .....*********************************... ...............*************............. ...............*...........*............. ...............*.**.....**.*............. ...............*...........*............. ...............*.....*.....*............. ...............*...........*............. ...............*.*********.*............. ...............*...........*............. ...............*************............. ...............*...........*............. ...............*...........*............. ................*.........*.............. .................**.....**............... ...................*****................. Рис. 1.1. Результат работы программы (с добавкой «бороды» в поз. 1) void myshape :: move(int a, int b) { rectangle :: move(a, b); l_eye.move(a, b); r_eye.move(a, b); mouth.move(a, b); } int main( ) { setlocale(LC_ALL, "Rus"); screen_init( ); //== 1.Объявление набора фигур == rectangle hat(point(0, 0), point(14, 5)); line brim(point(0,15),17); myshape face(point(15,10), point(27,18)); h_circle beard(point(40,10), point(50,20)); shape_refresh( ); std::cout << "=== Generated... ===\n"; std::cin.get(); //Смотреть исходный набор //== 2.Подготовка к сборке == hat.rotate_right( ); brim.resize(2); face.resize(2); beard.flip_vertically(); shape_refresh( ); std::cout << "=== Prepared... ===\n"; std::cin.get(); //Смотреть результат поворотов/отражений //== 3.Сборка изображения == // face.move(0, -10); // Лицо - в исходное положение up(brim, face); up(hat, brim); down(beard, face); shape_refresh( ); std::cout << "=== Ready! ===\n"; std::cin.get(); //Смотреть результат screen_destroy( ); return 0; } При запуске программы на экран сначала выводится объявленная коллекция фигур. Затем демонстрируется результат поворота/отражения некоторых фигур как подготовка к их использованию. Далее с помощью функций присоединения фигуры перемещаются и образуют заданную картинку: физиономию в шляпе (рис. 1.1). Для рисования использованы прямоугольники, линии и точки. Физиономия является пользовательской фигурой. |