Обзор возможностей класса Bezier

04.06.2008 от iv

Конструктор

Конструктор класса реализован таким образом, что ни один параметр не является обязательным. Такой подход упрощает создание экземпляров класса и в некоторых случаях серьезно развязывает руки разработчику.

Параметры конструктора — три точки на плоскости, объекты класса Point, а так же четвертый параметр — булева переменная, указывающая на режим работы с кривой.

Если параметры не заданы, то конструктор автоматически создает контрольные точки в нулевых координатах и режим работы с кривой — как с сегментом.

В целях обеспечения возможности изменения работы конструктора в подклассах, вся логика конструктора вынесена в protected метод initInstance(…).

Здесь же следует упомянуть метод clone, создающий копию текущей кривой. Других способов создания нового экземпляра класса не предусмотрено.

В предыдущей AS2 версии класса были предусмотрены методы создания экземпляра класса Bezier из строки, массива и XML. Однако, эта функциональность не была включена в текущую версию, поскольку не соответствует назначению пакета, а также может быть легко реализована самостоятельно в классе-фабрике.

Основные свойства

Контрольные точки, определяющие саму кривую Безье, и являются основными свойствами класса — start, control, end. Реализованы они как get/set свойства в целях возможности переопределения этих свойств в подклассах. Я рассуждал так: при замене контрольной точки на другую может потребоваться дополнительная функциональность, например перерисовка кривой. Эту логику проще всего реализовать переопределив свойства в подклассе.

Еще одним важным свойством является то, как мы относимся к кривой: как к сегменту, ограниченному начальной и конечной точками или как к бесконечной кривой. Значение этого свойства влияет на результаты вычисления пересечений, а также при вычислении точки на кривой, ближай$$$$$ей к произвольно заданной на плоскости.

Геометрические свойства

Длина сегмента кривой Безье, ограниченного начальной и конечной точками может быть получена из свойства length. Для получения длины произвольного сегмента кривой используется метод getSegmentLength(…), принимающий в качестве параметра time-итератор точки на кривой, до которой требуется найти длину сегмента от начальной точки. В случае, если потребуется найти длину сегмента, не начинающегося с начальной точки, то достаточно получить значения длины от начала кривой до начальной и конечной точки сегмента и вычислить разницу.

Площадь фигуры, образуемой кривой Безье и ее основанием, можно получить используя свойство area. Площадь этой фигуры равна 2/3 площади описывающего треугольника. Для вычисления площади сложной фигуры, отрисованной отрезками и кривыми Безье второго порядка, в общем случае следует:

  • разбить фигуру на треугольники, используя описывающие треугольники там, где контур образован кривыми;
  • суммировать их площадь;
  • вычесть все значения area для случаев вогнутых кривых, и вычесть половину значения area для случаев выпуклых кривых.

В случае выпуклых кривых вычитается половина area по той причине, что остав$$$$$аяся часть — фигура, образованная кривой и сторонами описывающего треугольника имеет площадь, равную 1/3 описывающего треугольника, или половине area.

Геометрический центр тяжести описывающего треугольника вычисляется как среднее арифметическое его вер$$$$$ин.

Описывающий треугольник делится кривой Безье на две части: вне$$$$$нюю и внутреннюю фигуры. Внутренняя фигура образуется кривой Безье и линией SE. Вне$$$$$няя фигура образуется кривой Безье и сторонами описывающего треугольника SC и CE.

Центры тяжести описывающего треугольника и фигур Безье находятся на одной линии, линии соединяющей середину основания (M) и управляющую точку C.

Центр тяжести внутренней фигуры находится на расстоянии 0.2 от основания до управляющей точки, описывающего треугольника — 1/3, и вне$$$$$ней фигуры на 0.6.

Для получения центров тяжести используйте свойства класса triangleCentroid, internalCentroid, externalCentroid. На демонстрации эти точки обозначены как Gt, Gi, Ge.

Габаритный прямоугольник можно получить используя свойство bounds. Следует заметить, что расчеты производятся для сегмента, ограниченного точками start и end, вне зависимости от текущего значения свойства isSegment, поскольку неограниченная кривая Безье бесконечна и о ее габаритах рассуждать бессмысленно.

Если требуется получить габаритный прямоугольник какой-либо части текущей кривой Безье, то вначале следует получить методом getSegment(…) новый объект Bezier и, в свою очередь, у него получать габаритный прямоугольник.

Применение этого свойства полезно в случаях, когда требуется произвести вычисление пересечения кривой с другими объектами. Как менее нагруженная операция, рекомендуется для предварительной оценки возможности пересечения.

Родительская парабола

Как я уже писал в предыдущих статьях, кривая Безье второго порядка, будучи построенной с time-итератором от минус бесконечности до плюс бесконечности, является параболой. Соответственно, сегмент кривой Безье, ограниченный точками start и end является сегментом параболы. Причем, зная контрольные точки Безье можно вычислить родительскую параболу, что и реализуют свойства parabolaVertex и parabolaFocusPoint.

Зная вер$$$$$ину параболы и фокус несложно вычислить все остальные параметры параболы и сделать необходимые расчеты, если они не реализованы классом Bezier.

Тут следует заметить, что класс Bezier служит исключительно для ре$$$$$ения наиболее часто встречающихся геометрических задач.

В процессе реализации класса авторами была проведена боль$$$$$ая работа и в финале функциональность класса при$$$$$лось существенно сокращать, оставив только самое необходимое. Некоторые из геометрических задач перекочевали из класса в примеры использования.

Мы надеемся, что практика использования на$$$$$его пакета классов поставит новые, еще не ре$$$$$енные задачи, и вы нам о них сообщите. Мы с удовольствием возьмемся за их ре$$$$$ение.

Точки на кривой

Получение точки по time-итератору производится методом getPoint(…). Этот метод преобразует точку на кривой, заданную time-итератором в точку на плоскости.

При вычислениях точки на кривой, методы класса возвращают time-итератор. По-сути, используется одномерное представление точки, что позволяет в промежуточных вычислениях избежать постоянных преобразований точки из двумерной в одномерную и наоборот.

В финале вычислений, когда требуется получить двумерную точку, и применяется этот метод.

Получение ближай$$$$$ей точки на кривой до произвольно заданной производится методом getClosest(…) и часто используется как операция, обратная получению точки по time-итератору.

В боль$$$$$инстве случаев точка, заданная координатами на плоскости, не будет математически точно лежать на кривой. При вычислениях требуется учитывать эту погре$$$$$ность и преобразовывать к ближай$$$$$ей точке на кривой.

На демонстрации в примерах 2 и 3 показаны особенности метода и способы его применения.

Получение точки по дистанции по кривой производится методом getTimeByDistance(…). Последовательность точек, находящихся на одинаковом расстоянии друг от друга луч$$$$$е получать методом getTimesSequence(…).

Эти методы — одна из самых сложных и отличительных составляющих на$$$$$его класса Bezier. Хотя мы и не на$$$$$ли чисто математического ре$$$$$ения этих задач, однако применение метода Ньютона позволило добиться высокой производительности вычислений.

Применение этих методов самое $$$$$ирокое – от рисования кривых пунктиром до вычисления позиции объекта, движущегося по траектории заданной кривыми.

В примерах, прилагающихся к пакету, рассмотрены варианты применения.

$$$$$зменение кривой

Провести кривую через заданную точку требуется, например, при организации визуального редактирования кривой с помощью мы$$$$$и, когда пользователь хватает мы$$$$$ью за произвольный участок кривой и, перемещая мы$$$$$ь, изменяет ее. Эту функциональность реализует метод setPoint(…).

Переместить и повернуть кривую можно используя методы offset(…) и angleOffset(…) соответственно. Думаю, особых пояснений тут не требуется.

Сегмент кривой

Сегмент кривой Безье второго порядка также является кривой Безье второго порядка и его можно получить методом getSegment(…). Сегмент можно получить в любых пределах time-итераторов.

Пересечения

В классе реализовано вычисление пересечений кривой Безье второго порядка с другой кривой Безье второго порядка, а также с линией. Результатом пересечения всегда является объект класса Intersection, описывающий пересечения. Если пересечения нет, возвращается null.

В данный момент еще не окончательно реализована проверка соответствия пересечения значению свойства isSegment. TODO, что я еще могу сказать.

$$$$$ван Дембицкий

Рубрики: Примеры использования классо, начинающим | Комментарии (8) »

Комментарии (8)

  1. Дмитрий пишет:

    А как вы реализовали проверку на нахождение курсора над кривой и в примере где реализовано затухание колебаний – как сделано (алгоритм)?

  2. iv пишет:

    Так есть же исходники на http://code.google.com/p/bezier/source/checkout

    Если что-то там непонятно, спросите, поясню.

  3. HunterNomad пишет:

    Доброго времени суток.
    Скажите, где можно скачать предыдущую версию под AS2.
    Спасибо

  4. iv пишет:

    http://bezier.googlecode.com/files/ru.bezier.zip

  5. elmortem пишет:

    Как узнать угол поворота кривой на определённой дистанции? Скажем, я двигаю по кривой ма?инку и хотел бы, чтобы она поворачивала в сторону движения. Самый простой способ – рассчитать угол между предыдущей и текущей точками при движении, но иногда нужно установить объект в произвольной дистанции на кривой и нужен угол поворота. Можно его узнать как-то проще, нежели рассчитывать сначала ?аг назад, а затем вычислять угол между двумя точками на кривой?
    Заранее спасибо.

  6. Lampard пишет:

    Здравствуйте, ещё не устанавливал, хочу спросить – можно ли использовав ва? класс чтобы линия была сначала утолщённой и плавно переходящей в тонкую. Либо применить функцию bezier на каком либо мувиклипе? Спасибо

  7. iv пишет:

    Шаг 1: «Установить на нужное расстояние по кривой» реализован здесь: http://bezier.ru/wp-content/uploads/2008/06/bezier.swf?demo=6
    (на все демки есть исходники)

    Шаг 2: Получить угол касательной методом getTangentAngle(time)
    time итератор получите на предыдущем ?аге

  8. iv пишет:

    По второму вопросу: можно нарисовать две кривых и залить пространство между ними.

    Что такое «применить функцию bezier на каком либо мувиклипе» я не понял.