Итак, мы собираемся управлять ориентацией изображения с помощью левой кнопки мыши. Перемещение курсора мыши при нажатой кнопке должно вращать изображение наиболее естественным образом, то есть горизонтальное перемещение должно происходить вокруг вертикальной оси Y, а вертикальное — вокруг горизонтальной оси X. Если одновременно с мышью нажата клавиша Ctrl, то мы будем перемещать (транслировать) изображение вдоль осей X и Y. С помощью правой кнопки будем перемещать изображение вдоль оси Z. Кроме того, с помощью левой кнопки мыши мы дадим возможность придать вращению постоянный характер. Для этого в обработчик WM_LBUTTONUP введем анализ на превышение квантом перемещения (m_dx, m_dy) некоторого порога чувствительности. Если он превышен, то мы запустим таймер, и дальнейшее вращение будем производить с его помощью. Если очередной квант перемещения ниже порога чувствительности, то мы остановим таймер, прекращая вращение. В обработке WM_MOUSEMOVE следует оценивать желаемую скорость вращения, которая является векторной величиной из двух компонентов и должна быть пропорциональна разности двух последовательных координат курсора. Такой алгоритм обеспечивает гибкое и довольно естественное управление ориентацией объекта. Начнем с обработки нажатия левой кнопки. Оно, очевидно, должно всегда останавливать таймер, запоминать факт нажатия кнопки и текущие координаты курсора мыши:
void COGView: :OnLButtonDown (UINT nFlags, CPoint point)
{
//====== Останавливаем таймер
KillTimer(1);
//====== Обнуляем кванты перемещения
m_dx = 0.f; m_dy = 0.f;
//====== Захватываем сообщения мыши,
//====== направляя их в свое окно
SetCapture ();
//====== Запоминаем факт захвата
m_bCaptured = true;
//====== Запоминаем координаты курсора
m_pt = point;
}
При нажатии на правую кнопку необходимо выполнить те же действия, что и при нажатии на левую, но дополнительно надо запомнить сам факт нажатия правой кнопки, с тем чтобы правильно интерпретировать последующие сообщения о перемещении указателя мыши и вместо вращения производить сдвиг вдоль оси Z:
void COGView::OnRButtonDown(UINT nFlags, CPoint point)
{
//====== Запоминаем факт нажатия правой кнопки
m_bRightButton = true;
//====== Воспроизводим реакцию на левую кнопку
OnLButtonDown(nFlags, point);
}
В обработчик отпускания левой кнопки мы вводим анализ на необходимость продолжения вращения с помощью таймера. В случае превышения порога чувствительности, запускаем таймер, сообщения от которого будут говорить, что надо продолжать вращение, поддерживая текущее значение скорости:
void COGView::OnLButtonUp(UINT nFlags, CPoint point)
{
//====== Если был захват,
if (m_bCaptured)
//=== то анализируем желаемый квант перемещения
//=== на превышение порога чувствительности
if (fabs(m_dx) > 0.5f || fabs(m_dy) > 0.5f)
//=== Включаем режим постоянного вращения
SetTimer(1,33,0);
else
//=== Выключаем режим постоянного вращения
KillTimer(1);
//====== Снимаем флаг захвата мыши
m_bCaptured = false;
//====== Отпускаем сообщения мыши
ReleaseCapture();
}
}
Отпускание правой кнопки должно просто отмечать факт прекращения перемещения вдоль оси Z и отпускать сообщения мыши для того, чтобы они работали на другие окна, в том числе и на наше окно-рамку. Если этого не сделать, то станет невозможным использование меню главного окна. Проверьте, если хотите. Для этого достаточно закомментировать вызов функции ReleaseCapture в обеих функциях:
void COGView::OnRButtonUp(UINT nFlags, CPoint point)
{
//====== Правая кнопка отпущена
m_bRightButton = false;
//====== Снимаем флаг захвата мыши
m_bCaptured = false;
//====== Отпускаем сообщения мыши
ReleaseCapture();
}
Теперь реализуем самую сложную часть алгоритма — реакцию на перемещение курсора. Здесь мы должны оценить желаемую скорость вращения. Она зависит от того, насколько резко пользователь подвинул объект, то есть оценить модуль разности двух последних позиций курсора, В этой же функции надо выделить случай одновременного нажатия служебной клавиши Ctrl Если она нажата, то интерпретация движения мыши при нажатой левой кнопке изменяется. Теперь вместо вращения мы должны сдвигать объект, то есть пропорционально изменять переменные m_xTrans и m_yTrans, которые затем подаются на вход функции glTranslate. Третья ветвь алгоритма обрабатывает движение указателя при нажатой правой кнопке. Здесь необходимо изменять значение переменной m_zTrans, обеспечивая сдвиг объекта вдоль оси Z. Числовые коэффициенты пропорциональности, которые вы видите в коде функции, влияют на чувствительность мыши и подбираются экспериментально. Вы можете изменить их на свой вкус так, чтобы добиться желаемой управляемости изображения:
void COGView::OnMouseMove(UINT nFlags, CPoint point)
{
if (m_bCaptured) // Если был захват,
{
// Вычисляем компоненты желаемой скорости вращения
m_dy = float (point .у - m_pt .у) /40 . f ;
m_dx = float (point .x - m_pt .x) /40. f ;
//====== Если одновременно была нажата Ctrl,
if (nFlags & MK_CONTROL)
{
//=== Изменяем коэффициенты сдвига изображения
m_xTrans += m_dx;
m_yTrans -= m_dy;
}
else
{
//====== Если была нажата правая кнопка
if (m_bRightButton)
//====== Усредняем величину сдвига
m_zTrans += (m_dx + m_dy)/2.f;
else
{
//====== Иначе, изменяем углы поворота
m_AngleX += m_dy;
m_AngleY += m_dx;
}
}
//=== В любом случае запоминаем новое положение мыши
m_pt = point; Invalidate (FALSE) ;
}
}
Запустите и проверьте управляемость объекта. Введите коррективы чувствительности на свой вкус. Попробуйте скорректировать эффект влияния поворота вокруг оси X на интерпретацию знака желаемого вращения вокруг оси Y. Здесь можно воспользоваться стеком матриц моделирования. Теперь добавим код в заготовку функции реакции на сообщения таймера с тем, чтобы ввести фиксацию состояния вращения.