0 变量说明
int origin_x, origin_y; //鼠标初始坐标
int m_x, m_y; //锚点坐标
int u,v;//鼠标点击坐标
int m_width, m_height; //屏幕长宽
float m_r; //!< min(m_width, m_height) //地球半径
float m_scale; //当前缩放比例
int delta;//滚轮方向,放大为正,缩小为负
QQuaternion R; //!< Rotation 全局旋转四元数
QQuaternion incR; //!< Increase Rotation 旋转增量四元数
QVector3D T; //!< Translation 全局平移向量
GLdouble M[16]; //四元数生成的旋转矩阵
其中
delta和m_scale控制缩放
u,v和m_x,m_y控制平移(即计算出T的增量)
u,v和origin_x,origin_y控制旋转
(即计算出R的增量incR)
(PS:因为矩阵的复合是乘的关系,所以最后incR对R计算贡献时,用的是R=incR*R)
1 scale
每一次滚轮转动,都会触发一次该函数,通过m_scale变量控制缩放比例。
m_scale = 1.0;
void CGLTrackball::wheel(int delta) { //每次缩放5%
if (delta > 0)
m_scale *= 1.05f;
else
m_scale *= 0.95f;
}
即只要滚轮转动的时候,鼠标位于观察区域(if (u<0 || u>m_width || v<0 || v>m_height) return;),就生成顺序执行的语句
Scale-Zoom(1.05/0.95) & Scale-Time(0.1); //0.1s为滚轮转动一下的时间
2 translate
每一次鼠标右键点击屏幕,都会触发一次该函数,通过QVector3D T变量控制平移的具体细节。
T = QVector3D(0.0, 0.0, 0.0);
void CGLTrackball::motion_translate(int u, int v) { //(u,v)为鼠标点击坐标
v = m_height - v;//将y轴颠倒过来 符合人的视觉
// a straight forward scheme
//计算在屏幕上移动的三维向量 z轴的偏移量为0
QVector3D screenMovementVector = QVector3D((float)(u - m_x)/(float)m_width, (float)(v - m_y)/(float)m_height, 0.0);
T += screenMovementVector;
}
即只要鼠标右键点击屏幕的时候,鼠标位于观察区域,就生成顺序执行的语句
Translation-Direction((float)(u - m_x)/(float)m_width, (float)(v - m_y)/(float)m_height, 0.0) & Translation-Time(1); //1s为默认移动的时间(可由用户在DSL编辑界面修改)
3 rotate
每一次鼠标左键按下后在屏幕上移动,都会触发一次该函数,通过QQuaternion R、incR变量控制旋转的具体细节。
void CGLTrackball::motion_rotate(int u, int v) { //u v为鼠标点击的位置
QVector3D N;
QVector3D v0(0, 0, 0), v1(0, 0, 0);
float theta;
v = m_height - v; //翻转y轴
if (u<0 || u>m_width || v<0 || v>m_height) return;
#if 0
v0.setX(m_x - m_width*0.5f);
v0.setY(m_y - m_height*0.5f);
#else
v0.setX(m_x - origin_x);
v0.setY(m_y - origin_y);
#endif
//第三个偏移维度开始先视为0(因为鼠标点击的是二维平面,而地球展示的三维平面)
v0.setZ(0.f);
if (v0.length() < 1e-2) { //几乎没有移动
m_x=u;
m_y=v;
return;
}
v0.setZ(m_r*m_r/2.f / v0.length()); //通过偏移矢量,计算z轴上实际偏移的距离
#if 0
//刚开始的时候,默认第一次点击的位置是屏幕中心
v1.setX(u - m_width*0.5f);
v1.setY(v - m_height*0.5f);
#else
//记录两次鼠标偏移的向量
v1.setX(u - origin_x);
v1.setY(v - origin_y);
#endif
v1.setZ(0.f);
if (v1.length() < 2) return;
v1.setZ(m_r*m_r/2.f / v1.length());
v0.normalize();//单位化
v1.normalize();//单位化
N = QVector3D::crossProduct(v0, v1);
//将两个向量做叉乘,再经过单位化,则可得到旋转轴的单位向量
N.normalize();
float dotProduct = QVector3D::dotProduct(v0, v1); //计算点乘,根据结果的符号则可以获得旋转的方向
if(dotProduct > 1.0)
dotProduct = 1.0;
if(dotProduct < -1.0)
dotProduct = -1.0;
theta = acos(dotProduct); //获得旋转的角度 单位为弧度
incR = QQuaternion(cos(theta/2.f), N*sin(theta/2.f)); //得到旋转的向量增加比例对应的四元数
R = incR*R; //得到新的旋转四元数 此时四元数的四个维度就是glrotate需要的四个参数
m_Quaternion2Matrix(); //根据四元数 生成旋转矩阵
m_x = u, m_y = v;
}
即只要鼠标左键点击屏幕,拖动后又松开的时候,鼠标位于观察区域,就生成顺序执行的语句
//首先按照函数里的顺序,计算出R。
Rotation-Axis(incR.x, incR.y, incR.z) & Rotation-Angle(incR.w*57.3) & Rotation-Time(1);//1s为默认移动的时间(可由用户在DSL编辑界面修改)
//1弧度约等于57.3度
网友评论