Znajdowanie normalnego wektora na urządzenie z systemem iOS
Chciałbym użyć CMAttitude, aby poznać wektor normalny do szyby ekranu iPada/iPhone ' a (w stosunku do ziemi). Jako taki otrzymałbym wektory takie jak:
Zauważ, że to różni się od orientacji, w tym, że nie obchodzi mnie, jak urządzenie jest obracane wokół osi Z. Więc gdybym trzymał iPada nad głową zwróconą w dół, to czytałby (0, -1,0), a nawet gdy obracałem go wokół głowy (jak helikopter), nadal czytałby (0,-1,0):
Wydaje mi się, że to może być dość łatwe, ale ponieważ jestem nowy w quaternions i nie do końca rozumiem opcje ramki odniesienia dla ruchu urządzenia, to unikają mnie cały dzień.
2 answers
- w Twoim przypadku możemy powiedzieć, że obrót urządzenia jest równy obrotowi urządzenia normalnego (obrót wokół samego normalnego jest po prostu ignorowany, tak jak go podałeś)
- CMAttitude które można uzyskać poprzez CMMotionManager.deviceMotion zapewnia rotację względem ramki odniesienia . Jego właściwości quaternion, roation macierze i kąty Eulera są po prostu różnymi reprezentacjami.
- ramka odniesienia może być określona podczas uruchamiania urządzenia aktualizacje ruchu przy użyciu metody CMMotionManager startDeviceMotionUpdatesUsingReferenceframe . Do iOS 4 trzeba było używać multiplebyinverseofattitude
Składając to razem, wystarczy pomnożyć kwaternion w we właściwy sposób z wektorem normalnym, gdy urządzenie leży twarzą do góry na stole. Teraz potrzebujemy właściwej drogi mnożenia czwartorzędowego, które reprezentuje obrót: zgodnie z wektorami obrotowymi jest to zrobione autor:
N = q * e * q' gdzie q jest kwaternionem dostarczonym przez CMAttitude [W, (x, y, z)], q' jest jego koniugatem [w, (-x, -y, -z)] i e jest czwartorzędową reprezentacją twarzy normalnej [0, (0, 0, 1)]. Niestety CMQuaternion Apple jest strukturą i dlatego potrzebujesz małej klasy pomocniczej.
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;
}
Wiem, że quaternions są trochę dziwne na początku, ale kiedy już mam pomysł są naprawdę genialne. To pomogło mi wyobrazić sobie quaternion jako obrót wokół wektora (x, y, z) i w jest (cosinus) kąta.
Jeśli chcesz zrobić z nimi więcej, spójrz na cocoamath projekt open source. Klasa Quaternion i jej rozszerzenie QuaternionOperations są dobrym punktem wyjścia.
Ze względu na kompletność, tak można to zrobić z mnożeniem macierzy, jak również:
N = M * E
Ale Ja wolałby quaternion sposób oszczędza Ci wszystkie problemy trygonometryczne i działa lepiej.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-20 16:59:55
Podziękowania dla Kay za punkt wyjścia do rozwiązania. Oto moja realizacja dla każdego, kto jej potrzebuje. Zrobiłem kilka małych tweeksów do Rady Kay w mojej sytuacji. Jako ostrzeżenie, używam prezentacji tylko w krajobrazie. Mam kod, który aktualizuje zmienną _isLandscapeLeft, aby dokonać niezbędnego dostosowania do kierunku wektora.
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;
- (id) initWithValues:(double)w2 x:(double)x2 y:(double)y2 z:(double)z2;
- (Quaternion*) multiplyWithRight:(Quaternion*)q;
@end
Quaternion.m
#import "Quaternion.h"
@implementation Quaternion
- (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;
}
@end
I moja klasa gry, która używa quaternionu do strzelectwo: {]}
-(void)fireWeapon{
ProjectileBaseClass *bullet = [[ProjectileBaseClass alloc] init];
bullet.position = SCNVector3Make(0, 1, 0);
[self.rootNode addChildNode:bullet];
Quaternion *e = [[Quaternion alloc] initWithValues:0 x:0 y:0 z:1];
CMQuaternion cm = _currentAttitude.quaternion;
Quaternion *quat = [[Quaternion alloc] initWithValues:cm.w x:cm.x y:cm.y z:cm.z];
Quaternion *quatConjugate = [[Quaternion alloc] initWithValues:cm.w x:-cm.x y:-cm.y z:-cm.z];
quat = [quat multiplyWithRight:e];
quat = [quat multiplyWithRight:quatConjugate];
SCNVector3 directionToShoot;
if (_isLandscapeLeft) {
directionToShoot = SCNVector3Make(quat.y, -quat.x, -quat.z);
}else{
directionToShoot = SCNVector3Make(-quat.y, quat.x, -quat.z);
}
SCNAction *shootBullet = [SCNAction moveBy:directionToShoot duration:.1];
[bullet runAction:[SCNAction repeatActionForever:shootBullet]];
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-13 11:25:30