似乎仍然有使用四元数的万向节锁
我使用参考四元数维护对象的位置...
我使用以下代码从 2D xy 触摸滑动旋转参考四元数...
-(void)rotateViewOnX:(CGFloat)x andOnY:(CGFloat)y
{
// x and y should be a simple Cartesian pixel movements ...
// the screen swipe moved m pixels in the x direction and n pixels in the y direction ...
// ignore micro movements ...
if(abs(x) < 0.1 && abs(y) < 0.1)
return;
// ignore excessive movements ...
if(abs(x) > VIEW_PORTAL_SIZE/3.0 || abs(y) > VIEW_PORTAL_SIZE/3.0)
return;
// simulate a very, very large trackball ...
double radius = VIEW_PORTAL_SIZE/2.0;
x = x/radius;
y = y/radius;
double z = sqrt(1 - x*x - y*y);
Vector *trackball = [[Vector alloc] initWithX:x andY:y andZ:z];
// use dot product to get the angle of rotation on the trackball
double theta = acos([self.zReferenceVector dotProduct:trackball]);
// use cross product to get the axis of rotation - convert to a unit vector
// this is an autoreleased object ...
Vector *rotationAxis = [[self.zReferenceVector crossProduct:trackball] normalise];
// create a quaternion to represent the latest movement from the simulated trackball ...
Quaternion *change = [[Quaternion alloc] initWithAngle:theta aroundVector:rotationAxis];
[change unitise];
// rotate the reference quaternion by this quaternion ...
self.zReferenceQuaternion = [[change times:self.zReferenceQuaternion] times:[change inverse]];
[self.zReferenceQuaternion unitise];
// clean-up ...
[trackball release]; trackball = nil;
[change release]; change = nil;
// and paint ...
[self setNeedsDisplay];
}
要旋转对象,我将以下代码应用于对象中的每个向量点...
// align the cube to the rotated reference quaternion ...
v = [self.zReferenceQuaternion rotateVector:v];
它使用以下方法...
-(Vector*)rotateVector:(Vector*)v
{
Quaternion *myInverse = [self inverse];
Quaternion *pureQuat = [[Quaternion alloc] initWithValues:0.0 :v.x :v.y :v.z];
Quaternion *step1 = [self times: pureQuat];
Quaternion *step2 = [step1 times: myInverse];
[pureQuat release];
return [[[Vector alloc] initWithX:step2.x andY:step2.y andZ:step2.z] autorelease];
}
我的问题是,当对象处于 180 度左右时,如果我沿 x 方向旋转它,我无法沿 y 方向移动它。相反,如果我在 y 方向上旋转它,旋转大约 180 度时,我无法在 x 方向上移动它。不知道我做错了什么!
后脚本...
为了回答评论员,整个四元数类目前如下:
#import "Quaternion.h"
@implementation Quaternion
@synthesize w=_w;
@synthesize x=_x;
@synthesize y=_y;
@synthesize z=_z;
// ----- initialisers
// this is the designated initaliser for w, x, y, z ...
-(id) initWithValues:(double)a :(double)b :(double)c :(double)d
{
self = [super init];
if(self)
{
_w = a; //1
_x = b; //i
_y = c; //j
_z = d; //k
}
return self;
}
-(id) initWithAngle:(double)angle aroundVector:(Vector*)vector
{
// some preliminaries ...
double halfAngle = angle / 2.0;
double sinAngleOnTwo = sin(halfAngle); // do once
Vector *v = [vector normalise];
// calculate the quaternion ...
double w = cos(halfAngle);
double x = v.x * sinAngleOnTwo;
double y = v.y * sinAngleOnTwo;
double z = v.z * sinAngleOnTwo;
// return result ...
return [self initWithValues:w :x :y :z];
}
-(Quaternion*)copy
{
Quaternion *a = self;
return [[Quaternion alloc] initWithValues:a.w :a.x :a.y :a.z];
}
// ----- algebra
-(Quaternion*)conjugate
{
Quaternion *a = self;
return [[[Quaternion alloc] initWithValues:a.w :-a.x :-a.y :-a.z ] autorelease];
}
-(Quaternion*)plus:(Quaternion*)b
{
Quaternion *a = self;
return [[[Quaternion alloc] initWithValues:a.w+b.w :a.x+b.x :a.y+b.y :a.z+b.z] autorelease];
}
-(Quaternion*)minus:(Quaternion*)b
{
Quaternion *a = self;
return [[[Quaternion alloc] initWithValues:a.w-b.w :a.x-b.x :a.y-b.y :a.z-b.z] autorelease];
}
-(Quaternion*)times:(Quaternion*) b
{
Quaternion *a = self;
double real = a.w*b.w - a.x*b.x - a.y*b.y - a.z*b.z;
double i = a.w*b.x + a.x*b.w + a.y*b.z - a.z*b.y;
double j = a.w*b.y - a.x*b.z + a.y*b.w + a.z*b.x;
double k = a.w*b.z + a.x*b.y - a.y*b.x + a.z*b.w;
return [[[Quaternion alloc] initWithValues:real :i :j :k] autorelease];
}
-(double)norm
{
Quaternion *a = self;
return a.w*a.w + a.x*a.x + a.y*a.y + a.z*a.z;
}
-(Quaternion*)inverse
{
Quaternion *a = self;
double n = [a norm];
return [[[Quaternion alloc] initWithValues:a.w/n :-a.x/n :-a.y/n :-a.z/n] autorelease];
}
-(Quaternion*)divides:(Quaternion*) b
{
Quaternion *a = self;
return [[a inverse] times: b];
}
-(double)magnitude
{
Quaternion *a = self;
return sqrt([a norm]);
}
-(void)unitise
{
double m = [self magnitude];
self.w /= m;
self.x /= m;
self.y /= m;
self.z /= m;
}
-(Quaternion*)unitQuaternion
{
Quaternion *u = [[self copy] autorelease];
[u unitise];
return u;
}
-(Vector*)rotateVector:(Vector*)v
{
Quaternion *vAsPureQuat = [[Quaternion alloc] initWithValues:0.0 :v.x :v.y :v.z];
Quaternion *r = [[self times: vAsPureQuat] times:[self inverse]];
[vAsPureQuat release];
return [[[Vector alloc] initWithX:r.x andY:r.y andZ:r.z] autorelease];
}
// ----- misc
-(NSString*)toString
{
Quaternion *a = self;
return [NSString stringWithFormat:@"%f + %fi + %fj +%fk", a.w, a.x, a.y, a.z];
}
@end
I maintain the position of an object using a reference quaternion ...
I rotate a reference quaternion from 2D x-y touch swipes using the following code ...
-(void)rotateViewOnX:(CGFloat)x andOnY:(CGFloat)y
{
// x and y should be a simple Cartesian pixel movements ...
// the screen swipe moved m pixels in the x direction and n pixels in the y direction ...
// ignore micro movements ...
if(abs(x) < 0.1 && abs(y) < 0.1)
return;
// ignore excessive movements ...
if(abs(x) > VIEW_PORTAL_SIZE/3.0 || abs(y) > VIEW_PORTAL_SIZE/3.0)
return;
// simulate a very, very large trackball ...
double radius = VIEW_PORTAL_SIZE/2.0;
x = x/radius;
y = y/radius;
double z = sqrt(1 - x*x - y*y);
Vector *trackball = [[Vector alloc] initWithX:x andY:y andZ:z];
// use dot product to get the angle of rotation on the trackball
double theta = acos([self.zReferenceVector dotProduct:trackball]);
// use cross product to get the axis of rotation - convert to a unit vector
// this is an autoreleased object ...
Vector *rotationAxis = [[self.zReferenceVector crossProduct:trackball] normalise];
// create a quaternion to represent the latest movement from the simulated trackball ...
Quaternion *change = [[Quaternion alloc] initWithAngle:theta aroundVector:rotationAxis];
[change unitise];
// rotate the reference quaternion by this quaternion ...
self.zReferenceQuaternion = [[change times:self.zReferenceQuaternion] times:[change inverse]];
[self.zReferenceQuaternion unitise];
// clean-up ...
[trackball release]; trackball = nil;
[change release]; change = nil;
// and paint ...
[self setNeedsDisplay];
}
To rotate the object I apply the following code to each Vector point in the object ...
// align the cube to the rotated reference quaternion ...
v = [self.zReferenceQuaternion rotateVector:v];
Which uses the following method ...
-(Vector*)rotateVector:(Vector*)v
{
Quaternion *myInverse = [self inverse];
Quaternion *pureQuat = [[Quaternion alloc] initWithValues:0.0 :v.x :v.y :v.z];
Quaternion *step1 = [self times: pureQuat];
Quaternion *step2 = [step1 times: myInverse];
[pureQuat release];
return [[[Vector alloc] initWithX:step2.x andY:step2.y andZ:step2.z] autorelease];
}
My problem is that when the object is at around 180 degrees if I rotated it there in an x-direction I cannot move it in the y-direction. Conversely, if I rotated it there in the y-direction, at around 180 degrees rotated I cannot move it in the x-direction. Not sure what I am doing wrong!
Post script ...
In answer to the commentator, the entire Quaternion class, as it currently stands follows:
#import "Quaternion.h"
@implementation Quaternion
@synthesize w=_w;
@synthesize x=_x;
@synthesize y=_y;
@synthesize z=_z;
// ----- initialisers
// this is the designated initaliser for w, x, y, z ...
-(id) initWithValues:(double)a :(double)b :(double)c :(double)d
{
self = [super init];
if(self)
{
_w = a; //1
_x = b; //i
_y = c; //j
_z = d; //k
}
return self;
}
-(id) initWithAngle:(double)angle aroundVector:(Vector*)vector
{
// some preliminaries ...
double halfAngle = angle / 2.0;
double sinAngleOnTwo = sin(halfAngle); // do once
Vector *v = [vector normalise];
// calculate the quaternion ...
double w = cos(halfAngle);
double x = v.x * sinAngleOnTwo;
double y = v.y * sinAngleOnTwo;
double z = v.z * sinAngleOnTwo;
// return result ...
return [self initWithValues:w :x :y :z];
}
-(Quaternion*)copy
{
Quaternion *a = self;
return [[Quaternion alloc] initWithValues:a.w :a.x :a.y :a.z];
}
// ----- algebra
-(Quaternion*)conjugate
{
Quaternion *a = self;
return [[[Quaternion alloc] initWithValues:a.w :-a.x :-a.y :-a.z ] autorelease];
}
-(Quaternion*)plus:(Quaternion*)b
{
Quaternion *a = self;
return [[[Quaternion alloc] initWithValues:a.w+b.w :a.x+b.x :a.y+b.y :a.z+b.z] autorelease];
}
-(Quaternion*)minus:(Quaternion*)b
{
Quaternion *a = self;
return [[[Quaternion alloc] initWithValues:a.w-b.w :a.x-b.x :a.y-b.y :a.z-b.z] autorelease];
}
-(Quaternion*)times:(Quaternion*) b
{
Quaternion *a = self;
double real = a.w*b.w - a.x*b.x - a.y*b.y - a.z*b.z;
double i = a.w*b.x + a.x*b.w + a.y*b.z - a.z*b.y;
double j = a.w*b.y - a.x*b.z + a.y*b.w + a.z*b.x;
double k = a.w*b.z + a.x*b.y - a.y*b.x + a.z*b.w;
return [[[Quaternion alloc] initWithValues:real :i :j :k] autorelease];
}
-(double)norm
{
Quaternion *a = self;
return a.w*a.w + a.x*a.x + a.y*a.y + a.z*a.z;
}
-(Quaternion*)inverse
{
Quaternion *a = self;
double n = [a norm];
return [[[Quaternion alloc] initWithValues:a.w/n :-a.x/n :-a.y/n :-a.z/n] autorelease];
}
-(Quaternion*)divides:(Quaternion*) b
{
Quaternion *a = self;
return [[a inverse] times: b];
}
-(double)magnitude
{
Quaternion *a = self;
return sqrt([a norm]);
}
-(void)unitise
{
double m = [self magnitude];
self.w /= m;
self.x /= m;
self.y /= m;
self.z /= m;
}
-(Quaternion*)unitQuaternion
{
Quaternion *u = [[self copy] autorelease];
[u unitise];
return u;
}
-(Vector*)rotateVector:(Vector*)v
{
Quaternion *vAsPureQuat = [[Quaternion alloc] initWithValues:0.0 :v.x :v.y :v.z];
Quaternion *r = [[self times: vAsPureQuat] times:[self inverse]];
[vAsPureQuat release];
return [[[Vector alloc] initWithX:r.x andY:r.y andZ:r.z] autorelease];
}
// ----- misc
-(NSString*)toString
{
Quaternion *a = self;
return [NSString stringWithFormat:@"%f + %fi + %fj +%fk", a.w, a.x, a.y, a.z];
}
@end
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我发现了我的方法的错误...
它位于维护旋转参考四元数的代码中...我不应该将它乘以逆...在下面的代码片段中,上面的错误代码问题已被注释掉。
这些运动现在累积在 self.zReferenceQuaternion iVar 中。在屏幕上移动物体时不会出现类似万向节锁的情况。
I have discovered the error of my ways ...
It lay in the code that maintains the rotated reference quaternion ... I should not have been multiplying it by the inverse ... in the following code snippet, the buggy code from the above question has been commented out.
The movements are now accumulated in the self.zReferenceQuaternion iVar. No gimbal-lock like situation occurs on the screen moving the object around.