Форум Flasher.ru
Ближайшие курсы в Школе RealTime
Список интенсивных курсов: [см.]  
  
Специальные предложения: [см.]  
  
 
Регистрация Блоги Правила Справка Пользователи Календарь Поиск рулит! Сообщения за день Все разделы прочитаны
 

Вернуться   Форум Flasher.ru > Блоги > fish_r

Оценить эту запись

Поворот объекта вокруг произвольной точки.

Запись от fish_r размещена 30.04.2011 в 14:21
Обновил(-а) fish_r 03.05.2011 в 13:58

Способы, описание, примеры, анализ и (возможно :)) обсуждение.


Вместо вступления.
Вообще стоило бы назвать "... вокруг произвольно выбранной...", но не стал дабы поисковику
было легче находить нужное.
Итак... Ангина... Работать толком не могу. Поэтому решил, пока, написать вот такой,
небольшой обзор темы довольно часто поднимаемой на страницах форума.

Благодарности.
Конечно до всего здесь изложенного я не всегда сам "допёр", поэтому благодарен
всем участникам форума комментировавшим вопросы касающиеся этой темы, а так же
администратору (iNils-у), супермодераторам и всем, кто принимает участие в под-
держании жизнеспособности форума. Ну и, конечно, маме :). К делу...


Ниже будут рассмотрены следующие способы поворота отображаемого объекта вокруг
произвольно выбранной точки:

1. Поворот объекта при помощи помещения его в специально созданный для этого контейнер,

2. Поворот объекта с использованием матрицы (2Д), с применением метода
fl.motion.MatrixTransformer.rotateAroundInternalPoint(),

3. Поворот объекта с использованием прямого преобразования исходного объекта Matrix,

4. То же, что в пп.2-3, только в случае если объект Matrix равен null,

5. Поворот объекта с использованием прямого преобразования исходного объекта Matrix3D.




1. Поворот объекта при помощи помещения его в специально
созданный для этого контейнер.



Часто рекомендуемый способ, особенно удобный для дизайнеров не желающих сильно углубляться
в дебри программирования. По сути представляет из себя следующее: Объект, к которому
необходимо применить поворот помещается в специально, для этого, созданный контейнер
(MovieClip или Sprite)со смещением равным координатам точки вокруг которой объект должен
быть повернут. Далее выполняется поворот этого, специально созданного контейнера, а не
непосредственно самого объекта. Он, сам, объект вращается опосредованно, так как вращается
содержащий его контейнер.

Пример 1.1:
Код AS3:
...
	         /**
		 *  Вращаемый контейнер  для опосредованного
		 * вращения объекта.
		 */
 
		public static var cntRotator:Sprite = new Sprite();
		/**
		 *  Вращает объект посредством вращения контейнера его содержащего
		 * и специально для этого созданного.
		 * 
		 * @param	obj - вращаемый вокруг произвольной точки объект
		 * @param	cX - координата точки по оси Х
		 * @param	cY - координата точки по оси У
		 * @param	angle - угол поворота в градусах.
		 */
		public static function rotationContainer(obj:DisplayObject, cX:Number, cY:Number, angle:Number):void
		{
			var pGlobal:Point;
			var pRotate:Point;
			var pLocal:Point;
 
 
 
			/**
			 * Добавляем на сцену спрайт для поворота объекта,
			 * помещаем в него объект, сдвигаем спрайт относительно
			 * контейнера, а объект относительно нулевых координат 
			 * спрайта.
			 */
 
			if (cntRotator.numChildren === 0) 
			{
				pGlobal = obj.localToGlobal(new Point(cX, cY));
				pRotate = obj.parent.globalToLocal(pGlobal);
			    obj.parent.addChild(cntRotator);
				cntRotator.addChild(obj);
				cntRotator.x = pRotate.x; cntRotator.y = pRotate.y;
				obj.x = -cX; obj.y = -cY;
			}
 
			/**
			 * вращаем (angle - градус поворота)
			 * это единственный способ поворота при котором возможно использование
			 * нативного твина,а это место где его, как раз, можно разместить
			 */
 
			cntRotator.rotation += angle;
 
                        return;
 
                 }


Достоинства:
- как говорилось выше, удобен для дизайнеров, не желающих углубляться в программирование,
- в процессе множественного вращения (придумал сам :), это когда надо не 1, и не 2, и не 3 раза
выполнить поворот а сразу много, может быть вращение на всем протяжении работы программы),
нет смещения относительно точки вращения (об этом позже - поподробнее).
- возможно применение нативного класса Tween, так как используются "несложный" метод rotate,
или tween-образных плагинов "не умеющих" выполнять самостоятельно подобные вещи,
- нет необходимости хранить начальную матрицу (об этом тоже позже) объекта,
- не зависит от типа хранимой матрицы (и об этом тоже - немного позже).

Недостатки:
- создание совершенно ненужного, в контексте программы, объекта создаваемого лишь для того,
чтобы повернуть другой - нужный объект,
- перемещение поворачиваемого объекта в DisplayList, опять же неоправданное и возникающие
в связи с этим нюансы по обращению к нему, по его позиционированию в дальнейшем и пр.,
- явно "костыльный" способ, не прельщающий программиста желающего стать труфлешером :).


Однако, первые два недостатка можно устранить.
Пример 1.2.:

Код AS3:
 
public static var cntRotator:Sprite = new Sprite();
		/**
		 *  Вращает объект посредством вращения контейнера его содержащего
		 * и специально для этого созданного.
		 * 
		 * @param	obj - вращаемый вокруг произвольной точки объект
		 * @param	cX - координата точки по оси Х
		 * @param	cY - координата точки по оси У
		 * @param	angle - угол поворота в градусах.
		 */
		public static function rotationContainer(obj:DisplayObject, cX:Number, cY:Number, angle:Number):void
		{
			var pGlobal:Point;
			var pRotate:Point;
			var pLocal:Point;
 
 
 
			/**
			 * Добавляем на сцену спрайт для поворота объекта,
			 * помещаем в него объект, сдвигаем спрайт относительно
			 * контейнера, а объект относительно нулевых координат 
			 * спрайта.
			 */
 
			if (cntRotator.numChildren === 0) 
			{
				pGlobal = obj.localToGlobal(new Point(cX, cY));
				pRotate = obj.parent.globalToLocal(pGlobal);
			    obj.parent.addChild(cntRotator);
				cntRotator.addChild(obj);
				cntRotator.x = pRotate.x; cntRotator.y = pRotate.y;
				obj.x = -cX; obj.y = -cY;
			}
 
			/**
			 * вращаем (angle - градус поворота)
			 * это единственный способ поворота при котором возможно использование
			 * нативного твина,а это место где его, как раз, можно разместить
			 */
 
			cntRotator.rotation += angle;
 
			/**
			 *  Удаляем отработавший спрайт, объект размещаем
			 * в первоначальном контейнере, предварительно преобразовав
			 * координаты объекта относительно спрайта для поворота, в координаты
			 * объекта первоначального контейнера, сдвигаем и поворачиваем
			 * его таким образом как был повернут спрайт
			 */
 
			pGlobal = obj.localToGlobal(new Point(0, 0));
			pLocal = cntRotator.parent.globalToLocal(pGlobal);
 
			cntRotator.removeChild(obj);
			cntRotator.parent.addChild(obj);
			obj.parent.removeChild(cntRotator);
			obj.rotation = cntRotator.rotation;
			obj.x = pLocal.x;
			obj.y = pLocal.y;
 
 
			return;
 
		}


Ну, а последний недостаток свойственен "по определению" :).


2. Поворот объекта с использованием матрицы (2Д), с применением метода
fl.motion.MatrixTransformer.rotateAroundInternalPoint().


Специально созданный нативный метод вращения использующий матрицу объекта. Не требующий
понимания основ использования матричных преобразований. Сводящий к минимуму объем кода
для подготовки и последствий вращения.

Пример 2:
Код AS3:
/**
		 *  Вращение объекта вокруг произвольной точки
		 * выраженной в системе координат объекта. С исполь-
		 * зованием метода нативного класса fl.motion.MatrixTransformer
		 * 
		 * _baseMatrix - исходная матрица объекта, устанавливаемая  через сетер. Важно, что 
		 * если поворотов  будет несколько, исходная матрица должна иметь значение 
		 * которое было до начала процедур поворотов, иначе из за относительной 
		 * неточности расчетов, со временем, становится заметным смещение объекта 
		 * к координатам 0,0 сцены.
		 * 
		 * @param	obj - вращаемый вокруг произвольной точки объект
		 * @param	cX - координата точки по оси Х
		 * @param	cY - координата точки по оси У
		 * @param	angle - угол поворота в градусах.
		 */
 
 
		internal static function rotateMatrixTransformer( obj:DisplayObject, cX:Number, cY:Number, angle:Number ):void
		{
			var matrix:Matrix;
			if (_baseMatrix) matrix = _baseMatrix;
			else matrix = obj.transform.matrix;
 
			MatrixTransformer.rotateAroundInternalPoint( matrix, cX, cY, angle );
 
			obj.transform.matrix = matrix;
 
		}

Достоинства:
- для поворота объекта требует столь мало кода, что выносить его в отдельную функцию требуется
только для удобочитаемости,
- не требует преобразования точки вращения из системы координат объекта в систему координат
контейнера его содержащего.

Недостатки:
- в случае null-евой матрицы объекта (см. ниже) его применение вызовет ошибку исполнения,
- в случае "множественного" вращения необходимо хранить начальную матрицу и каждый раз
подавать её в аргументах, иначе объект постепенно будет смещаться к нулевым координатам сцены,
и происходит это не из за неточности Namber-а, как я понял, а из за какого то непонятного сме-
щения (что то криво в недрах алгоритмов).

Примечание. Есть также метод fl.motion.MatrixTransformer.rotateAroundExternalPoint(), который здесь
не рассматривается.


3. Поворот объекта с использованием прямого преобразования исходного объекта Matrix.

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


Пример 3 :

Код AS3:
 
/**
		 *  Вращение объекта вокруг произвольной точки
		 * выраженной в системе координат его контейнера, а
		 * не его самого при помощи нативных методов изменений
		 * матрицы объекта.
		 * 
		 * _baseMatrix - исходная матрица объекта, устанавливаемая  через сетер. Важно, что 
		 * если поворотов  будет несколько, исходная матрица должна иметь значение 
		 * которое было до начала процедур поворотов, иначе из за относительной 
		 * неточности расчетов, со временем, становится заметным смещение объекта 
		 * к координатам 0,0 сцены.
		 * 
		 * 
		 * @param	obj - вращаемый вокруг произвольной точки объект
		 * @param	cX - координата точки по оси Х
		 * @param	cY - координата точки по оси У
		 * @param	angle - угол поворота в градусах.
		 */
		internal static function rotateMatrixMethodsOnly( obj:DisplayObject, cX:Number, cY:Number, angle:Number ):void
		{
			var matrix:Matrix;
 
			if (_baseMatrix) matrix = _baseMatrix;
			else matrix = obj.transform.matrix;
 
			/**
			 * Преобразование координат точки вращения из
			 * локальных координат вращаемого объекта в коорди-
			 * наты его контейнера
			 */
 
			var pGlobal:Point = obj.localToGlobal(new Point(cX, cY));
			var pRotate:Point = obj.parent.globalToLocal(pGlobal);
 
 
			var rad:Number = angle * (Math.PI / 180);
 
 
			matrix.translate( -pRotate.x, -pRotate.y );
			matrix.rotate( rad );
			matrix.translate( pRotate.x, pRotate.y );
 
			obj.transform.matrix = matrix;
		}
Достоинства:

- так же как и предидущий способ не требует создания дополнительных DisplayObject-ов,
- действительно программистский подход к делу, в отличии от способа № 1 :).

Недостатки:
- требует перевода координат точки вращения из системы координат вращаемого объекта в
систему координат содержащего его контейнера,
- в случае "множественного" вращения требует отдельного хранения координат точки вращения
и начальной матрицы, т.к. и то и другое если расчитывается перед очередным оборотом (что,
впрочем, само по себе не хорошая практика) приводит к смещению вращаемого объекта,
- приведёт к выводу ошибки исполнения, если в момент обращения матрица объекта равна null.



4. То же, что в пп.2-3, только в случае если объект Matrix равен null.

Дело в том, что есть один нюанс с использованием матрицы. Она иногда может быть равна null.
Сам был немало озадачен когда это обнаружил. Проблема в том, что в описаниях матрицы, её методов
и свойств не указываются случаи когда матрица может быть равна null. Порыскав по коду приложения,
которое делал в тот момент стал подозревать, что к этому привели манипуляции с методами осевых rotation
объекта. Вскоре обнаружил этому подтверждение в блоге Mike Chambers,

и после, уже, увидел скупой, как мужская слеза, комментарий по этому поводу разделе adobe help-a Matrix3D
где сказано следующее:
Цитата:
Если свойство z или какое-либо свойство поворота или масштабирования экранного объекта задано явно, соответствующий объект Matrix3D создается автоматически.
От себя добавлю, к этому, что насчет масштабирования - не уверен, а поворот по любой оси (X,Y,Z), правда кроме
поворота через вызов myObject.rotation = ..., и даже просто вызов сеттера объекта, как то: myObject.z = myObject.z
приводит к обнулению myObject.transform.matrix, и автоматическому созданию объекта myObject.transform.matrix3D.
И далее все матричные манипуляции должны осуществляться через объект Matrix3D.

Однако, создавая приложение в 2D (или в 2.5D пространстве) разве нужны нам 3D-манипуляции? Так же, видимо,
примерно рассуждал и Mike Chambers, и написал свой класс который возрождает объект matrix обнуляя matrix3D. Кстати,
говоря, приведенный им код не верен, но это не его вина, так как он следует схеме матрицы предоставленной в справоч-
ном разделе адоба, а она не верна в принципе. Об этом подробнее здесь: http://www.senocular.com/flash/tutor...ansformmatrix/.

Приведу так же и свой код "возрождения" объекта matrix, его можно исопльзовать когда нет увренности в том, что объект
matrix не обнулён.
Пример 4:

Код AS3:
package Utils 
{
	import flash.display.DisplayObject;
	import flash.geom.Matrix;
	import flash.geom.Matrix3D;
	/**
	 * 
	 *  Класс предназначен для трансляции матриц
	 * отображаемых объектов из объекта matrix в 
	 * matrix3D и обратно.
	 * ...
	 * @author fish_r
	 */
	public final class TranslationMatrixs 
	{
		private static var _rawData:Vector.<Number>;
 
		/**
		 *  Транслирует обратимую matrix3D в 
		 * "простую" matrix2D DisplayObject-а
		 * @param	obj - любой объект-наследник класса DisplayObject
		 */
		public static function transInMatrix(obj:DisplayObject):void
		{
			var matrix3D:Matrix3D;
			var matrix:Matrix;
 
 
			if (!obj.transform.matrix)
			{
				matrix3D = obj.transform.matrix3D;
				_rawData = matrix3D.rawData;
 
				/**
				 * В результате извлечения данных объекта matrix3D
				 * получаем вектор содержащий данные матрицы3Д, например:
				 * 
				 * 1,0,0,0, | 0,2.9981725215911865,0.10469848662614822,0  | ,0,-0.03489949554204941,0.9993908405303955,0 | ,-12,-28,0,1
				 * 1,0,0,0, | 0,1                  ,0                 ,0  | ,0,0                   ,1                 ,0 | ,-12,-28,2,1
				 * 
				 *   1,   0, 0, 0, 
				 *   0,   1, 0, 0, 
				 *   0,   0, 1, 0,
				 * -12, -28, 2, 1.
				 * 
				 * Где указанные цифры эквивалентны последовательности матрицы
				 * 4х4:
				 * 
				 *  a,  b,  0,  0,
				 *  c,  d,  0,  0,
				 *  0,  0,  z,  0,
				 * tx, ty, tz, tw.
				 * 
				 */
 
 
				/**
				 * создаем объект 2-х мерной матрицы
				 */
				matrix = new Matrix( _rawData[0], _rawData[1], _rawData[4], _rawData[5], _rawData[12], _rawData[13]);
 
				/**
				 * обнуляем matrix3D и сообщаем
				 * matrix-у созданное значение
				 */
				obj.transform.matrix3D = null;
				obj.transform.matrix = matrix;
			}
			else
			{
				/**
				 *  Матрица объекта не равна null, поэтому 
				 * не применяем преобразование
				 */
 
				////////////// T R A C E  //////////////////
				trace("Utils.TranslationMatrixs.transInMatrix. Переданный объект содержит настроенную матрицу " + 
					" Преобразование не было произведено. obj.transform.matrix : " + obj.transform.matrix);
				////////////// A N D  T R A C E  ///////////
			}
		}
 
 
 
	}
 
}
Примечание. Надо учитывать, что информация о осевых поворотах(например myObject.rotationX,Y,Z) и смещению
по z-координате безвозвратно теряется в результате применения метода TranslationMatrixs.transInMatrix к объекту.
Если же вы, всё таки, решили вращать объект через трансформацию его объекта matrix, как в способах №2 и №3, а он равен null, то вначале
примените к нему приведенный метод для реанимации объекта matrix.


5. Поворот объекта с использованием прямого преобразования исходного объекта Matrix3D.

Ну и последний из рассматриваемых здесь способов вращения объекта. Matrix3D.appendRotation(), в этот метод сразу
передается точка, вокруг которой объект будет вращаться. Ещё одно его отличие от методов Matrix, в том, что для
указаний оси вращения и точки вокруг которой вращение происходит используется класс Vector3D (совершенно не тоже са-
мое что Vector<>) представляющий из себя матрицу 4 х 1 с последовательностью X, Y, Z, W, где последний элемент
является необязательным при вращениях и всегда равен 0 (нуль), мы прикладываем изменения непосредственно
к объекту matrix3D содержащемуся во вращаемом объекте, а не к его копии, экземпляру, как в случае с matrix.
Ещё хочу обратить ваше внимание на то, что объект matrix3D DisplayObjecta изначально равен null. Он создается
в результате наклонов объекта по осям либо при обращении к сеттеру z объекта, т.е. для того чтобы его искусственно
инициализировать достаточно например такой строчки myObject.z = myObject.z, ну и учитывайте, что matrix после этого
действия станет равным null.



Пример 5:

Код AS3:
/**
		 *        Вращение объекта при помощи метода Matrix3D.appendRotation.
		 * Так же как и в случае вращения объекта с помощью матрицы 2D необходим 
		 * перевод точки вращения из системы координат объекта в систему коорди-
		 * нат контейнера, однако в отличии от последнего преобразование в каждой
		 * итерации не вызывает смещения объекта относительно оси вращения.
		 * 
		 * 
		 * @param	obj - вращаемый вокруг произвольной точки объект
		 * @param	cX - координата точки по оси Х
		 * @param	cY - координата точки по оси У
		 * @param	angle - угол поворота в градусах.
		 */
		internal static function rotateMatrix3D(obj:DisplayObject, cX:Number, cY:Number, angle:Number):void
		{
			var matrix3D:Matrix3D;
			if (_baseMatrix3D) matrix3D = _baseMatrix3D;
			else matrix3D = obj.transform.matrix3D;
 
			/**
			 * Преобразование координат точки вращения из
			 * локальных координат вращаемого объекта в коорди-
			 * наты его контейнера
			 */
			var pGlobal:Point = obj.localToGlobal(new Point(cX, cY));
			var pRotate:Point = obj.parent.globalToLocal(pGlobal);
 
			/**
			 *  Вариант из спр. руководства
			 * 
			 */
			/*matrix3D.appendTranslation( -pRotate.x, -pRotate.y, 0);
			matrix3D.appendRotation(angle, Vector3D.Z_AXIS);
			matrix3D.appendTranslation( pRotate.x, pRotate.y, 0);*/
 
			/**
			 * Другой, рабочий, вариант
			 */
 
			var pivotP:Vector3D = new Vector3D(pRotate.x, pRotate.y, 0, 0);
			matrix3D.appendRotation(angle, Vector3D.Z_AXIS, pivotP);
			obj.transform.matrix3D = matrix3D;
 
		}
Недостатки:
- справочное руководство утверждает ()
"Когда преобразование метода appendRotation() применяется к объекту Matrix3D экранного объекта, кэшированные
значения свойств поворота экранного объекта становятся недействительными."

Это не верно. Установлено опытным путем. Информация о поворотах по осям с помощью методов, например, rotationX,
rotationY сохраняется. Зато, если объект имеет реальный наклон по указанным осям, это приводит к неверной работе
метода в контексте 2D (2.5D) приложения (!). Будьте внимательны.


Примечание.
Интересно, что, здесь, в случае "множественного" или постоянного вращения, при передаче перед каждым поворотом
текущей матрицы в метод (а не созданной до процедур поворотов), а также при расчете точки вращения в
сист. координат контейнера (что, ещё раз подчеркну, не хорошо, расчеты можно и нужно вынести за скобки метода,
в этом случае, в нем только вращение) не происходит постепенного смещения объекта относительно точки вращения.
Класс Matrix3D имеет и другие, может быть и более эффективные, способы вращения объекта вокруг произволь-
но выбранной точки, которые здесь не рассматриваются. Ознакомиться с ними вы можете, конечно, в хелпе.
http://help.adobe.com/ru_RU/AS3LCR/F.../Matrix3D.html


Напоследок приведу полный код класса с перечисленными выше методами:

Код AS3:
package src 
{
	import flash.display.DisplayObject;
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.geom.Matrix;
	import fl.motion.MatrixTransformer;
	import flash.geom.Matrix3D;
	import flash.geom.Point;
	import flash.geom.Vector3D;
 
	/**
	 *  Класс содержит набор статических методов
	 * для поворотов объекта вокруг произвольно
	 * выбранной точки.
	 *
	 * @author fish_r
	 */
	public class MatrixRotate 
	{
 
 
		/**
		 *  Начальная матрица объекта.
		 */
		private static var  _baseMatrix:Matrix;
 
		/**
		 *  Начальная 3D матрица
		 */
		private static var  _baseMatrix3D:Matrix3D;
 
 
/**-------------------------------------------------------------------------------
* 
* 	
* 
*	               S E T   &   G E T  	
*
* 
* 
* ------------------------------------------------------------------------------
*/
		/**
		 *  Сеттер начальной матрицы объекта
		 */
		public static function set baseMatrix(matrix:Matrix):void
		{
			if (_baseMatrix === matrix) return;
			_baseMatrix = matrix;
			return;
		}
 
 
		/**
		 *  Сеттер начальной 3D матрицы объекта
		 */
		public static function set baseMatrix3D(matrix3D:Matrix3D):void
		{
			if (_baseMatrix3D === matrix3D) return;
			_baseMatrix3D = matrix3D;
			return;
		}
 
 
/**-------------------------------------------------------------------------------
* 
* 	
* 
*	         М Е Т О Д Ы  В Р А Щ Е Н И Я    О Б Ъ Е К Т А 	
*
* 
* 
* ------------------------------------------------------------------------------
*/
		/**
		 *  Вращаемый контейнер  для опосредованного
		 * вращения объекта.
		 */
 
		public static var cntRotator:Sprite = new Sprite();
		/**
		 *  Вращает объект посредством вращения контейнера его содержащего
		 * и специально для этого созданного.
		 * 
		 * @param	obj - вращаемый вокруг произвольной точки объект
		 * @param	cX - координата точки по оси Х
		 * @param	cY - координата точки по оси У
		 * @param	angle - угол поворота в градусах.
		 */
		public static function rotationContainer(obj:DisplayObject, cX:Number, cY:Number, angle:Number):void
		{
			var pGlobal:Point;
			var pRotate:Point;
			var pLocal:Point;
 
 
 
			/**
			 * Добавляем на сцену спрайт для поворота объекта,
			 * помещаем в него объект, сдвигаем спрайт относительно
			 * контейнера, а объект относительно нулевых координат 
			 * спрайта.
			 */
 
			if (cntRotator.numChildren === 0) 
			{
				pGlobal = obj.localToGlobal(new Point(cX, cY));
				pRotate = obj.parent.globalToLocal(pGlobal);
			    obj.parent.addChild(cntRotator);
				cntRotator.addChild(obj);
				cntRotator.x = pRotate.x; cntRotator.y = pRotate.y;
				obj.x = -cX; obj.y = -cY;
			}
 
			/**
			 * вращаем (angle - градус поворота)
			 * это единственный способ поворота при котором возможно использование
			 * нативного твина,а это место где его, как раз, можно разместить
			 */
 
			cntRotator.rotation += angle;
 
			/**
			 *  Удаляем отработавший спрайт, объект размещаем
			 * в первоначальном контейнере, предварительно преобразовав
			 * координаты объекта относительно спрайта для поворота, в координаты
			 * объекта первоначального контейнера, сдвигаем и поворачиваем
			 * его таким образом как был повернут спрайт
			 */
 
			pGlobal = obj.localToGlobal(new Point(0, 0));
			pLocal = cntRotator.parent.globalToLocal(pGlobal);
 
			cntRotator.removeChild(obj);
			cntRotator.parent.addChild(obj);
			obj.parent.removeChild(cntRotator);
			obj.rotation = cntRotator.rotation;
			obj.x = pLocal.x;
			obj.y = pLocal.y;
 
 
			return;
 
		}
 
 
		/**
		 *  Вращение объекта вокруг произвольной точки
		 * выраженной в системе координат объекта. С исполь-
		 * зованием метода нативного класса fl.utils.MatrixTransformer
		 * 
		 * _baseMatrix - исходная матрица объекта, устанавливаемая  через сетер. Важно, что 
		 * если поворотов  будет несколько, исходная матрица должна иметь значение 
		 * которое было до начала процедур поворотов, иначе из за относительной 
		 * неточности расчетов, со временем, становится заметным смещение объекта 
		 * к координатам 0,0 сцены.
		 * 
		 * @param	obj - вращаемый вокруг произвольной точки объект
		 * @param	cX - координата точки по оси Х
		 * @param	cY - координата точки по оси У
		 * @param	angle - угол поворота в градусах.
		 */
 
 
		internal static function rotateMatrixTransformer( obj:DisplayObject, cX:Number, cY:Number, angle:Number ):void
		{
			var matrix:Matrix;
			if (_baseMatrix) matrix = _baseMatrix;
			else matrix = obj.transform.matrix;
 
			MatrixTransformer.rotateAroundInternalPoint( matrix, cX, cY, angle );
 
			obj.transform.matrix = matrix;
 
		}
 
 
		/**
		 *  Вращение объекта вокруг произвольной точки
		 * выраженной в системе координать его контейнера, а
		 * не его самого при помощи нативных методов изменений
		 * матрицы объекта.
		 * 
		 * _baseMatrix - исходная матрица объекта, устанавливаемая  через сетер. Важно, что 
		 * если поворотов  будет несколько, исходная матрица должна иметь значение 
		 * которое было до начала процедур поворотов, иначе из за относительной 
		 * неточности расчетов, со временем, становится заметным смещение объекта 
		 * к координатам 0,0 сцены.
		 * 
		 * 
		 * @param	obj - вращаемый вокруг произвольной точки объект
		 * @param	cX - координата точки по оси Х
		 * @param	cY - координата точки по оси У
		 * @param	angle - угол поворота в градусах.
		 */
		internal static function rotateMatrixMethodsOnly( obj:DisplayObject, cX:Number, cY:Number, angle:Number ):void
		{
			var matrix:Matrix;
 
			if (_baseMatrix) matrix = _baseMatrix;
			else matrix = obj.transform.matrix;
 
			/**
			 * Преобразование координат точки вращения из
			 * локальных координат вращаемого объекта в коорди-
			 * наты его контейнера
			 */
 
			var pGlobal:Point = obj.localToGlobal(new Point(cX, cY));
			var pRotate:Point = obj.parent.globalToLocal(pGlobal);
 
 
			var rad:Number = angle * (Math.PI / 180);
 
 
			matrix.translate( -pRotate.x, -pRotate.y );
			matrix.rotate( rad );
			matrix.translate( pRotate.x, pRotate.y );
 
			obj.transform.matrix = matrix;
		}
 
 
		/**
		 *        Вращение объекта при помощи метода Matrix3D.appendRotation.
		 * Так же как и в случае вращения объекта с помощью матрицы 2D необходим 
		 * перевод точки вращения из системы координат объекта в систему коорди-
		 * нат контейнера, однако в отличии от последнего преобразование в каждой
		 * итерации не вызывает смещения объекта относительно оси вращения.
		 * 
		 * 
		 * @param	obj - вращаемый вокруг произвольной точки объект
		 * @param	cX - координата точки по оси Х
		 * @param	cY - координата точки по оси У
		 * @param	angle - угол поворота в градусах.
		 */
		internal static function rotateMatrix3D(obj:DisplayObject, cX:Number, cY:Number, angle:Number):void
		{
			var matrix3D:Matrix3D;
			if (_baseMatrix3D) matrix3D = _baseMatrix3D;
			else matrix3D = obj.transform.matrix3D;
 
			/**
			 * Преобразование координат точки вращения из
			 * локальных координат вращаемого объекта в коорди-
			 * наты его контейнера
			 */
			var pGlobal:Point = obj.localToGlobal(new Point(cX, cY));
			var pRotate:Point = obj.parent.globalToLocal(pGlobal);
 
			/**
			 *  Вариант из спр. руководства
			 * 
			 */
			/*matrix3D.appendTranslation( -pRotate.x, -pRotate.y, 0);
			matrix3D.appendRotation(angle, Vector3D.Z_AXIS);
			matrix3D.appendTranslation( pRotate.x, pRotate.y, 0);*/
 
			/**
			 * Другой, рабочий, вариант
			 */
 
			var pivotP:Vector3D = new Vector3D(pRotate.x, pRotate.y, 0, 0);
			matrix3D.appendRotation(angle, Vector3D.Z_AXIS, pivotP);
			obj.transform.matrix3D = matrix3D;
 
		}
 
	}
 
}



Послесловие. Статья не претендует на всестороннее и полное рассмотрение способов, методов и особенностей
поворотов DisplayObject-ов вокруг произвольно выбранной точки. Она призвана, лишь, дать первичные инструменты
для гарантированного выполнения такой операции. Раскрывает некоторые особенности работы с объектами matrix и
matrix3D. А так же указывает не неполноту и неточности в описаниях справочного руководства.
И, возможно, последует продолжение...


Приложения. Здесь я прилагаю архив содержащий проект FD (пользоваться приложением можно и без установленного FlashDevelop).
В нем содержится стат. класс содержащий все методы о которых говорится здесь, а так же тестовый класс, класс преобра-
зования матриц и фла файл с тестируемыми объектами. Не стал вкомпиливать символы в код, потому, что заинтересовавшим -
ся специалистам достаточно будет просто просмотреть код без тестирования, а у начинающих программеров всегда имеется
Flash (кстати фла образца Flash CS4). Код не является сложным для восприятия и не предназначен для реализации
в проектах, однако его части или весь целиком может быть использован по своему усмотрению, в том числе и в ком. целях,
имеет множество закомментированных строк которые при расскоменнтировании изменяют либо среду выполнения,
либо метод выполнения поворота и пр.
Вложения
Тип файла: rar MatrixExample.rar (23.1 Кб, 352 просмотров)
Всего комментариев 13

Комментарии

Старый 30.04.2011 22:44 GBee вне форума
GBee
 
Аватар для GBee
2 способ
Цитата:
- не требует преобразования точки вращения из системы координат объекта в систему координат
контейнера его содержащего.
Вы уверены, что внутри MatrixTransformer.rotateAroundInternalPoint этого не происходит? Зачем туда отдается матрица? :о)
Старый 30.04.2011 22:48 -De- вне форума
-De-
 
Аватар для -De-
Кстати, а почему нет варианта, где вместо
Код AS3:
matrix.translate( -pRotate.x, -pRotate.y );
			matrix.rotate( rad );
			matrix.translate( pRotate.x, pRotate.y );
просто берутся и пишутся a, b, c, d, tx, ty матрицы? Любой второкур тех. вуза должен уметь.
Старый 30.04.2011 22:56 in4core вне форума
in4core
 
Аватар для in4core
Цитата:
Любой второкур тех. вуза должен уметь.
ну зачем же так!
Старый 30.04.2011 22:57 in4core вне форума
in4core
 
Аватар для in4core
Код AS3:
pGlobal = obj.localToGlobal(new Point(0, 0));
			pLocal = cntRotator.parent.globalToLocal(pGlobal);
 
			cntRotator.removeChild(obj);
			cntRotator.parent.addChild(obj);
			obj.parent.removeChild(cntRotator);
			obj.rotation = cntRotator.rotation;
			obj.x = pLocal.x;
			obj.y = pLocal.y;
 
 
			return;
Зачем ретурн?

И второй вопрос, зачем кругом internal методы ? смысловая нагрузка применения их вместо private какая?

Пакеты стоит указывать так же с маленькой буквы.
Обновил(-а) in4core 30.04.2011 в 23:01
Старый 01.05.2011 08:41 fish_r вне форума
fish_r
 
Аватар для fish_r
@GBee, конечно же я имел в виду, что этого не требуется от вас, а не то, что внутри метода происходит.

@-De-. А почему спрашиваете? Может лучше, сразу, кодом? Вообще там много чего ещё нет, обще и поверхностно. Писать не очень то и хотелось, а тут еще оказалось, что и довольно скучно . Просто на форуме о некоторых вещах не упоминается, да и всё в разных местах. Решил, что надо бы попытаться собрать в кучу....

@in4core.
- где то читал, что return; зрительно помогает определить конец функции и мне эта мысль понравилась, но ещё как то не привык везде это делать..
- код конечно неряшливый, но ведь я же указал, что не для применения в проектах (когда писал и публиковать то не собирался). Это вообще из разряда "наколеночных" кодов которые пишутся для изучения чего либо, затем просто выкидываются, либо хранятся в черновиках. Поэтому оформление на втором плане. Вообще, непонятно, private то вам зачем? По сути это библиотека методов, здесь всё паблик, по идее, быть должно.
Про AND верно заметили, лажанул . Спасибо.
Обновил(-а) fish_r 01.05.2011 в 08:44
Старый 01.05.2011 18:22 Котяра вне форума
Котяра
 
Аватар для Котяра
Зрительно определить конец функции помогает конец функции т.е. закрывающая скобка и нормальная табуляция. Return не нужен - это лишний шум. Не надо читать всякие глупости.
Код AS3:
package src
- не надо называть так пакеты. src нужно прописывать в параметрах проекта (путь до исходников)
Обновил(-а) Котяра 01.05.2011 в 18:26
Старый 02.05.2011 10:41 fish_r вне форума
fish_r
 
Аватар для fish_r
ага, понял, насчёт src...
насчет return-a - незнаю, сам ещё не определился, ещё и поэтому - то ставлю, то нет... Присматриваюсь.
Старый 03.05.2011 10:47 incoob вне форума
incoob
Можно уточнить про нативность MatrixTransformer? Я слышал только о fl.motion.MatrixTransformer, который поставляется (в исходниках) вместе с Flas CS. Тем более, что в итоговом классе вы используете именно его
Старый 03.05.2011 11:09 f.g.programmer вне форума
f.g.programmer
 
Аватар для f.g.programmer
Цитата:
- где то читал, что return; зрительно помогает определить конец функции и мне эта мысль понравилась, но ещё как то не привык везде это делать..
Возможно там имелся в виду смысловой конец функции, например вместо
Код AS3:
private function doSomething():void {
	if (isAction()) {
		// do...
		// do...
		// do...
		// do...
		// do...
		// do...
		// do...
		// do...
		// do...
		// do...
	}
}
Использовать
Код AS3:
private function doSomething():void {
	if (!isAction()) {
		return;
	}
	// do...
	// do...
	// do...
	// do...
	// do...
	// do...
	// do...
	// do...
	// do...
	// do...
}
Старый 03.05.2011 11:17 f.g.programmer вне форума
f.g.programmer
 
Аватар для f.g.programmer
А вообще довольно полезная статья, особенно факт замещения 2д-матрицы 3д-матрицей. Возможно удалением 3д-матриц получится оптимизировать рисованную художниками графику, конечно если это не приведет к значительным визуальным артефактам.
Старый 03.05.2011 13:53 fish_r вне форума
fish_r
 
Аватар для fish_r
@incoob действительно - fl.motion..., в fl и нет то utils. Поправил. Спасибо.
Старый 11.02.2012 20:48 PlutDem вне форума
PlutDem
 
Аватар для PlutDem
Столкнулся с проблемой, используя третий способ поворота. При использовании начальной матрицы, если объект был перемещен куда то, а потом повернут, то он вернется в свое изначальное положение (tx,ty изначальной матрицы ведь не менялись). как можно решить эту проблему?
Старый 23.02.2012 06:18 fish_r вне форума
fish_r
 
Аватар для fish_r
В одной из тем Wolsh привел ещё и тригонометрическое решение. И дабы оно не потерялося в пучинах форума приведу его здесь. Дополнять основную запись, правда, не буду (может быть потом, когда нибудь). Иначе форматирование съедет нафиг, редактировать неохота. Расчеты учитывают и компенсируют постепенное смещение объекта при вращении.

6. Тригонометрическое вращение.


Код AS3:
package  
{
    import flash.geom.Point;
    import flash.display.DisplayObject;
/* * * * * * * * * *
*                  *
*  @author wolsh   *
*                  *
\* * * * * * * * * */ 
 
    public function pointRotate(object:DisplayObject, center:Point, angle:Number) : void
    {
        // return to zero
        angle += object.rotation;
        var a0:Number = - object.rotation * Math.PI / 180;
        var s0:Number = Math.sin(a0);
        var c0:Number = Math.cos(a0);
        var dX0:Number = object.x - center.x;
        var dY0:Number = object.y - center.y;
 
        object.rotation = 0;
        object.x = Math.round(center.x + dX0 * c0 - dY0 * s0);
        object.y = Math.round(center.y + dX0 * s0 + dY0 * c0);
 
        // new rotation
        var r:Number = angle * Math.PI / 180;
        var s:Number = Math.sin(r);
        var c:Number = Math.cos(r);
        var dX:Number = object.x - center.x;
        var dY:Number = object.y - center.y;
 
        object.rotation += angle;
        object.x = center.x + dX * c - dY * s;
        object.y = center.y + dX * s + dY * c;
    }
 
}

И пример прокомментированный автором:

Цитата:
Во вложении тестовая свфка просто.
Можно кликать и ставить точку, вокруг которой прямоугольник крутится.
Черная точка ставится в стейдже, а белая - внутри самого прямоугольника, чтобы наблюдать люфт.
 

 


Часовой пояс GMT +4, время: 18:28.


Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2022, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.