Что такое динамическое изображение
Динамическое изображение
Динамическое изображение отличается от статического тем, что данное изображение изменяется, его состояние определяется контролируемым процессом. Изображение может перемещаться, изменять размеры, поворачиваться, может происходить перемещение пунктиров по его контору, объект может быть заполнен до определенного уровня.
Для получения динамического изображения всегда необходима привязка к аргументу, значение которого отображается тем или иным способом.
При динамическом контуре (закладка динамический контур ) задаются (рис. 2.10) два цвета: цвет штрихов и промежутка между ними, длина штриха, которая также определяет шаг перемещения штрихов. Происходит перемещение штрихов по контору. Скорость их перемещения определяется привязанным аргументом. Если аргумент равен 0, то перемещение отсутствует. Когда аргумент равен 1 происходит перемещение штрихов при каждом такте на один шаг. Если аргумент равен двум, к примеру, то штрихи перемещаются на один шаг один раз за 2 такта.
|
Рис. 2.10 Настройка динамического контура Рассмотрим динамическую трансформацию (закладка динамческая
трансформация .). Можно выделить динамическое перемещение, масштабирование, вращение, поставив соответствующий флаг. При
динамическом перемещении задается ломаная линия, вдоль которой происходит перемещение. Для ряда точек (узлов) задаются соответствующие им значения (рис. 2.11). Текущее положение объекта зависит от значения привязанного аргумента и значений, соответствующих узлам, флага перемещать плавно.
|
Рис. 2.11 Динамическое перемещение
При динамизации масштабирования задаются начальный и конечный размер объекта и значения привязанного аргумента, соответствующие заданным значениям (рис. 2.12) и центр относительно которого будет происходить масштабирование. Текущий размер зависит от значения аргумента.
|
Рис. 2.12 Динамическое перемещение
При динамическом вращении задается начальный и конечный угол, значения аргумента, соответствующие начальному и конечному углу, центр относительно которого происходит вращение. Угловое положение в данном случае зависит от значения аргумента. Настройка вращения происходит аналогично настройке перемещения.
Рассмотрим подробнее динамическую заливку. Динамическая заливка заключается в том, что задается максимальное и минимальное значение привязываемого аргумента (рис. 2.13). Происходит заливка объекта до уровня, который определяется привязанным аргументом. Заливка может однослойной (отображается значение одного аргумента) и многослойной (отображение значений нескольких аргументов).
Можно настроить изменение цвета динамической заливки в зависимости от состояния технологического процесса (предупреждение, авария, вне границ). Для этого необходимо выбрать цвета заполнения и выбрать значение true в поле цвета для диапазонов.
Для настройки зависимости цвета заливки объекта (объект без динамической заливки) от состояния процесса на закладке основные свойства в разделе заливка следует раскрыть раздел цвет заливки, где можно выбрать вид индикации, выбрать цвета заливки (рис. 2.14), задать диапазон.
Язык Java и его возможности
Часть III. Динамические изображения
В предыдущих публикациях мы уже неоднократно говорили, что главными достоинствами программ-аплетов следует назвать их интерактивность и динамичность. Сегодня мы рассмотрим подробнее, как именно создаются на Java динамически меняющиеся изображения.
Известно, что создание любой анимационной картинки опирается на средства статической графики и эффективный механизм замены отдельных кадров.
С наиболее важными принципами организации графики в Java мы уже знакомы из части I. Что касается второй составляющей проблемы, т.е. реализации замены картинок с течением времени, то для этой цели (кстати, и не только для нее одной!) в Java существует очень мощный механизм — потоки. Понимание базовых идей организации потоков в современной информатике трудно переоценить, поскольку одновременное параллельное исполнение нескольких процессов есть одно из магистральных направлений совершенствования как software, так и hardware. Вспомним хотя бы о многопоточности современных операционных систем или о многоядерных микропроцессорах, где каждое отдельное ядро занято решением некоторой подзадачи. Язык Java, с самого начала созданный в качестве многопоточного (см. часть I), предоставляет нам широкое поле для изучения потоков. В большинстве примеров мы ограничимся одним потоком, но последняя весьма эффектная учебная задача даст нам повод поэкспериментировать с несколькими самостоятельными потоками.
Как читатели уже, наверное, догадались, разделение свойств интерактивности и динамичности (в терминах данной публикации это части II и III) удается осуществить благодаря относительной независимости средств их реализации. Именно поэтому приводимые ниже примеры сознательно лишены интерактивности: общепринятая в педагогике точка зрения советует при первоначальном изучении явления по возможности сконцентрироваться на его наиболее характерных чертах. Зато, освоив по отдельности интерактивность и динамичность, читатели смогут легко объединить 1 все преимущества этих мощных технологий и создать на основе Java-аплетов учебные материалы нового поколения, возможности которых существенно выходят за рамки традиционных педагогических средств.
1. Принцип создания динамического изображения
Общая идея получения изменяющихся (движущихся) изображений довольно проста и интуитивно понятна: динамический процесс разбивают на отдельные фазы (кадры), которые в ходе демонстрации сменяются с необходимой скоростью. Сразу заметим, что представление непрерывного изменения в виде последовательности статических кадров есть не что иное, как дискретизация, поэтому такая технология прекрасно подходит для компьютера.
Конкретные способы получения кадров и их демонстрации весьма разнообразны. Например, не так давно, когда съемка кинофильмов производилась механическими камерами на специальную светочувствительную пленку, большое внимание уделялось технике транспортировки киноленты вдоль объектива. Сейчас, когда многие люди уже не встречались с подобным оборудованием, стоит напомнить, что движение киноленты реализовывалось весьма специфическим неравномерным способом: быстрая протяжка на один кадр 2 сменялась остановкой для фиксации кадра на пленке (или его демонстрации). Помимо автоматически записанного изображения, зафиксировавшего некоторую натурную съемку, в “докомпьютерном” кинематографе кадры создавались и вручную — художники-мультипликаторы мастерски рисовали различные фазы движения своих героев, а затем оператор снимал их рисунки в режиме специальной покадровой съемки. Демонстрация художественных и мультипликационных фильмов происходила совершенно одинаково.
Многочисленные эксперименты показали, что иллюзия непрерывного движения возникает при смене как минимум 10–12 кадров за секунду. В любительских киноаппаратах обычно применялась скорость 16 кадров/сек., а в профессиональных — 24.
В телевидении используются совсем другие технологические принципы создания движущихся изображений, но и там речь идет о кратковременной демонстрации на экране статических кадров. Частота смены кадров составляет 25 или 30 кадров, что очевидным образом связано с частотой питающей электросети, которая принята в тех или иных странах.
Переход к компьютерному видео не содержал принципиальных барьеров, но инженерам пришлось изрядно потрудиться, чтобы решить целый ряд сложных технологических проблем: обеспечить достаточное быстродействие видеосистемы, разместить большие объемы видеоданных на машинных носителях (увеличение объема последних, сжатие) и сконструировать каналы, способные обеспечить передачу этих данных без задержек. Типовое компьютерное оборудование, которое в настоящее время широко предлагается для домашнего использования в магазинах, решает все перечисленные задачи на вполне достаточном уровне. В результате просмотр видео на компьютере становится одним из любимых направлений его использования. Не менее важным обстоятельством являются также широчайшие возможности коррекции и редактирования компьютерной видеоинформации. В результате при профессиональном уровне работы разница между кадрами, реально отснятыми видеокамерой и откорректированными на компьютере, практически незаметна. Все больший реализм приобретает и компьютерная анимация.
Таким образом, мы видели, что для создания на экране дисплея изменяющегося изображения очень важно иметь механизм быстрой смены статических картинок. Посмотрим, как обстоит с этим дело в языке Java.
2. Потоки как средство организации динамической графики в Java
Очевидно, что для получения динамических изображений, которые будут воспроизводиться с одинаковым эффектом на разных компьютерах, необходимы специальные средства, “привязанные” не к скоростным характеристикам аппаратуры, а к реальному времени. Иными словами, если мы хотим реализовать с помощью языка универсальную не зависящую от характеристик компьютера картинку, мы должны в операторах этого языка использовать не тактовую частоту или аналогичные технические величины, а обычные секунды и их доли.
В Java имеется специальный механизм, позволяющий организовать функционирование программы в реальном времени, — это поток. Хочется подчеркнуть, что потоки, хотя и служат прекрасным средством для организации динамических изображений, созданы для гораздо более общих целей. Рассмотрим этот аспект немного подробнее.
Большинство читателей хорошо понимают, что такое многозадачность, ибо современные операционные системы существенным образом приспособлены к одновременному функционированию нескольких приложений: аудиопроигрыватель, менеджер закачек из Интернета, текстовой редактор, калькулятор и т.д. плюс сама операционная система и ее внешняя оболочка для манипуляции файлами. Но и внутри каждого крупного приложения можно также распределять работу между заданиями, которые выполнять параллельно, например, в редакторе печатать документ, вести в нем поиск и производить форматирование. Описанный способ исполнения заданий настолько важен, что его вполне можно увидеть стандартными средствами Windows. Щелкните правой кнопкой мыши по панели задач и в появившемся меню выберите пункт “Диспетчер задач”; в результате вы увидите картину, похожую на ту, что приведена на рис. 1.
Рис. 1. Диспетчер задач Windows
Но разделение задачи на параллельно исполняемые части характерно не только для программного обеспечения, но и для современной аппаратной части. Достаточно вспомнить о сопроцессорах и контроллерах, а также о многоядерных процессорах, которые на глазах превращаются из экзотики в норму.
Первый тип приложений, который выигрывает от поддержки многопоточности, предназначен для задач, где действительно требуется выполнять несколько действий одновременно, например, когда сервер обслуживает запросы нескольких клиентов. Кстати, принципиально важно, чтобы запросы были независимыми, иначе они будут мешать выполнению друг друга.
Рис. 2. Основные принципы функционрования потока
Следующий пример — активные приложения, где необходимо одновременно опрашивать клавиатуру и другие устройства ввода, чтобы реагировать на действия пользователя, и в то же время рассчитывать и перерисовывать изменяющееся состояние экрана.
Еще одно преимущество проистекает из того, что компьютер состоит не только из одного или нескольких процессоров. Вычислительное устройство — лишь один из ресурсов, необходимых для выполнения задач. Всегда есть оперативная память, дисковая подсистема, сетевые подключения, периферия и т.д. Здесь существенно, что все перечисленные устройства работают медленнее (а многие — существенно медленнее), чем процессор и, кроме того, имеют самостоятельные элементы управления (контроллеры). В результате задачи обмена данными требуют совсем незначительного участия центрального процессора, а значит, он легко справится с параллельным обслуживанием нескольких задач. Напротив, когда задачи в основном загружают процессор (например, математические расчеты), то их одновременное исполнение займет в лучшем случае столько же времени, что и последовательное, а то и больше.
Характерной чертой Java является то, что в ней изначально предусмотрена поддержка многопоточного программирования (multithread programming). Далеко не каждый язык может этим похвастаться, например, в Паскале или Бейсике ничего подобного просто нет.
Примечание. При переводе терминов Java на русский язык имеется некоторая специфическая проблема. В Java существует два разных термина — thread и stream, первый из которых фактически является потоком команд, а второй — потоком данных. Тем не менее оба этих термина обычно переводятся как “поток”, что может приводить к смешению этих довольно разных понятий. В данной статье речь везде идет только о потоках типа thread.
Полная модель обработки потоков в Java достаточно сложна. Язык позволяет описывать не просто изолированные, но взаимодействующие потоки, причем одни потоки способны управлять другими, они могут иметь разный приоритет, синхронизироваться и т.д. В данной публикации мы рассмотрим только основы работы с потоками, которых будет достаточно для создания динамической графики. Но даже в рамках этой задачи удастся посмотреть подлинно многопоточную реализацию (см. последний пример).
Как использовать поток для динамической графики
Рассмотрим теперь, как можно использовать потоковый механизм для создания в окне аплета динамически меняющегося изображения. Обратимся к рис. 2, на котором изображены основные принципы функционирования потока. Подчеркнем, что данная схема составлена на основе систематического описания в классической книге [3]; особенности версии Java 2 будут рассмотрены по ходу изложения примеров.
Любой аплет имеет некоторый набор стандартных процедур, каждая из которых играет вполне определенную роль и вызывается Java-машиной в определенное время. Названия наиболее важных процедур ( init, start и т.д.) выписаны на рис. 2; дополнительная информация о них приведена в табл. 1.
Две оставшиеся процедуры не имеют непосредственного отношения к потокам: paint ответственна за рисование изображения на экране, а action обрабатывает воздействия пользователя клавиатурой и мышью. С точки зрения изучения принципов реализации движущихся изображений необычайно важно подчеркнуть, что метод paint всегда автоматически очищает экран перед новым рисованием, поэтому программисту достаточно описывать только процесс рисования очередного кадра, не заботясь об удалении предыдущего.
Примечание. Если окно аплета велико, а реально обновляется лишь небольшая его часть, при вызове метода перерисовки имеет смысл указывать координаты обновляемого прямоугольника — остальная (бо’льшая) часть рисунка при этом сохраняется.
Отметим, что сам аплет также представляет собой некоторый базовый поток, так что создаваемый нами для нужд мультипликации поток будет уже вторым; он является дочерним, т.е. основной поток аплета управляет им и по окончании работы обеспечивает его уничтожение.
Как уже отмечалось выше, заложенная в Java 1 идеология потоков не во всем себя оправдала, так что реальные программы для потоков не столь логичны, как описанная выше картина. Приведенные в данной публикации примеры за образец принимают официальные рекомендации фирмы Sun [4]. Кроме того, если потоки работают кратковременно и по окончании цикла работы завершаются естественным образом, то можно обойтись без дополнительных мер по их остановке.
В языке Java предусмотрено два механизма создания потоков: реализация интерфейса Runnable (обычно у самого аплета) или расширение стандартного класса Thread с последующим созданием экземпляров нового класса.
В примерах 1–3 будет использован более простой первый метод, а в примере 4 продемонстрирован второй.
Итак, типичное решение задачи о создании потока для динамического изображения выглядит следующим образом.
· В методе start аплета создать поток и запустить, вызвав метод start потока.
· Если цикл воспроизведения картинки бесконечен, в методе stop предусмотреть окончание работы потока: в соответствии с рекомендациями Java 2 использовать для этой цели изменение значения переменной (детали см. в примере 1).
3. Примеры динамических изображений
Перейдем теперь к рассмотрению примеров аплетов, создающих на экране динамические изображения. Они разнообразны по реализации, так что автор надеется, что после чтения статьи читатели сумеют выбрать “стартовую точку” для реализации своего собственного аплета.
Пример 1. Реализация простейшего движения
Начнем с простейшего движущегося изображения: опишем на языке Java небольшую тележку, которая перемещается в горизонтальном направлении. В качестве простейшего алгоритма поведения предлагается модельное движение “взад-вперед” между двумя граничными точками. Возможный вид аплета в некоторый фиксированный момент времени изображен на рис. 3.
Рис. 3. Реализация простейшего движения
Обратимся теперь к листингу 1, показывающему, как выглядит решение нашей задачи.
Примечание. Время 20 мсек. было выбрано экспериментально. Оно соответствует скорости демонстрации 1000/20 = 50 кадров/сек., что надежно обеспечивает плавность движения тележки.
public class timer extends java.applet.Applet
volatile Thread mult = null; //поток для анимации
String w; //время в виде строки
public void run() //работа потока — индикация
while (mult == threadCopy) //проверка отсутствия признака его остановки
String s = d.toString();
Пример 2. Индикация времени
Изменения, происходящие на экране, не обязательно должны быть чисто графическими. В данном примере продемонстрировано, как образовать поток, который будет отображать на экране строку с текущим временем. Подчеркнем, что такой поток можно добавлять к любому аплету, в котором вы хотите индицировать время.
Рис. 4. Индикация времени
Текст программы аплета приведен в листинге 2.
Сравнение листингов 1 и 2 показывает, что значительная их часть совпадает. Причина очевидна — организация потока в новой программе точно такая же, как и в предыдущей.
Пример 3. Как сделать мультфильм
Изучив предыдущие примеры, наблюдательный читатель может заметить, что в них динамическое изображение формировалось средствами языка Java. Конечно, это тоже не так уж мало, поскольку позволяет создать строящиеся в реальном времени диаграммы или графики, всевозможные прогресс-индикаторы, динамические схемы, плавный переход от одной картинки к другой, бегущую строку, появляющиеся и исчезающие указатели и многое другое. Тем не менее определенные ограничения на “оживляемые” картинки это все-таки накладывает: попробуйте, например, операторами Java нарисовать лошадь или котенка! Иными словами, хочется иметь возможность использовать картинки, созданные вне среды Java, например, в графическом редакторе. Решению этой задачи и посвящен пример 3.
Примечание. Выбранное в условии значение 70 получено путем приблизительного деления 400 (горизонтальный размер окна аплета) на 6 (смещение объекта за один кадр — обоснование последнего значения будет дано ниже).
public class animat extends java.applet.Applet
Thread mult = null; //поток для анимации
Vector frames = new Vector(12); //для хранения кадров
Image pict; //рабочий объект для картинки
int x = 0; //координата
//Формирование кадров мультфильма
Canvas c = new Canvas(); //»холст» для рисования
pict = c.createImage(32,32); //рисунок 32×32
Если не считать отдельных деталей вроде величины смещения и цвета фона, программа в листинге 3 весьма универсальна и годится для любого мультфильма. Автор при тестировании воспользовался картинками, подготовленными в свое время к одной из своих разработок [5]. Там в одном из первых демонстрационных примеров использовались картинки катящегося пятнистого мяча (пятна, как известно, позволяют легко заметить вращение). Изначально мяч был нарисован в редакторе CorelDraw и скопирован 11 раз с поворотами, кратными 360/12 = 30 градусам. В итоге получились 12 фаз (циклически повторяющегося) движения — они приведены на рис. 5.
Рис. 5. Кадры движения катящегося мяча
Теперь можно, наконец, рассчитать, на сколько пикселей в горизонтальном направлении должен сместиться мяч при переходе от одной картинки к другой (т.е. при повороте на 30 градусов): 2R/12
R/2. Для применяемых в аплете картинок получалось значение 7 пикселей, экспериментально картинка казалась оптимальной при шести.
Примечание. Чем меньше смещение, тем “плавнее” катится мяч. Но слишком маленькой эту величину тоже делать нельзя, поскольку мяч начнет “проскальзывать”: вращение будет казаться быстрее, чем горизонтальное перемещение.
На рис. 6 приведен общий вид итогового аплета. Чувствуется, что статическая картинка на бумаге много теряет по сравнению с реальным динамическим изображением!
Рис. 6. Аплет, демонстрирующий простейший мультфильм
Пример 4. Многопотоковый аплет
Во всех рассмотренных выше примерах мы имели дело с единственным потоком, создававшим динамическое изображение. (Не стоит, правда, забывать, что запрограммированный нами поток был на самом деле вторым, поскольку сам аплет также является потоком.) В данном примере мы рассмотрим создание изображения с помощью нескольких параллельно работающих потоков.
В качестве демонстрационной задачи рассмотрим ход параллельного пучка лучей в собирающей линзе — рис. 7.
Из школьного курса физики известно, что, проходя через собирающую линзу, лучи пересекаются в характерной точке, которая называется фокусом. В нашем аплете параллельный пучок моделируется пятью лучами (считая проходящий вдоль оси x), но изменить это число не составляет никакого труда.
Помимо общей схемы движения лучей, которую мы планируем увидеть по завершении работы аплета, на рис. 7 указаны в пикселях экрана характерные размеры изображения.
Рис. 7. Схема, демонстрирующая ход лучей в собирающей линии
Несколько слов о том, откуда берется формула y = y0 * (1 – x/F), описывающая ход луча после прохождения линзы. Из математики известно, что уравнение любой не вертикальной прямой представляется в виде y = kx + b, которое справедливо в любой ее точке. Воспользуемся последним обстоятельством и запишем данное уравнение для двух характерных точек (0, y0) и (F, 0). Благодаря нулевым координатам полученная система из двух уравнений разрешается моментально; автор надеется, что среднестатистический школьник еще в состоянии это сделать.
Примечание. По мнению автора, при решении задач по информатике надо всеми силами привлекать несложные математические выкладки и расчеты (см. также вычисления времени “засыпания” потока в примере 1 и смещения катящегося мяча в примере 3). И не только потому, что классическая педагогика всегда подчеркивала важность межпредметных связей, но еще и потому, что в ходе проводимых реформ образования все чаще провозглашается невозможность постижения основ этого важного предмета учениками, выбирающими нематематические специальности. Аргументация порой доходит до курьезов. Недавно в Интернете прочел в обсуждении одной из статей, посвященных содержанию обучения, следующий пассаж. Женщина (бухгалтер по профессии!) пишет, что в школе люто ненавидела математику и до сих пор не понимает, что такое синус. Но процентную-то меру налогов она как бухгалтер не может не понимать! А синус, который есть отношение величин катета и гипотенузы, по сути мало чем отличается от начисления процентов подоходного налога, ну разве что синус не принято выражать в процентах! Конечно, это частности, но почему-то в бурно развивающейся Юго-Восточной Азии преподаванию математики и естественных наук уделяется все возрастающее внимание, а не наоборот.
Всесторонне подготовившись, можно приступать к написанию программы аплета (см. листинг 4).
public class lens extends java.applet.Applet <
class Ray extends Thread< //единичный луч
int D = 20; //задержка потока — «скорость» рисования