Бьерн Страуструп - Язык программирования С++. Главы 5-8
Страница 37. Библиотека фигур



6.4.2 Библиотека фигур

 Начнем с определения общего понятия фигуры. Определение должно
 быть таким, чтобы им можно было воспользоваться (как базовым классом
 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);
          }

     Представим теперь, что эта библиотека является собственностью
 некоторой фирмы, продающей программы, и, что она продает только
 заголовочный файл с определениями фигур и оттранслированные
 определения функций. Все равно вы сможете определить новые фигуры,
 воспользовавшись для этого купленными вами функциями.

 
« Предыдущая статья   Следующая статья »