Олимпийские кольца
Пока олимпиада еще не закончилась в голову пришла мыль с помощью Delphi нарисовать олимпийские кольца. Для рисования используем Direct2D, для анимации Windows Animation Manager. Для уменьшения размера исполняемого файла - реализуем программу без использования VCL.
На рисунке выше скриншот формы с кольцами. Оригинал рисунка колец можно найти в Википедии. Толщина кольца равняется 1/5 радиуса. Расстояние между кольцами равны толщине. Особо исходник приводить не буду, но опишу ключевые моменты программы, которые могут быть интересны. При старте программы кольца появляются друг за другом, и при этом вращаются. Исходниый код проекта и скомпилированный файл в конце статьи. Размер exe-файла - 190Кбайт.
А теперь о самой программе и реализации:
- Чтобы нарисовать кольцо нам необходимы два эллипса разных радиусов - ID2D1EllipseGeometry. Две этих фигуры мы можем с помощью ID2D1PathGeometry преобразовать в кольцо. Для чего следует использовать метод CombineWithGeometry с параметром D2D1_COMBINE_MODE_EXCLUDE, что позволяет вырезать из большего круга малый.
- Далее обращаем внимание на прорези в кольцах. Они не прямые, а как бы делаются с помощью кольца большей толщины. Для этих целей создаем фрагмент такого кольца. Во первых создаем само кольцо аналогичным образом. Вторым шагом создаем с помощью ID2D1PathGeometry треугольник с центром в центре кольца, и еще две точки лежат на касательной к кольцу. Здесь опять используем ID2D1PathGeometry и пополняем его точками с помощью AddLine. После этого вновь используем PathGeomtry и пересекаем кольцо и треугольник. В результате имеем сектор кольца.
- Теперь с помощью этого сегмента проделаываем прорези в наших кольцах. Для этого сегмент надо передвигать и поворачивать. А потом применять EXCLUDE к этим фигурам. Таким образом создаем 8 прорезей в 5 кольцах.
- Размытие за кольцами рисуется с помощью круговой градиентной кисти. Сначала необходимо прорисовать все "тени", итолько потом кольца. Иначе в прорезях будут "артефакты".
- Каждое кольцо реализуется с помощью класса TRing, и возвращает матрицу преобразования, необхимую для позицинирования каждого из колец. Т.е. для переноса кольца на свое место TD2DMatrix3x2F.Translation.
- При выводе композиции из колец мы центрируем ее. Для этого опять используем Translation. Кроме этого при увеличении формы мы увеличиваем и кольца. Для этого опять таки используем матрицы искажений, в данном случае - TD2DMatrix3x2F.Scale. Это очень упрощает работу. Только задумайтесь - для масштабирования того, что отрисовываем, нам нужно только поределить матрицу с коэффициентом умножения. Параметром матрицы является коэффициент увелчения и точка, относительно которой увеличение происходит. В нашем случае это центр формы.
- При непосредственной отрисовке кольца нам необходимо собрать все эти матрицы воедино, чтобы получить конечную позицию. Для этого конечно же их необходимо просто перемножить. Поэтому мы умножаем матрицы центрирования в форме, увеличения по размерам формы, матрицу позиционирования каждого из колец. А еще у нас есть матрица поворота кольца (в начале я писал, что кольца анимируются - вращаются).
- При анимации колец используются две переменные - угол поворота и прозрачность (на картинке пятое кольцо приведено в повороте, и полупрозрачно). Для поворота исопользуется CreateAccelerateDecelerateTransition, поэтому сначала кольцо разгоняется, а потом замедляется. Для прозрачности - линеная функция CreateLinearTransition. Обе переменные анимируются одновременно. Первая просто добавляется в план - StoryBoard, вторая добавляется в начало - AddTransitionAtKeyframe с параметром UI_ANIMATION_KEYFRAME_STORYBOARD_START.
- Анимация проводится для всех 5 колец. Поэтому после анимирования первой переменной добавляется ключевой кадр. И анимация запускается в цикл 5 раз,для чего применяется метод RepeatBetweenKeyframes. В текущей версии Windows Animation Manager нет событий преднозначенных для отлавливания итераций. В Windows 8 такой функционал уже будет. Но нам необходимо эти итерации отслеживать, поскольку переход к новой итерации обозначает, что анимируется новое кольцо, и необходимо переключать индекс анимируемого кольца. Для отслеживание итераций применяется событие изменения значение анимируемой переменной - OnValueChanged. Кольцо делает 4 оборота. Стартовое значение 360*4. Конечное значение 0. Следовательно каждое последюущее значение меньше предыдущего. Нарушение этого правила и есть смена итерации.
- При использовании обычных VCL форм размер скомпилированного файла в Delphi 2010 - 800Кбайт. В Delphi Xe2 - 1300Кбайт. Что выглядит слишком большим, при пустой то форме. Поэтому я реализовал приложение без испоьзования VCL. Конечный размер файла стал равен 190Кбайтам.
- Создать оконное приложение без использования обычных форм достаточно просто. Для этого нам необходимо вызвать функцию 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 можно посмотреть в исходном коде. Так же можете скачать скомпилированный пример. Большой скриншот колец ниже (кликабелен).