понедельник, 18 февраля 2008 г.

Трансформация объекта относительно произвольной точки

Недавно столкнулся с задачей трансформации (масштабирование, поворот или наклон) объекта относительно произвольной точки. Предлагаю к ознакомлению свое решение этой задачи.

package com.dmitrykrasnov.research {
    import flash.display.Shape;
    import flash.display.Sprite;
    import flash.geom.Matrix;
    import flash.geom.Point;

    public class TransformationAboutPoint extends Sprite {
        public function TransformationAboutPoint() {

            // Для примера, нарисуем квадрат.
            var myBox : Shape = new Shape();
            myBox.graphics.lineStyle(1, 0x000000);
            myBox.graphics.beginFill(0x000000);
            myBox.graphics.drawRect(16, 16, 32, 32);
            myBox.graphics.endFill();

            // Возьмем его матрицу трансформации и повернем ее на 45 градусов.
            // Заметьте, поворот произойдет относительно начала координат.
            var myMatrix : Matrix = myBox.transform.matrix;
            myMatrix.rotate(Math.PI / 4);

            // Выберем точку вращения (пусть это будет центр нашего квадрата).
            var axisPoint : Point = new Point(32, 32);

            // Выясним, куда она сместилась в результате поворота.
            var newPoint : Point = myMatrix.transformPoint(axisPoint);

            // Вычислим относительное смещение.
            var deltaX : Number = axisPoint.x - newPoint.x;
            var deltaY : Number = axisPoint.y - newPoint.y;

            // Компенсируем относительное смещение.
            myMatrix.translate(deltaX, deltaY);

            // Применим трансформации к нашему квадрату.
            myBox.transform.matrix = myMatrix;

            // Посмотрим на результат.
            addChild(myBox);

            // На всякий случай, нарисуем проверочные направляющие.
            var guideLines : Shape = new Shape();
            guideLines.graphics.lineStyle(1, 0xFF0000);
            guideLines.graphics.moveTo(0, axisPoint.y);
            guideLines.graphics.lineTo(64, axisPoint.y);
            guideLines.graphics.moveTo(axisPoint.x, 0);
            guideLines.graphics.lineTo(axisPoint.x, 64);
            addChild(guideLines);
        }
    }
}

Если кому-то известно более изящное решение, пожалуйста, опишите его в комментариях.

11 комментариев:

  1. Насчет изящности не знаю, но в моем переводе статьи про Motion XML описан способ через transformationPoint

    http://fla-master.livejournal.com/5588.html

    ОтветитьУдалить
  2. fla-master, указанный вами способ очень хорош там, где нужна анимация. Если же речь идет о статичном размещении объектов, об отображении на bitmap или о построении градиентов, то Motion XML ни к чему. О решении именно таких задач я и писал.

    ОтветитьУдалить
  3. Антон Волков19 февраля 2008 г., 19:48

    Матричные (хоть 2D, хоть 3D) поворот и масштабирование делаются вокруг центра матрицы. Чтобы выполнить трансформацию вокруг произвольной точки достаточно предварительно сдвинуть матрицу, чтобы эта самая точка стала центром (x -= px, y -= py). После чего выполнить необходимые преобразования и вернуть матрицу на место (x += px, y += py).

    ОтветитьУдалить
  4. Антон Волков21 февраля 2008 г., 18:20

    Куда уж подробнее :)
    В вашем случае:
    myMatrix.translate(-axisPoint.x, -axisPoint.y);
    myMatrix.rotate(Math.PI / 4);
    myMatrix.translate(axisPoint.x, axisPoint.y);

    ОтветитьУдалить
  5. для большого количества точек жельательно один раз посчитать матрицу преобразования, и с помощью нее преобразовывать все точки (будет меньше вычислений).

    MATR = MatrTrans(-point) * MatrRot * MatrTrans(+point)

    ОтветитьУдалить
  6. во, дела! я со своим каментом почти на год опоздал :)

    ОтветитьУдалить
  7. > во, дела! я со своим каментом почти на год опоздал :)

    Ну, это не страшно. Ваш комментарий не стал от этого менее полезным.

    Вам и всем отписавшимся - огромное спасибо за дельные мысли.

    ОтветитьУдалить
  8. Анонимный13 июля 2010 г., 19:46

    Стоит отметить, что такие н

    ОтветитьУдалить
  9. О-о-огроменное спасибо! Почти спас от бессонной ночи! :)

    ОтветитьУдалить