Delphi programming blog
Источник: http://teran.karelia.pro/articles/item_5548.html
 

Олимпийские кольца

Опубликовано 12.08.2012 г. 20:48

Пока олимпиада еще не закончилась в голову пришла мыль с помощью Delphi нарисовать олимпийские кольца. Для рисования используем Direct2D, для анимации Windows Animation Manager. Для уменьшения размера исполняемого файла - реализуем программу без использования VCL.

На рисунке выше скриншот формы с кольцами. Оригинал рисунка колец можно найти в Википедии. Толщина кольца равняется 1/5 радиуса. Расстояние между кольцами равны толщине. Особо исходник приводить не буду, но опишу ключевые моменты программы, которые могут быть интересны. При старте программы кольца появляются друг за другом, и при этом вращаются. Исходниый код проекта и скомпилированный файл в конце статьи. Размер exe-файла - 190Кбайт.

А теперь о самой программе и реализации:

  1. Чтобы нарисовать кольцо нам необходимы два эллипса разных радиусов - ID2D1EllipseGeometry. Две этих фигуры мы можем с помощью ID2D1PathGeometry преобразовать в кольцо. Для чего следует использовать метод CombineWithGeometry с параметром  D2D1_COMBINE_MODE_EXCLUDE, что позволяет вырезать из большего круга малый.
  2. Далее обращаем внимание на прорези в кольцах. Они не прямые, а как бы делаются с помощью кольца большей толщины. Для этих целей создаем фрагмент такого кольца. Во первых создаем само кольцо аналогичным образом. Вторым шагом создаем с помощью ID2D1PathGeometry треугольник с центром в центре кольца, и еще две точки лежат на касательной к кольцу. Здесь опять используем ID2D1PathGeometry и пополняем его точками с помощью AddLine. После этого вновь используем PathGeomtry и пересекаем кольцо и треугольник. В результате имеем сектор кольца.
  3. Теперь с помощью этого сегмента проделаываем прорези в наших кольцах. Для этого сегмент надо передвигать и поворачивать. А потом применять EXCLUDE к этим фигурам. Таким образом создаем 8 прорезей в 5 кольцах.
  4. Размытие за кольцами рисуется с помощью круговой градиентной кисти. Сначала необходимо прорисовать все "тени", итолько  потом кольца. Иначе в прорезях будут "артефакты".
  5. Каждое кольцо реализуется с помощью класса TRing, и возвращает матрицу преобразования, необхимую для позицинирования каждого из колец. Т.е. для переноса кольца на свое место TD2DMatrix3x2F.Translation.
  6. При выводе композиции из колец мы центрируем ее. Для этого опять используем Translation. Кроме этого при увеличении формы мы увеличиваем и кольца. Для этого опять таки используем матрицы искажений, в данном случае - TD2DMatrix3x2F.Scale. Это очень упрощает работу. Только задумайтесь - для масштабирования того, что отрисовываем, нам нужно только поределить матрицу с коэффициентом умножения.  Параметром матрицы является коэффициент увелчения и точка, относительно которой увеличение происходит. В нашем случае это центр формы.
  7. При непосредственной отрисовке кольца нам необходимо собрать все эти матрицы воедино, чтобы получить конечную позицию. Для этого конечно же их необходимо просто перемножить. Поэтому мы умножаем матрицы центрирования в форме, увеличения по размерам формы, матрицу позиционирования каждого из колец. А еще у нас есть матрица поворота кольца (в начале я писал, что кольца анимируются - вращаются).
  8. При анимации колец используются две переменные - угол поворота и прозрачность (на картинке пятое кольцо приведено в повороте, и полупрозрачно). Для поворота исопользуется CreateAccelerateDecelerateTransition, поэтому сначала кольцо разгоняется, а потом замедляется. Для прозрачности - линеная функция CreateLinearTransition. Обе переменные анимируются одновременно. Первая просто добавляется в план - StoryBoard, вторая добавляется в начало - AddTransitionAtKeyframe с параметром UI_ANIMATION_KEYFRAME_STORYBOARD_START.
  9. Анимация проводится для всех 5 колец. Поэтому после анимирования первой переменной добавляется ключевой кадр. И анимация запускается в цикл 5 раз,для чего применяется  метод RepeatBetweenKeyframes. В текущей версии Windows Animation Manager нет событий преднозначенных для отлавливания итераций. В Windows 8 такой функционал уже будет. Но нам необходимо эти итерации отслеживать, поскольку переход к новой итерации обозначает, что анимируется новое кольцо, и необходимо переключать индекс анимируемого кольца. Для отслеживание итераций применяется событие изменения значение анимируемой переменной - OnValueChanged. Кольцо делает 4 оборота. Стартовое значение 360*4. Конечное значение 0. Следовательно каждое последюущее значение меньше предыдущего. Нарушение этого правила и есть смена итерации.
  10. При  использовании обычных VCL форм размер скомпилированного файла в Delphi 2010 - 800Кбайт. В Delphi Xe2 - 1300Кбайт. Что выглядит слишком большим, при пустой то форме. Поэтому я реализовал приложение без испоьзования VCL. Конечный размер файла стал равен 190Кбайтам.
  11. Создать оконное приложение без использования обычных форм достаточно просто. Для этого нам необходимо вызвать функцию CreateWindow для непосредственного создания окна, а также описать функцию обработки сообщений. Для этих целей реализована пара классов. Один - TWindow отвечает просто за создание окна и пересылку сообщений. Второй TORWindow - отвечает уже за рисование колец и т.п. Оконная функция выполнена в виде статичного метода класса. А для передачи сообщений используется стандартный TObject.Dispatch:
    class function TWindow.WndProc(windowHandle: HWND; uMsg: Cardinal; wp: WPARAM; lp: LPARAM): LRESULT;
    var wm : TMessage;
    begin
        result := DefWindowProc(windowHandle, uMsg, wp, lp);
    
        case uMsg of
            WM_DESTROY,
            WM_PAINT,
            WM_SIZE,
            WM_ERASEBKGND : begin
                    wm.Msg := uMsg;
                    wm.WParam := wp;
                    wm.LParam := lp;
                    wm.Result := result;
    
                    Window.Dispatch(wm);
                    result := wm.Result;
            end;
        end;
    end;
    Здесь Window возвращает созданный экземпляр окна. А обработа обработка сообщений выполняется стандартным образом:
            procedure WMEraseBkgnd(var message : TWMEraseBkgnd); message WM_ERASEBKGND;
            procedure wmSize(var message : TWMSize); message WM_SIZE;
            procedure wmPaint(var message : TWMPaint); message WM_PAINT;
            procedure wmDestroy(var message  : TWMDestroy); message WM_DESTROY;
    
    Нам необходимы всего несколько сообщений: отрисовка, изменение размера, закрытие окна.

Вот наверное и все, что о программе интерено было рассказать. Непосредственную реализацию и отрисовки в Direct2D, и анимации с Windws Animation Manager, и создание программы без VCL можно посмотреть в  исходном коде. Так же можете скачать скомпилированный пример. Большой скриншот колец ниже (кликабелен).

 

Метки:  windows 7  |  Direct2D  |  animation 

Комментарии

Нет комментариев
- Имя
- e-mail*
- Сайт
вы можете использовать теги [i],[b],[code],[quote]
Дополнительно