需要在WhirlyGlobe的3D地图上来实现一个指南针的功能,初步考虑需要以下几个步骤:监测地图位置,改变的时候触发事件——旋转指南针的图片到对应的角度。
找到地图的缩放,拖动,旋转的方法后,发现所有涉及角度变换的地方都是改变了Eigen::Quaternionf rotQuat的值。下面介绍下Quaternion:
Quaternion 的定义
四元数一般定义如下:
q=w+xi+yj+zk
其中 w,x,y,z是实数。同时有:
i*i=-1
j*j=-1
k*k=-1
四元数也可以表示为:
q=[w,v]
其中v=(x,y,z)是矢量,w是标量,虽然v是矢量,但不能简单的理解为3D空间的矢量,它是4维空间中的的矢量,也是非常不容易想像的。
通俗的讲,一个四元数(Quaternion)描述了一个旋转轴和一个旋转角度。这个旋转轴和这个角度可以通过 Quaternion::ToAngleAxis转换得到。当然也可以随意指定一个角度一个旋转轴来构造一个Quaternion。这个角度是相对于单位四元数而言的,也可以说是相对于物体的初始方向而言的。
当用一个四元数乘以一个向量时,实际上就是让该向量围绕着这个四元数所描述的旋转轴,转动这个四元数所描述的角度而得到的向量。
(摘自http://www.linuxgraphics.cn/opengl/opengl_quaternion.html)
简单来说Quaternion是用来表示旋转的,地图所存储的Quaternion变量表示了从初始位置到当前位置的旋转变量,这样我们可以通过计算北极点(0,0,1)经过旋转之后位置:
Eigen::Vector3f northPole = (rot * Eigen::Vector3f(0,0,1)).normalized();
P.S. Eigen是一个非常著名的C++数学库;
然后就可以算出所需要的角度了:
float angle = atan2(northPole.x(), northPole.y());//atan2为math.h下的C函数
UIVIEW有一个transform属性来负责控制变型的,我们可以创建一个旋转变换来旋转图片,每次旋转当前的角度和上次的差值。
CGFloat rotation = 0.0 -(lastCompassAngle-angle); //lastCompassAngle存储了上次的角度
CGAffineTransform currentTransform = compass.transform;CGAffineTransform newTransform = CGAffineTransformRotate(currentTransform,rotation);
[compass setTransform:newTransform];lastCompassAngle = angle;
OK大功告成!!
参考文章:
http://www.cppblog.com/heath/archive/2009/12/13/103127.html
http://www.linuxgraphics.cn/opengl/opengl_quaternion.html