- В вашем случае мы можем сказать, что вращение устройства равно вращению нормали устройства (вращение вокруг самой нормали просто игнорируется, как вы это указали)
- CMAttitude, который можно получить через CMMotionManager.deviceMotion обеспечивает вращение относительно опорного кадра. Его свойства кватернион, матрица вращения и углы Эйлера — это просто разные представления.
- Опорный кадр можно указать при запуске обновлений движения устройства с помощью startDeviceMotionUpdatesUsingReferenceFrame. До iOS 4 вам приходилось использовать умножитьByInverseOfAttitude
Собрав это вместе, вам просто нужно умножить кватернион правильным образом на вектор нормали, когда устройство лежит на столе лицевой стороной вверх. Теперь нам нужен этот правильный способ умножения кватернионов, представляющий поворот: согласно Вращение векторов это делается с помощью:
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
Кватернион.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*) 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
Но я бы предпочел кватернион, поскольку он избавляет вас от всех тригонометрических хлопот и работает лучше.
person
Kay
schedule
31.05.2012