Как получить необращенный экранный объект шириной / высотой повернутого экранного объекта? - PullRequest
5 голосов
/ 31 января 2010

Если я создам прямоугольник с шириной 100 пикселей и высотой 100 пикселей, а затем поверну его, размер «ящика» элемента увеличится.

При 45 поворотах размер становится примерно 143x143 (от 100x100).

Иногда кажется, что cos(angleRad) * currentWidth работает на 45 оборотов, но для других больших углов это не так.

На данный момент я делаю это:

var currentRotation = object.rotation;
object.rotation = 0;
var normalizedWidth = object.width;
var normalizedHeight = object.height;
object.rotation = currentRotation;

Конечно, должен быть лучший и более эффективный способ. Как получить «нормализованную» ширину и высоту экранного объекта, то есть размер, когда он не был повернут?

Ответы [ 4 ]

12 голосов
/ 01 февраля 2010

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

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

Второй, Точность . Из любопытства я закодировал все три предложения, которые в настоящее время находятся в этой теме, и я не очень удивился, обнаружив, что для произвольно масштабируемого объекта они дают три несколько разных ответа. Причина этого, в двух словах, заключается в том, что внутренние компоненты рендеринга Flash сильно оптимизированы, и, среди прочего, width и height не хранятся внутри как плавающие числа. Они хранятся в виде «твипов» (двадцатых пикселей) на том основании, что дальнейшая точность визуально не имеет значения.

В любом случае, если три метода дают разные ответы, какой из них наиболее точный? За мои деньги самый правильный ответ - то, что Flash думает о ширине объекта, когда он не повернут, что дает нам простой метод. Кроме того, этот метод является единственным, который всегда дает ответы, округленные до ближайшей 1/20, что, как я предполагаю (хотя я предполагаю), означает, что оно, вероятно, равно значению, хранимому внутри, а не вычисляемому значению.

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

Вы, вероятно, ожидали, что простой метод будет медленнее на том основании, что изменение поворота объекта может привести к пересчету многих других вещей, что приведет к накладным расходам. Но все, что происходит на самом деле сразу, когда вы меняете вращение, это то, что матрица преобразования объекта получает новые значения. Flash на самом деле мало что делает с этой матрицей, пока в следующий раз не нарисует объект на экране. Что касается того, что математика происходит, когда вы читаете ширину / высоту объекта, сказать сложно. Но стоит отметить, что все, что происходит в простом методе, выполняется сильно оптимизированными внутренними компонентами Player, а не в AS3, как алгебраический метод.

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


Вот код, который я использовал:

// init
var clip:MovieClip = new MovieClip();
clip.graphics.lineStyle( 10 );
clip.graphics.moveTo( 12.345, 37.123 ); // arbitrary
clip.graphics.lineTo( 45.678, 29.456 ); // arbitrary
clip.scaleX = .87; // arbitrary
clip.scaleY = 1.12; // arbitrary
clip.rotation = 47.123; // arbitrary

// run the test
var iterations:int = 1000000;
test( method1, iterations );
test( method2, iterations );
test( method3, iterations );

function test( fcn:Function, iter:int ) {
 var t0:uint = getTimer();
 for (var i:int=0; i<iter; i++) {
  fcn( clip, i==0 );
 }
 trace(["Elapsed time", getTimer()-t0]);
}

// the "simple" method
function method1( m:MovieClip, traceSize:Boolean ) {
 var rot:Number = m.rotation;
 m.rotation = 0;
 var w:Number = m.width;
 var h:Number = m.height;
 m.rotation = rot;
 if (traceSize) { trace([ "method 1", w, h ]); }
}

// the "algebraic" method
function method2( m:MovieClip, traceSize:Boolean ) {
 var r:Number = m.rotation * Math.PI/180;
 var c:Number = Math.abs( Math.cos( r ) );
 var s:Number = Math.abs( Math.sin( r ) );
 var denominator:Number = (c*c - s*s); // an optimization
 var w:Number = (m.width * c - m.height * s) / denominator;
 var h:Number = (m.height * c - m.width * s) / denominator;
 if (traceSize) { trace([ "method 2", w, h ]); }
}

// the "getBounds" method
function method3( m:MovieClip, traceSize:Boolean ) {
 var r:Rectangle = m.getBounds(m);
 var w:Number = r.width*m.scaleX;
 var h:Number = r.height*m.scaleY;
 if (traceSize) { trace([ "method 3", w, h ]); }
}

И мой вывод:

method 1,37.7,19.75
Elapsed time,1416
method 2,37.74191378925391,19.608455916982187
Elapsed time,1703
method 3,37.7145,19.768000000000004
Elapsed time,1589

Удивительно, а? Но здесь есть важный урок о разработке Flash. Настоящим я крещу Закон лени Фена:

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

Это не только ускоряет выполнение, но, по моему опыту, в любом случае это обычно приводит к выигрышу в производительности. Счастливая оптимизация!

7 голосов
/ 01 февраля 2010

Вот алгоритмический подход и его вывод.

Во-первых, давайте выполним противоположную задачу: учитывая прямоугольник без поворота ширины w , без поворота высоты h и вращения r , что такое ширина поворота а высота?

w r = abs (грех ( r )) * h + abs (cos ( r )) * w
h r = abs (sin ( r )) * w + abs (cos ( r )) * h

Теперь попробуйте выполнить задачу следующим образом: заданный прямоугольник с повернутой шириной w r , повернутая высота h r и вращение r , что такое не повернутая ширина и высота?

Нам нужно решить вышеприведенные уравнения для ч и ш . Пусть c представляет abs (cos ( r )) и s представляет abs (sin ( r )). Если мои навыки в области ржавой алгебры все еще работают, то вышеприведенные уравнения могут быть решены с помощью:

w = ( w r * c - h r * с ) / ( с 2 - с 2 )
h = ( h r * c - w r * с ) / ( с 2 - с 2 )

3 голосов
/ 31 января 2010

Вы должны получить границы вашего квадрата в координатном пространстве вашего объекта (что означает отсутствие поворотов).

, например

var b:Sprite = new Sprite();
b.graphics.lineStyle(0.1);
b.graphics.drawRect(0,0,100,100);
b.rotation = 10;
trace('global coordinate bounds: ' + b.getBounds(this));//prints global coordinate bounds: (x=-17.35, y=0, w=115.85, h=115.85);
trace('local coordinate bounds: ' + b.getBounds(b));//prints local coordinate bounds: (x=0, y=0, w=100, h=100)

НТН, George

0 голосов
/ 11 января 2012

Чип ответ в коде:

// convert degrees to radians
var r:Number = this.rotation * Math.PI/180;
// cos, c in the equation
var c:Number = Math.abs(Math.cos(r));
// sin, s in the equation
var s:Number = Math.abs(Math.sin(r));
// get the unrotated width
var w:Number = (this.width * c - this.height * s) / (Math.pow(c, 2) - Math.pow(s, 2));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...