[ЗМІСТ]      [Назад]      [Початок розділу]

 

11.4 Проект „Опукла оболонка”

 

      Розглянемо об’єктно-орієнтований проект для розв’язку задачі побудови опуклої оболонки множини точок. Задача формулюється так: є послідовність точок площини, заданих своїми координатами. Треба побудувати опуклу оболонку цієї послідовності точок, показати її а також обчислити її площу та периметр. Проект будемо реалізовувати у Паскалі.

      В залежності від заданої послідовності точок їх опукла оболонка може мати вид:

1.    Порожня оболонка („нулькутник”).

2.    Одноточкова оболонка („однокутник”).

3.    Двоточкова оболонка (відрізок або „двокутник”).

4.    Опуклий многокутник.

Види опуклих оболонок зображено на Рис 11.2 а) – г).

 

Рис. 11.2 Види опуклих оболонок

 

      Визначимо класи для реалізації нашого проекту. Для роботи з точками та відрізками на площині нам знадобляться відповідні класи, які ми назвемо Point та Segment. Для кожного виду опуклої оболонки (1-4) визначимо свій клас: Convex, OneConv, TwoConv та Polygon. Вершини многокутника будемо зберігати у деку, для чого використаємо описаний раніше клас деку довільних об’єктів Deque. Оскільки ми використовуємо дек довільних об’єктів, в якому будуть зберігатися точки, класи Point та Segment будуть нащадками AnyObject. Загальна ієрархія класів опуклої оболонки наведена на Рис. 11.3.

 

Рис. 11.3 – Ієрархія класів опуклої оболонки

 

Polygon не є нащадком TwoConv, як того можна було б очікувати, тому що ці класи мають мало спільного. Так само Segment не є нащадком Point.

 

      Класи Point та Segment описані та реалізовані у модулі Geom. Клас Point є нащадком AnyObject та має властивості X,Y – координати точки та методи Init – створити точку з координатами TX,TY; GetX, GetY – отримати координати X та Y; деструктор Done. Наведена нижче реалізація методів класу Point є дуже простою.

 

      Клас Segment є більш складним. Він містить дві властивості – точки A, B, які визначають кінці відрізку та самі є об’єктами класу Point. Клас Segment містить також методи: конструктор Init; GetA, GetB – отримати точки A та B; Len – довжина відрізку; OnLine – чи лежить точка T на одній прямій з відрізком; Into – чи лежить точка T всередині відрізку; Square – площа відрізку зі знаком; деструктор Done.

      У конструкторі Init класу Segment ми не можемо присвоїти A:=T1 або B:=T2, тому що A та B – це об’єкти класу Point, який містить віртуальні методи. Тому треба спочатку викликати конструктор Init класу Point (A.Init(T1.GetX,T1.GetY); B.Init(T2.GetX,T2.GetY);). Аналогічно робимо у методах GetA, GetB для точки T. Довжина відрізку – це просто відстань між A та B. Що ж стосується площі, то метод Square обчислює площу S DTAB як значення детермінанту

 

          1 |1  1  1|

      S = -*|Tx Ax Bx|

          2 |Ty Ay By|

 

Знак цього значення („+” або „-”) залежить від взаємного розташування точок T, A та B. Якщо вважати відрізок AB вектором, спрямованим з точки A у точку B, то площа трикутника DT1AB (Рис. 11.4) зі знаком буде мати знак „+”, а площа трикутника DT2AB зі знаком буде мати знак „-”.

 

Рис. 11.4 – Різні випадки взаємного розташування точки та відрізку

 

Метод Square ми будемо, зокрема, використовувати для того, щоб визначити чи лежить точка всередині многокутника. Метод OnLine перевіряє, чи лежить точка T на одній прямій з відрізком AB. Для перевірки ми використовуємо метод Square. Але через наближені обчислення для дійсних чисел ми не можемо перевіряти рівність площі 0. Натомість перевіряємо, чи близьке значення площі до 0 з деякою точністю delta, яку ми вважаємо достатньою. Метод Into використовує два допоміжних відрізки: s1 – відрізок AT та s2 – відрізок TB. Якщо точка T лежить на одній прямій з відрізком AB та довжина кожного з відрізків s1, s2 не більша довжини відрізку AB, то точка T лежить всередині відрізку AB.

 

 

{ Модуль роботи з точками та відрізками на площині }

Unit Geom;

 

interface

 

Uses AnyThing;

 

Type

  PointRef = ^Point;

  Point = Object(AnyObject)                     { точка площини }

           private

            X,Y: Real;                          { координати }

           public

            Constructor Init(TX,TY: Real);      { створити точку }

            Function  GetX: Real;               { координата X }

            Function  GetY: Real;               { координата Y }

            Destructor Done; Virtual;           { деструктор }

          end;

  SegmentRef = ^Segment;

  Segment = Object(AnyObject)                    { відрізок на площині }

             private

              A,B: Point;                        { кінці відрізку }

             public

              Constructor Init(T1,T2: Point);    { створити відрізок }

              Procedure GetA(VAR T: Point);      { точка A }

              Procedure GetB(VAR T: Point);      { точка B }

              Function Len: Real;                { довжина відрізку }

              Function OnLine(T: Point): Boolean;{ чи лежить точка T на одній прямій з відрізком ? }

              Function Into(T: Point): Boolean;  { чи лежить точка T всередині відрізку ? }

              Function Square(T: Point): Real;   { площа трикутника TAB зі знаком }

              Destructor Done; Virtual;          { деструктор }

            end;

 

implementation

 

  Constructor Point.Init;

    begin

      X:=TX; Y:=TY

    end;

 

  Function Point.GetX: Real;

    begin  GetX:=X end;

 

  Function Point.GetY: Real;

    begin  GetY:=Y end;

 

  Destructor Point.Done;

    begin end;

 

  Constructor Segment.Init;

    begin

      A.Init(T1.GetX,T1.GetY);

      B.Init(T2.GetX,T2.GetY);

    end;

 

  Procedure Segment.GetA;

    begin

      T.Init(A.GetX,A.GetY)

    end;

 

  Procedure Segment.GetB;

    begin

      T.Init(B.GetX,B.GetY)

    end;

 

  Function Segment.Len: Real;

    begin

      Len:=Sqrt(Sqr(A.GetX-B.GetX)+Sqr(A.GetY-B.GetY))

    end;

 

  Function Segment.Square;

    begin

      Square:=0.5*(A.GetX*B.GetY-B.GetX*A.GetY+B.GetX*T.GetY

                  -T.GetX*B.GetY+T.GetX*A.GetY-A.GetX*T.GetY)

    end;

 

  Function Segment.OnLine;

    const delta = 0.001;

    begin

      OnLine:=abs(Segment.Square(T)) <= delta

    end;

 

  Function Segment.Into;

    var a1,b1: Point; s1,s2: Segment; l: Real;

    begin

      Segment.GetA(a1); s1.Init(a1,T);

      Segment.GetB(b1); s2.Init(b1,T);

      l:=Segment.Len;

      Into:=Segment.OnLine(T) and (l >= s1.Len) and (l >= s2.Len)

    end;

 

  Destructor Segment.Done;

    begin end;

 

end.

 

      Класи, що безпосередньо реалізують опуклу оболонку, описані у модулі Conv. Цей модуль використовує розглянуті раніше модулі AnyThing, Geom, AnyDeque. Для кожного класу описано також вказівник на об’єкти цього класу. Всі класи у модулі мають віртуальні методи AddPoint - додати точку, Show - показати опуклу оболонку та деструктор Done. Метод AddPoint за поточним видом оболонки та новою точкою T визначає, чи змінюється вид опуклої оболонки. Якщо так, то створюється новий динамічний об’єкт та викликається конструктор відповідного класу (існуючий об’єкт при цьому знищується), якщо ні, то, можливо, змінюються характеристики поточної оболонки, але її вид не змінюється. AddPoint повертає вказівник на новостворений об’єкт або на себе, якщо оболонка не змінює вид. Для того, щоб у реалізації методу отримати доступ до об’єкта, для якого цей метод викликано, у Паскалі використовують ключове слово Self. Відповідно, @Self дає адресу цього об’єкта або вказівник на цей об’єкт. Метод Show показує координати всіх точок опуклої оболонки а також її площу та периметр.

 

      Клас Convex реалізує порожню оболонку. Він має властивості Nangle – кількість кутів, Per, Sq – периметр та площа та методи Init - почати роботу, GetNAngle - отримати кількість кутів, GetPer - отримати периметр, GetSq - отримати площу, AddPoint - додати точку, Show – показати опуклу оболонку, деструктор Done. Метод AddPoint відразу створює новий об’єкт класу OneConv, оскільки додавання будь-якої точки до порожньої оболонки створює одноточкову оболонку.

 

      Клас OneConv є нащадком Convex та реалізує одноточкову оболонку. Він має власну властивість A – точка та методи Init - почати роботу, GetA - отримати точку, AddPoint - додати точку, Show – показати опуклу оболонку, деструктор Done. Метод AddPoint створює новий об’єкт класу TwoConv, якщо нова точка не співпадає з вже існуючою. Інакше AddPoint повертає вказівник на себе.

 

      Клас TwoConv є нащадком OneConv та реалізує двоточкову оболонку. Він має власну властивість B – друга точка та методи Init - почати роботу, GetB - отримати точку B, AddPoint - додати точку, Show – показати опуклу оболонку, деструктор Done. Двокутник вже має периметр, відмінний від 0. Це – довжина відрізку AB, яку ми обчислюємо у конструкторі Init за допомогою методу Len класу Segment. Метод AddPoint створює новий об’єкт класу Polygon, якщо нова точка T не лежить на одній прямій з відрізком AB. Інакше AddPoint розглядає три випадки розташування точки T та відрізку AB на одній прямій (Рис. 11.5). Точка T може лежати або всередині відрізку AB (тоді опукла оболонка не змінюється), або поза відрізком AB ближче до точки A (тоді маємо нову двоточкову оболонку з точками B та T), або поза відрізком AB ближче до точки B (тоді маємо нову двоточкову оболонку з точками A та T).

 

Рис. 11.5 – Розташування точки та відрізку на одній прямій

 

      Клас Polygon є, безумовно, найскладнішим з точки зору реалізації. Вершини многокутника будемо зберігати у деку. Послідовність вершин у деку будемо підтримувати так, щоб ребра (якщо їх розглядати як вектори) були спрямовані проти годинникової стрілки (Рис. 11.6). У кожний момент часу одне з ребер будемо вважати „поточним”. Нехай це ребро буде спрямоване від точки, що лежить у кінці деку, до точки, що лежить на початку деку.

 

Рис. 11.6 – Впорядкування вершин многокутника

 

      Для визначення порядку вершин у новому многокутнику (трикутнику) а також для додавання точки до многокутника розглянемо поняття „видимості” ребра многокутника з точки. Будемо вважати, що ребро AB є видимим з точки T, якщо точка T лежить на продовженні ребра AB або у трикутнику TAB ребра (як вектори) спрямовані за годинниковою стрілкою (Рис. 11.7 а) ) та невидимим, якщо T лежить всередині ребра AB або у трикутнику TAB ребра спрямовані проти годинникової стрілки (Рис. 11.7 б) ). Якщо точка T не лежить на одній прямій з AB, то ребро AB є видимим з точки T, якщо площа трикутника TAB зі знаком більша 0. Видимість ребра (відрізку S) з точки T перевіряє булівська функція Visible, яка є внутрішньою функцією модуля Conv та не відноситься до жодного з класів.

 

Рис. 11.7 – Видимість ребра з точки

 

      Клас Polygon є нащадком Convex та має одну власну властивість Dq – дек та методи Init - почати роботу CurSide - повернути поточне ребро, NextSide – перейти до наступного ребра; PreviousSide – перейти до попереднього ребра, AddPoint - додати точку, Show - показати опуклу оболонку, деструктор Done.

      Конструктор Init створює трикутник з точок T1, T2, T3, які не лежать на одній прямій. Треба розташувати точки у деку так, щоб ребра були спрямовані проти годинникової стрілки. Використовуємо 3 допоміжних відрізки: s1 (T1T2), s2 (T1T3), s3 (T2T3) та обчислюємо площу та периметр трикутника. Далі кладемо точку T2 до початку деку. Якщо ребро T1T3 є видимим з точки T2, то треба покласти точку T1 до початку деку, а точку T3 - до кінця деку, інакше – навпаки.

      Метод CurSide повертає відрізок, який є поточним ребром многокутника. Щоб не змінювати дек, спочатку беремо точки з нього, а потім кладемо на місце. Методи NextSide та PreviousSide забезпечують переміщення по многокутнику, змінюючи його поточне ребро. Метод Show перекладає по черзі всі точки многокутника з початку деку до кінця деку, показуючи на екрані їх координати. Деструктор Done очищує дек, видаляючи з нього всі точки.

      Нарешті, метод AddPoint додає нову точку до многокутника (Рис 11.8). При додаванні нової точки можливі два випадки:

1)    точка T лежить всередині многокутника або на одному з його ребер (Рис. 11.8 а) );

2)    точка T лежить ззовні многокутника (Рис. 11.8 б) ).

 

Рис. 11.8 – Додавання точки до многокутника

 

Якщо маємо випадок 1), то жодне з ребер многокутника не є видимим і опуклу оболонку змінювати не потрібно. У випадку 2) треба зробити такі дії:

·         видалити з многокутника всі видимі з точки T ребра (A1A2, A2A3, A3A4);

·         додати до многокутника два ребра з точки T до граничних точок (A1T, TA4);

·         переобчислити кількість кутів, площу та периметр многокутника.

Видалення видимих ребер забезпечується видаленням з деку вершин (A2, A3). Додавання ребер – розміщенням у деку нової точки T. Переобчислення кількості кутів n, периметру p та площі s виконується за правилами:

 

      n = n – ndel + 2,

      p = p – pdel + l1 + l2,

      s = s + sdel,

 

де ndel – кількість видалених ребер, pdel – сумарна довжина ребер, що видаляються (A1A2, A2A3, A3A4), l1 – довжина відрізку A1T, l2 – довжина відрізку TA4, sdel – сума площ трикутників, які утворює точка T з ребрами, що видаляються (DTA1A2 DTA2A3, DTA3A4).

      У методі AddPoint ми враховуємо, що поточне ребро може бути як видимим, так і невидимим з точки T. Тому спочатку пропускаємо всі видимі ребра (якщо такі є). Потім пропускаємо всі невидимі ребра. Після цього ми або пройшли всі ребра та не знайшли ребро, яке є видимим з точки T (це означає, що T лежить всередині многокутника), або поточним є перше видиме з точки T ребро. Якщо T лежить ззовні многокутника, то виконуємо видалення видимих ребер до першого ребра, яке не є видимим з точки T, додавання нових ребер та переобчислення характеристик опуклої оболонки.

 

 

{ Модуль опису класів опуклої оболонки }

Unit Conv;

 

interface

 

Uses AnyThing, Geom, AnyDeque;

 

Type

  ConvRef = ^Convex;

  Convex = Object                                          { нулькутник }

            private

             NAngle: Integer;                              { кількість кутів }

             Per,Sq: Real;                                 { периметр та площа }

            public

             Constructor Init;                             { почати роботу }

             Function GetNAngle: Integer;                  { кількість кутів }

             Function GetPer: Real;                        { периметр }

             Function GetSq: Real;                         { площа }

             Function AddPoint(T: Point): ConvRef; Virtual;{ додати точку }

             Procedure Show; Virtual;                      { показати }

             Destructor Done; Virtual;                     { закінчити роботу }

           end;

  OneRef = ^OneConv;

  OneConv = Object(Convex)                                  { однокутник }

             private

              A: Point;                                     { точка }

             public

              Constructor Init(T: Point);                   { почати роботу }

              Procedure GetA(Var T: Point);                 { точка A }

              Function AddPoint(T: Point): ConvRef; Virtual;{ додати точку }

              Procedure Show; Virtual;                      { показати }

              Destructor Done; Virtual;                     { закінчити роботу }

            end;

  TwoRef = ^TwoConv;

  TwoConv = Object(OneConv)                                 { двокутник }

             private

              B: Point;                                     { друга точка }

             public

              Constructor Init(T1,T2: Point);               { почати роботу }

              Procedure GetB(Var T: Point);                 { точка B }

              Function AddPoint(T: Point): ConvRef; Virtual;{ додати точку }

              Procedure Show; Virtual;                      { показати }

              Destructor Done; Virtual;                     { закінчити роботу }

            end;

  PolyRef = ^Polygon;

  Polygon = Object(Convex)                                  { многокутник }

             private

              Dq: Deque;                                    { дек точок }

             public

              Constructor Init(T1,T2,T3: Point);            { почати роботу }

              Procedure CurSide(Var S: Segment);            { поточне ребро }

              Procedure NextSide;                           { наступне ребро }

              Procedure PreviousSide;                       { попереднє ребро }

              Function AddPoint(T: Point): ConvRef; Virtual;{ додати точку }

              Procedure Show; Virtual;                      { показати }

              Destructor Done; Virtual;                     { закінчити роботу }

            end;

 

implementation

 

  Constructor Convex.Init;

    begin

      NAngle:=0; Per:=0; Sq:=0

    end;

 

  Function Convex.GetPer;

    begin

      GetPer:=Per

    end;

 

  Function Convex.GetNAngle;

    begin

      GetNAngle:=NAngle

    end;

 

  Function Convex.GetSq;

    begin

      GetSq:=Sq

    end;

 

  Function Convex.AddPoint;

    var p: OneRef;

    begin

      p:=ConvRef(@Self);

      AddPoint:=New(OneRef,Init(T));  { почати роботу однокутника }

      dispose(p,Done);

    end;

 

  Procedure Convex.Show;

    begin

      writeln('P = ',Per:7:1);

      writeln('S = ',Sq:7:1);

    end;

 

  Destructor Convex.Done;

    begin end;

 

  Constructor OneConv.Init;

    begin

      Convex.Init; NAngle:=1;

      A.Init(T.GetX,T.GetY);

    end;

 

  Procedure OneConv.GetA;

    begin

      T.Init(A.GetX,A.GetY)

    end;

 

  Function OneConv.AddPoint;

    var p: OneRef;

    begin

      p:=OneRef(@Self);

      if (A.GetX <> T.GetX) or (A.GetY <> T.GetY) then begin

        AddPoint:=New(TwoRef,Init(A,T)); { почати роботу двокутника }

        dispose(p,done) end

      else AddPoint:=p

    end;

 

  Procedure OneConv.Show;

    begin

      writeln('( ',A.GetX:7:1,',',A.GetY:7:1,' )');

      Convex.Show;

    end;

 

  Destructor OneConv.Done;

    begin end;

 

  Constructor TwoConv.Init;

    var s: Segment;

    begin

      OneConv.Init(T1); NAngle:=2;

      B.Init(T2.GetX,T2.GetY);

      s.Init(A,B); Per:=s.Len;

    end;

 

  Procedure TwoConv.GetB;

    begin

      T.Init(B.GetX,B.GetY)

    end;

 

  Function TwoConv.AddPoint;

    var s,s1,s2: Segment;

        p: TwoRef;

    begin

      p:=TwoRef(@Self);

      s.Init(A,B); s1.Init(A,T); s2.Init(B,T);

      if s.Online(T) then                { точки T,A,B на одній прямій ? }

        begin

          if s1.Into(B) then B:=T        { точка B лежить всередині A,T ? }

          else if s2.Into(A) then A:=T;  { точка A лежить всередині B,T ? }

          s.Init(A,B); Per:=s.Len; AddPoint:=p

        end

      else begin

         AddPoint:=New(PolyRef,Init(A,B,T)); { почати роботу многокутника }

         dispose(p,done)

      end

    end;

 

  Procedure TwoConv.Show;

    begin

      writeln('( ',B.GetX:7:1,',',B.GetY:7:1,' )');

      OneConv.Show;

    end;

 

  Destructor TwoConv.Done;

    begin end;

 

  Function Visible(S: Segment; T: Point): Boolean;

    begin

      Visible:=(S.Online(T) and not S.Into(T)) or (S.Square(T) > 0)

    end;

 

  Constructor Polygon.Init;

    var s1,s2,s3: Segment;

    begin

      s1.Init(T1,T2); s2.Init(T1,T3); s3.Init(T2,T3);

      NAngle:=3; Per:=s1.Len+s2.Len+s3.len; Sq:=abs(s1.Square(T3));

      Dq.Init;

      Dq.PutBg(New(PointRef,Init(T2.GetX,T2.GetY))); { покласти T2 до початку }

      if Visible(s2,T2) then { ребро T1,T3 видиме з точки T2 ? }

        begin

          Dq.PutBg(New(PointRef,Init(T1.GetX,T1.GetY))); { покласти T1 до початку }

          Dq.PutEn(New(PointRef,Init(T3.GetX,T3.GetY))) { покласти T3 до кінця }

        end

      else

        begin

          Dq.PutBg(New(PointRef,Init(T3.GetX,T3.GetY))); { покласти T3 до початку }

          Dq.PutEn(New(PointRef,Init(T1.GetX,T1.GetY))) { покласти T1 до кінця }

        end

    end;

 

  Procedure Polygon.CurSide;

    var p1,p2: PointRef;

    begin

      p1:=PointRef(Dq.GetEn); p2:=PointRef(Dq.GetBg);

      S.Init(p1^,p2^);

      Dq.PutEn(p1); Dq.PutBg(p2)

    end;

 

  Procedure Polygon.NextSide;

    begin

      Dq.PutEn(Dq.GetBg);

    end;

 

  Procedure Polygon.PreviousSide;

    begin

      Dq.PutBg(Dq.GetEn);

    end;

 

  Function Polygon.AddPoint;

    var s: Segment; p: PointRef;

        ndel,i: Integer; vis: Boolean;

        pdel: Real;  {сумарна довжина ребер, що видаляються}

        sdel: Real;  {площа над ребрами, що видаляються}

    begin

      Polygon.CurSide(s); vis:=Visible(s,T);

      i:=1;

      while (i <= NAngle) and vis do begin  { пропустити видимі ребра }

        Polygon.NextSide; Polygon.CurSide(s);

        vis:=Visible(s,T); i:=i+1

      end;

      while (i <= NAngle) and not vis do begin { пропустити невидимі ребра }

        Polygon.NextSide; Polygon.CurSide(s);

        vis:=Visible(s,T); i:=i+1

      end;

      if vis then begin

        ndel:=1; pdel:=s.Len; sdel:=s.Square(T);

        while true do begin          { видалити видимі ребра з многокутника }

          Polygon.NextSide; Polygon.CurSide(s);

          if not Visible(s,T) then break;

          ndel:=ndel+1; pdel:=pdel+s.Len; sdel:=sdel+s.Square(T);

          p:=PointRef(Dq.GetEn); dispose(p,Done);

        end;

        Polygon.PreviousSide;                        {повернутися на крок назад}

        Dq.PutBg(New(PointRef,Init(T.GetX,T.GetY))); { покласти T до початку }

        Polygon.CurSide(s); Per:=Per-pdel+s.Len;

        Polygon.NextSide; Polygon.CurSide(s); Per:=Per+s.Len;

        NAngle:=NAngle-ndel+2; Sq:=Sq+sdel;

      end;

      AddPoint:=@Self

    end;

 

  Procedure Polygon.Show;

    var p: PointRef; i: Integer;

    begin

      for i:=1 to NAngle do begin

        p:=PointRef(Dq.GetBg);

        writeln('( ',p^.GetX:7:1,',',p^.GetY:7:1,' )');

        Dq.PutEn(p);

      end;

      Convex.Show;

    end;

 

  Destructor Polygon.Done;

    begin

      Dq.Clear

    end;

 

end.

 

 

      Програма ConvCalc створює опуклу оболонку як динамічний об’єкт, організовує введення послідовності координат точок, що додаються до опуклої обоолонки, показує опуклу оболонку, після чого знищує її. Введення послідовності координат точок здійснюється до кінця стандартного вхідного файлу input (до натиснення комбінації клавіш <Ctrl>+<Z>). Оскільки методи AddPoint, Show, Done є віртуальнимим, обробка опуклої оболонки здійснюється незалежно від її виду.

 

 

Program ConvCalc;

 

Uses AnyThing,Geom,AnyDeque,Conv;

 

Var

  cv: ConvRef;

  t: Point; x,y: Real;

 

  begin

    writeln('Введіть послідовність точок для побудови опуклої оболонки');

    New(cv,Init);                     { почати роботу з опуклою оболонкою }

    while true do begin               { цикл с виходом }

      write('Введіть координати точки: '); Readln(x,y);

      if eof then break;              { якщо кінець введення, то вихід }

      t.Init(x,y);

      cv:=cv^.AddPoint(t);            { додати точку }

    end;

    writeln('Опукла оболонка:');

    cv^.Show;                         { показати опуклу оболонку }

    dispose(cv,Done);                 { видалити опуклу оболонку }

  end.

 

 

 

Задачі та вправи

 

      Вправа 11.1. Реалізувати методи PutEn та GetEn для деків у Паскалі та C++.

 

      Вправа 11.2. Реалізувати проект „опукла оболонка” у мові C++.

 

      Вправа 11.3. Реалізувати додатковий клас (класи) та змінити реалізацію методів Show (можливо, додати нові властивості) для показу опуклої оболонки у графічному режимі.

 

[ЗМІСТ]      [Назад]      [Початок розділу]