Как определить тип преобразования элемента (масштаб, перемещение, поворот) для повернутых элементов в зависимости от положения курсора? - PullRequest
2 голосов
/ 02 февраля 2010

Ситуация

Я написал следующий метод (в ActionScript 3 это приложение Flash), который возвращает тип преобразования, необходимый для текущей позиции мыши относительно данной позиции элемента.

Элемент можно перемещать, масштабировать и вращать. Этот метод возвращает, если любое из этих преобразований применимо с заданными координатами:

(не волнуйтесь, вам не нужно отлаживать этот метод, он работает - просто добавьте его здесь, чтобы вы поняли, что я делаю)

//returns whether the mouse is at a transforming position
        private static function mouseAtTransformPosition(cX:Number, cY:Number, eX:Number, eY:Number, eW:Number, eH:Number, margin:Number):String
        {
            //initialize the transformation type
            var transformType:String = null;

            //set the transformation type depending on the given coordinates
            if ((cX > eX) && (cX < eX + eW) && (cY > eY) && (cY < eY + eH))
            {
                transformType = "mover";
            }
            else if ((cX > eX) && (cX < eX + eW) && (cY <= eY) && (cY >= eY - margin))
            {
                transformType = "scalerUp";
            }
            else if ((cX >= eX + eW) && (cX <= eX + eW + margin) && (cY > eY) && (cY < eY + eH))
            {
                transformType = "scalerRight";
            }
            else if ((cX > eX) && (cX < eX + eW) && (cY >= eY + eH) && (cY <= eY + eH + margin))
            {
                transformType = "scalerDown";
            }
            else if ((cX <= eX) && (cX >= eX - margin) && (cY > eY) && (cY < eY + eH))
            {
                transformType = "scalerLeft";
            }
            else if ((cX >= eX - margin) && (cX <= eX) && (cY >= eY - margin) && (cY <= eY))
            {
                transformType = "scalerUpLeft";
            }
            else if ((cX >= eX + eW) && (eX <= eX + eW + margin) && (cY >= eY - margin) && (cY <= eY))
            {
                transformType = "scalerUpRight";
            }
            else if ((cX >= eX + eW) && (cX <= eX + eW + margin) && (cY >= eY + eH) && (cY <= eY + eH + margin))
            {
                transformType = "scalerDownRight";
            }
            else if ((cX >= eX - margin) && (cX <= eX) && (cY >= eY + eH) && (cY <= eY + eH + margin))
            {
                transformType = "scalerDownLeft";
            }
            else if ((cX >= eX - margin * 2) && (cX <= eX) && (cY >= eY - margin * 2) && (cY <= eY))
            {
                transformType = "rotatorTopLeft";
            }
            else if ((cX >= eX + eW) && (cX <= eX + eW + margin * 2) && (cY >= eY - margin * 2) && (cY <= eY))
            {
                transformType = "rotatorTopRight";
            }
            else if ((cX >= eX + eW) && (cX <= eX + eW + margin * 2) && (cY >= eY + eH) && (cY <= eY + eH + margin * 2))
            {
                transformType = "rotatorBottomRight";
            }
            else if ((cX >= eX - margin * 2) && (cX <= eX) && (cY >= eY + eH) && (cY <= eY + eH + margin * 2))
            {
                transformType = "rotatorBottomLeft";
            }

            //return the found transformation type
            return transformType;
        }

Уточнение всех необходимых параметров:

cX: cursor X position
cY: cursor Y position
eX: element X position
eY: element Y position
eW: element width
eH: element height
margin: how far the cursor can be removed from an exact transformation location to still be applicable

Если преобразование не применимо, возвращается ноль.

Проблема

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

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

cursor is...
  inside the element -> move
  at top of element -> scale up
  at right of element -> scale right
  at bottom of element -> scale down
  at left of element -> scale left
  at top left of element -> scale up-left
  at top right of element -> scale up-right
  at bottom left of element -> scale down-left
  at bottom right of element -> scale down-right
  further top left of element -> rotate right/left
  further top right of element -> rotate right/left
  further bottom right of element -> rotate right/left
  further bottom left of element -> rotate right/left

Вопрос

Мой метод делает это отлично, если нет вращения. Однако, как я могу изменить эту функцию, чтобы учесть вращение, например, изменение координат углов?

Обновление для Джастина

//checks whether mouse transforming is applicable
        private static function checkMouseTransforming(cX:Number, cY:Number, render:Sprite):void
        {           
            //temp disable rotation to get accurate width/height, x and y
            var realRotation:Number = render.getChildAt(0).rotation;
            render.getChildAt(0).rotation = 0;

            var eX:Number = render.x;
            var eY:Number = render.y;
            var eW:Number = render.width;
            var eH:Number = render.height;
            var margin:uint = 10;

            //restore real rotation
            render.getChildAt(0).rotation = realRotation;

            cX -= eX + eW / 2;
            cY -= eY + eH / 2;

            var theta:Number = render.getChildAt(0).rotation * Math.PI / 180;
            var newcX:Number = Math.cos(theta) * cX - Math.sin(theta) * cY;
            var newcY:Number = Math.sin(theta) * cX + Math.cos(theta) * cY;

            newcX += eX + eW / 2;
            newcY += eY + eH / 2;

            var transformType:String = mouseAtTransformPosition(newcX, newcY, eX, eY, eW, eH, margin);
                        if (transformType)
                        {
                           //...
                        }

Как видите, поворот задается и принимается от дочернего элемента визуализируемого объекта. Это связано с тем, что фактический объект - это positionWrapper, а внутри этой обертки есть rotWrapper (для которого установлено вращение), а внутри этой обертки остается фактический видимый элемент. X, y, ширина и высота позиционного упаковщика всегда равны таковым у видимого экранного объекта, поэтому это не должно вызывать проблем.

Теперь вот результаты:

  • Без вращения все работает как положено
  • При повороте на 45 градусов при наведении указателя мыши на объект визуального отображения:
    • нижний центр: он возвращает «scalerLeft», а должен возвращать «scalerDown»
    • правый центр: возвращает ScalerDown, а должен возвращать ScalerRight
    • верхний центр: возвращает ScalerRight, а должен возвращать ScalerUp
    • левый центр: он возвращает scalerUp, а должен возвращать scalerLeft (обратите внимание на шаблон смещения здесь)
    • Двигатель, кажется, возвращается довольно точно
    • вращатель, кажется, возвращается в прямом нижнем углу, а не там, где он должен
    • Скейлер bottomLeft, похоже, возвращается в нижнем правом углу, я думаю, что это то же самое смещение является проблемой и, вероятно, то же самое для всех других скейлеров

Это ошибка для отладки, но, возможно, это поможет.

Обновление № 2 для Джастина альтернативный текст http://feedpostal.com/transformBug.gif Смещение / ошибка, кажется, тем сильнее, чем выше вращение. При повороте на 0 эта ошибка не появляется.

Имеет ли это смысл для вас или кого-либо еще? Заранее спасибо.

Ответы [ 2 ]

2 голосов
/ 07 февраля 2010

Я сделал несколько предположений здесь. Я предположил, что объект является прямоугольным и двумерным (без трехмерных вращений). Я предположил, что это единственный объект, который может быть преобразован для упрощения вещей. Кроме того, из комментариев к другому ответу я понимаю, что размеры объекта (eX, eY, eW, eH) по-прежнему остаются неизменными после поворота.

Теперь вот мое решение. Вам определенно необходимо знать угол поворота (который я буду считать положительным для вращения по часовой стрелке) объекта, который я назову тета. Сначала я подумал, что вы должны повернуть ограничивающие рамки для различных областей (например, области, где преобразование является «двигателем»), но это намного сложнее. Что вы должны сделать, это повернуть координаты X и Y мыши вокруг центра объекта и в противоположном направлении тета. Вот несколько явных шагов:

  1. Вычтите смещение по x и y по центру объекта из соответствующих координат x и y (cX и cY). Полученные значения дают смещения x и y от центра объекта.

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

  3. Добавьте смещение по x и y к центру объекта к результирующим координатам x и y из предыдущего шага.

  4. Используйте результирующие значения x и y в качестве значений cX и cY в функции mouseAtTransformPosition.

Теперь вот код, иллюстрирующий это.

cX -= eX + eW/2;
cY -= eY + eH/2;

newcX = cos(theta)*cX - sin(theta)*cY;
newcY = sin(theta)*cX + cos(theta)*cY;

newcX += eX + eW/2;
newcY += eY + eH/2;

newcX и newcY будут координатами, фактически используемыми в mouseAtTransformPosition.

Дайте мне знать, если это не имеет смысла или у вас есть какие-либо вопросы.

*** Обновлено:

Я думаю, что вы вращаете координаты в неправильном направлении. Для изменения направления вращения используйте:

newcX = cos(theta)*cX + sin(theta)*cY;
newcY = -sin(theta)*cX + cos(theta)*cY;

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

1 голос
/ 02 февраля 2010

Если вы используете mouseX / mouseY свойства объекта, который вы трансформируете, вместо свойств его родителя (или стадии в этом отношении), вы получите их в координатном пространстве объектов.
Таким образом, вам никогда не придется беспокоиться о ротации или масштабировании, и вы можете сохранить свой код почти таким, какой он есть.

Я бы также рекомендовал использовать scaleX / scaleY для изменения размера, поскольку width и height будут меняться при вращении объекта.

...