Автор: © Aib
Программное рисование во Flash MX. Управление кривыми. (Часть I)
Вместо вступления.
Наконец-то!!! Теперь во Flash MX мы можем
рисовать по средствам программного кода: создавать и удалять клипы, делать
различные градиентные заливки, свободно управлять размером, местоположением и
прочими характеристиками текста и, конечно же, рисовать прямые и кривые
линии.
Но вслед за восторгом от новшеств вскоре приходят некоторые
проблемы. Кривая, заданая тремя точками (начальная является концом предыдущей
линии или может быть задана с помощью метода moveTo), ведёт себя совершенно
непонятно - через точку (ControlX, ControlY) она даже не проходит, и может
возникнуть проблема с рисованием даже простой окружности.
Не спешите
"выбрасывать на свалку" инструмент curveTo, отчаявшись научиться им
управлять. В этой статье будет рассказано о том, что же рисует curveTo, а также
о том, как можно облегчить себе жизнь, создав несколько собственных инструментов
рисования.
Что рисует curveTo().
По сути данная кривая
всего-навсего CV NURBS из трёх точек, каждая с весом 1, кривая степени 2. Для
тех, кто изучал 3D-Studio MAX R2 или выше или какую-либо другую программу
создания 3D-графики и анимации этого достаточно (Poser и Bryce3D в расчёт не
беруться, т.к. Я их не видел и наличие там NURBS гарантировать не могу). Для
всех остальных потребуются пояснения. Control Vertex Non-Uniform Rational Basic
Splines (сокращённо CV NURBS) - это линии, состоящие из частей кривых различного
порядка (т.е. заданых математическими уравнениями некоторой степени), форма
которых определяется направляющими векторами, концы которых находятся в
задаваемых пользователем точках. В Flash MX эти составляющие имеют степень 2,
т.е. это могут быть параболы, гиперболы и эллипсы. Но так как вес точки
(коэффициэнт, влияющий на то, как близко к этой точке подходит кривая) постоянен
и равен единице, то линия, создаваемая curveTo() является частью параболы. Из
этого следует, что просто нарисовать обычную окружность или эллипс нельзя, а
можно лишь нарисовать кривую, которая будет на них похожа. Ввиду ограниченности
кол-ва задаваемых точек, сложную кривую нужно создавать по частям, рассчитывая
точки так, чтобы не было изломов. Однако есть и свои плюсы. Направляющие векторы
в нашем случае - это касательные к параболе в двух точках её графика, а точка
(ControlX, ControlY) - пересечение этих касательных.
Это позволяет получить условие построения более сложной
кривой без изломов. Для этого нужно, чтобы контрольная (сontrol) и якорная
(anchor) точки первой кривой и контрольная точка второй кривой находились на
одной прямой.
Однако хотелось бы, чтобы Flash сам
рисовал кривые кривые любой сложности, а нам лишь нужно было задавать
контрольные точки. Поэтому переходим к следующему пункту...
Метод
рисование сложной кривой multicurveTo().
Данный метод позволит нам
рисовать почти полноценный CV NURBS. Почти, потому что мы не сможем менять вес
точки (всегда равный единице) и устанавливать степень кривой больше 2. Будет ещё
одна оговорка, но о ней чуть позднее. Итак, программный код
метода:
function Multicurve(Xargs, Yargs, closed){ var Xmid =
Xargs.slice(0); var Ymid = Yargs.slice(0); if ((Xmid.length !=
Ymid.length) || (Xmid.length < 2)){ trace('Wrong
Arguments'); return this }; if (Xmid.length ==
2){ this.moveTo(Xmid[0], Ymid[0]); this.lineTo(Xmid[1],
Ymid[1]); delete Xmid; delete Ymid; return
this; }; var Xpoint = new Array(); var Ypoint = new
Array(); for (var i = 1; i < Xmid.length-2; i++){ Xpoint[i] =
0.5*(Xmid[i+1]+Xmid[i]); Ypoint[i] =
0.5*(Ymid[i+1]+Ymid[i]); }; if (closed){ Xpoint[0] =
0.5*(Xmid[1]+Xmid[0]); Ypoint[0] =
0.5*(Ymid[1]+Ymid[0]); Xpoint[i] =
0.5*(Xmid[i+1]+Xmid[i]); Ypoint[i] =
0.5*(Ymid[i+1]+Ymid[i]); Xpoint[i+1] =
0.5*(Xmid[i+1]+Xmid[0]); Ypoint[i+1] =
0.5*(Ymid[i+1]+Ymid[0]); Xmid[i+2] = Xmid[0]; Ymid[i+2] =
Ymid[0]; Xpoint[i+2] = Xpoint[0]; Ypoint[i+2] = Ypoint[0]; }
else { Xpoint[0] = Xmid[0]; Ypoint[0] = Ymid[0]; Xpoint[i]
= Xmid[i+1]; Ypoint[i] =
Ymid[i+1]; Xmid.pop(); Ymid.pop(); }; this.moveTo
(Xpoint[0], Ypoint[0]); for (var i = 1; i < Xmid.length;
i++){ this.curveTo(Xmid[i], Ymid[i], Xpoint[i],
Ypoint[i]); }; delete Xmid; delete Ymid; delete
Xpoint; delete Ypoint; return
this; } Object.prototype.multicurveTo = Multicurve;
Теперь разберём
всё по порядку:
function Multicurve(Xargs, Yargs, closed){
В
качестве аргументов передаём два массива - координаты контрольных точек кривой
по х и по у, а также параметр closed, который показывает, какую мы хотим
получить кривую. Если кривая замкнута, то линия не имеет начала и конца, все
заданные точки используются как контрольные (значение closed равно true), в
противном случае нулевая (исходя из индекса в массиве) и последняя заданные
точки являются началом и концом кривой(значение closed равно false). Можно было
бы сделать проверку на замкнутость автоматической, но иногда бывают случаи,
когда нужно, чтобы линия имела совпадающие начало и конец.
var Xmid =
Xargs.slice(0); var Ymid = Yargs.slice(0);
Возможно, Вам в
дальнейшем потребуются массивы, которые вы задали как параметры функции. Если
производить с аргументами Xargs и Yargs какие-нибудь преобразования, то
изменятся и сами передаваемые массивы. Поэтому создаются их локальные копии (на
локальность указывает слово var). Если Вы уверены, что передаваемые массивы
нигде больше использоваться не будут, можно удалить эти две строчки, а первую
строку записать как function Multicurve(Xmid, Ymid, closed){.
if
((Xmid.length != Ymid.length) || (Xmid.length < 2)){ trace('Wrong
Arguments'); return this };
Проверка на отсутствие ошибок.
Если задано меньше двух точек, или количество координат по X не равно количеству
координат по Y, то метод заканчивается с передачей ссылки на себя и выводит в
режиме теста соответствующее сообщение. Если проверка наличия хотя-бы двух точек
- простая формальность, то проверка различного числа координат может быть весьма
полезной. При желании строчку trace('Wrong Arguments'); можно заменить на
что-нибудь вроде trace('Wrong Arguments: '+(Xmid.length-Ymid.length)),
чтобы знать, сколько и каких координат не достаёт.
if (Xmid.length ==
2){ this.moveTo(Xmid[0], Ymid[0]); this.lineTo(Xmid[1],
Ymid[1]); delete Xmid; delete Ymid; return
this; };
Ещё одна проверка. Если задано всего две точки, то мы
просто рисуем соединяющий их отрезок и заканчиваем выполнение метода. Данная
проверка обязательна, т.к. иначе используемый далее метод curveTo() просто не
получит достаточное количество параметров. Слово this используется для того,
чтобы данный метод, применённый к любому клипу перенимал все его свойства и
действовал в его пределах. Обращаю Ваше внимание на следующие
строки:
delete Xmid; delete Ymid;
В них удаляются из памяти
локальные массивы. В ActionScript эти строчки не обязательны, т.к. "сборщик
мусора" сам с ними разберётся.
Теперь вся подготовительная работа
выполнена и можно начинать создание массивов данных для построения
кривой:
var Xpoint = new Array(); var Ypoint = new
Array(); for (var i = 1; i < Xmid.length-2; i++){ Xpoint[i] =
0.5*(Xmid[i+1]+Xmid[i]); Ypoint[i] =
0.5*(Ymid[i+1]+Ymid[i]); };
Cоздаём ещё два локальных массива,
Xpoint и Ypoint, которые будут содержать координаты всех якорных точек. Далее в
цикле происходит рассчёт координат этих якорных точек. Здесь и появляется та
самая оговорка. В оригинале в CV NURBS координаты таких точек зависят от того,
насколько далеко от концов кривой они находятся. Но подобный способ получения
координат во Flash себя не оправдывает, и вот почему. В нашем методе мы создаём
якорные точки на середине отрезка, соединяющего соседние контрольные точки.
Максимальное отклонение "реального" местоположения якорных точек от
середины отрезка меньше 0,58 % длины самого отрезка. Так что этим вполне можно
пренебречь. Отмечу, что рассчёт координат происходит для точек с первой по
предпоследнюю, так как координаты первой и последней рассчитываются в
зависимости от того, замкнута кривая или нет.
if
(closed){ Xpoint[0] = 0.5*(Xmid[1]+Xmid[0]); Ypoint[0] =
0.5*(Ymid[1]+Ymid[0]); Xpoint[i] =
0.5*(Xmid[i+1]+Xmid[i]); Ypoint[i] =
0.5*(Ymid[i+1]+Ymid[i]); Xpoint[i+1] =
0.5*(Xmid[i+1]+Xmid[0]); Ypoint[i+1] =
0.5*(Ymid[i+1]+Ymid[0]); Xmid[i+2] = Xmid[0]; Ymid[i+2] =
Ymid[0]; Xpoint[i+2] = Xpoint[0]; Ypoint[i+2] =
Ypoint[0]; }
Если кривая замкнута, то мы создаём нулевую якорную
точку на середине отрезка между нулевой и первой контрольными точками, последнюю
- между предпоследней и последней. И нам потребуются координаты ещё нескольких
якорных точек: точки, находящейся на середине отрезка, соединяющего последнюю
контрольную точку с первой, и копии координат нулевых якорной и контрольной
точек. Это делается для того, чтобы объединить в один код рисование замкнутой и
не замкнутой кривых. Используемая в индексах переменная i после окончания цикла
имела значение Xmid.length-2, поэтому, следуя нашей организации массивов,
Xpoint[i], Ypoint[i] - это координаты якорной точки, находящейся между
предпоследней и последней контрольными (если здесь вообще уместно говорить,
какая точка первая, а какая - последняя), Xpoint[i+1], Ypoint[i+1] - между
последней и первой, Xpoint[i+2], Ypoint[i+2] - копии координат.
} else
{ Xpoint[0] = Xmid[0]; Ypoint[0] = Ymid[0]; Xpoint[i] =
Xmid[i+1]; Ypoint[i] =
Ymid[i+1]; Xmid.pop(); Ymid.pop(); };
Если же кривая
не замкнута, то мы просто делаем нулевую и последнюю контрольные точки нулевой и
последней якорной. Снова используем переменную i: Xmid[i+1], Ymid[i+1] являются
поледними элементами массивов, т.к. i+1 = Xmid.length-1. Далее мы удаляем
последний элемент в массивах координат контрольных точек, но оставляем первый
затем, чтобы каждой паре якорных точек [i-1], [i] соответствовала [i]-ая
контрольная точка, как и в случае замкнутой кривой.
Нахождение координат
точек завершено и начинается процесс рисования кривой:
this.moveTo
(Xpoint[0], Ypoint[0]); for (var i = 1; i < Xmid.length;
i++){ this.curveTo(Xmid[i], Ymid[i], Xpoint[i],
Ypoint[i]); };
Сначала указываем начальную точку. Затем начинаем
рисовать кривые, где для каждой i-ой кривой начальная точка имеет координаты
(Xpoint[i-1], Ypoint[i-1]), контрольная - (Xmid[i], Ymid[i]), а конечная -
(Xpoint[i], Ypoint[i]). Искомая линия построена!!!
delete
Xmid; delete Ymid; delete Xpoint; delete
Ypoint; return this; }
Удаление массивов и окончание метода.
Без комментариев.
В последней строчке Object.prototype.multicurveTo =
Multicurve; мы добавляем наш метод к набору имеющихся методов класса
Object.
В завершении...
Метод описан, можете смело его
использовать. Для простоты рекомендую поместить весь программный код в текстовый
файл с расширением .as и в каждом новом клипе, где этот метод может пригодиться,
в корне (_root) написать строку
#include path
где path - это
абсолютный или относительный путь к файлу с кодом. Заметьте, что после #include
точка с запятой НЕ ставится. Лично у меня это выглядит так:
#include
"D:/Flash/aibdraw.as"
Если для написания программного кода вы
используете встроеный во Flash редактор, то, возможно, Вам пригодиться
следущее: -- Заходите в корневой каталог Flash, далее \First
Run\ActionsPanel -- Открываете файл ActionsPanel.xml,
находите <ifdef mode="FEATURE_DRAWING_API"> --
Перед </folder> прописываете подобную строчку: <string
name="multicurveTo" tiptext="Draws a multipoint curve from the
first to the last point" object="MovieClip"
text=".multicurveTo(% [pointsX], [pointsY], Closed? %)"
type="procedure" version="6" /> После этого в левом
столбике редактора ActionScript и всплывающих подсказках появится
multicurveTo().
Литература по FLASH
|