Ответ 1
- В вашем случае мы можем сказать, что вращение устройства равно вращению устройства в обычном режиме (вращение вокруг самой нормы просто игнорируется, как вы указали)
- CMAttitude, который вы можете получить через CMMotionManager.deviceMotion обеспечивает вращение относительно опорной рамы. Его свойства quaternion, roation матрица и углы Эйлера - это просто разные представления.
- Эталонный кадр может быть указан при запуске обновлений движений устройства с использованием метода CMMotionManager startDeviceMotionUpdatesUsingReferenceFrame. До iOS 4 вам пришлось использовать multiplyByInverseOfAttitude
Сложив это вместе, вам просто нужно умножить кватернион в правильном направлении с нормальным вектором, когда устройство лежит лицом к лицу на столе. Теперь нам нужен этот правильный способ умножения кватернионов, который представляет собой поворот: согласно Вращающиеся векторы это делается с помощью:
n = q * e * q ', где q - кватернион, поставленный CMAttitude [w, (x, y, z)], q' является его сопряженным [w, (-x, -y, -z)] и e является кватернионным представлением прямой вверх по нормали [0, (0, 0, 1)], К сожалению, Apple CMQuaternion является структурой и, следовательно, вам нужен небольшой вспомогательный класс.
Quaternion e = [[Quaternion alloc] initWithValues:0 y:0 z:1 w:0];
CMQuaternion cm = deviceMotion.attitude.quaternion;
Quaternion quat = [[Quaternion alloc] initWithValues:cm.x y:cm.y z:cm.z w: cm.w];
Quaternion quatConjugate = [[Quaternion alloc] initWithValues:-cm.x y:-cm.y z:-cm.z w: cm.w];
[quat multiplyWithRight:e];
[quat multiplyWithRight:quatConjugate];
// quat.x, .y, .z contain your normal
Quaternion.h:
@interface Quaternion : NSObject {
double w;
double x;
double y;
double z;
}
@property(readwrite, assign)double w;
@property(readwrite, assign)double x;
@property(readwrite, assign)double y;
@property(readwrite, assign)double z;
Quaternion.m:
- (Quaternion*) multiplyWithRight:(Quaternion*)q {
double newW = w*q.w - x*q.x - y*q.y - z*q.z;
double newX = w*q.x + x*q.w + y*q.z - z*q.y;
double newY = w*q.y + y*q.w + z*q.x - x*q.z;
double newZ = w*q.z + z*q.w + x*q.y - y*q.x;
w = newW;
x = newX;
y = newY;
z = newZ;
// one multiplication won't denormalise but when multipling again and again
// we should assure that the result is normalised
return self;
}
- (id) initWithValues:(double)w2 x:(double)x2 y:(double)y2 z:(double)z2 {
if ((self = [super init])) {
x = x2; y = y2; z = z2; w = w2;
}
return self;
}
Я знаю, что кватернионы немного странны в начале, но как только у вас появилась идея, они действительно блестящие. Это помогло мне представить кватернион как вращение вокруг вектора (x, y, z), а w (угол косинуса).
Если вам нужно сделать больше с ними, посмотрите cocoamath проект с открытым исходным кодом. Классы Quaternion и его расширение QuaternionOperations являются хорошей отправной точкой.
Для полноты, да, вы можете сделать это и с умножением матрицы:
n = M * e
Но я бы предпочел, чтобы кватернионный способ избавил вас от всех тригонометрических хлопот и работает лучше.