Иллюстрированный самоучитель по Visual Studio .NET

Работа с контейнером

Для работы с файлом мы пользовались буфером переменных типа BYTE. Для работы с данными в памяти значительно более удобной структурой данных является динамический контейнер. Мы, как вы помните, выбрали для этой цели контейнер, скроенный по шаблону vector. При заказе на его изготовление указали тип данных для хранения в контейнере. Это объекты класса CPointSD (точки трехмерного пространства). Мы пошли по простому пути и храним в файле только один компонент Y из трех координат точек поверхности в 3D. Остальные две координаты (узлов сетки на плоскости X-Z) будем генерировать на регулярной основе.

Такой подход оправдан тем, что изображение OpenGL все равно претерпевает нормирующие преобразования, перед тем как попасть на двухмерный экран. Создание контейнера точек производится в теле функции SetGraphPoints, к разработке которой сейчас и приступим.

На вход функции подается временный буфер (и его размер), в который попали данные из файла. В настоящий момент в буфере находятся данные тестовой поверхности, а потом, при вызове из функции ReadData, в него действительно попадут данные из файла. Выбор данных из буфера происходит аналогично их записи. Здесь мы пользуемся адресной арифметикой, определяемой типом указателя. Так, операция ++ в применении к указателю типа UINT сдвигает его в памяти на sizeof (UINT) байт. Смена типа указателя (на float*) происходит в тот момент, когда выбраны данные о размерах сетки узлов.

Для надежности сначала проверяем данные из буфера на внутреннюю непротиворечивость в смысле размерностей. Затем мы уничтожаем данные контейнера и генерируем новые на основе содержимого буфера. В процессе генерации трехмерных координат точек их ординаты (Y) масштабируются для того, чтобы график имел пропорции, удобные для просмотра:

void COGView::SetGraphPoints(BYTE* buff, DWORD nSize)
{
//====== Готовимся к расшифровке данных буфера
//====== Указываем на него указателем целого типа
UINT *p = (UINT*)buff;
//=== Выбираем данные целого типа, сдвигая указатель
m_xSize = *р; m_zSize = *++p;
//====== Проверка на непротиворечивость
if (m_xSize<2 || m_zSize<2 ||
m_xSize*m_zSize*sizeof(float)
+ 2 * sizeof(UINT)!= nSize)
{
MessageBox (_T ("Данные противоречивы"));
return;
}
//====== Изменяем размер контейнера
//====== При этом его данные разрушаются
m_cPoints .resize (m_xSize*m_zSize);
if (m_cPoints.empty ())
{
MessageBox (_T ("Не возможно разместить данные")
return;
}
//====== Подготовка к циклу пробега по буферу
//====== и процессу масштабирования
float x, z,
//====== Считываем первую ординату
*pf = (float*) ++р,
fMinY = *pf,
fMaxY = *pf,
right = (m_xSize-l) /2 .f,
left = – right,
read = (m_zSize-l) /2 .f,
front = – rear,
range = (right + rear) /2 .f;
UINTi, j, n;
//====== Вычисление размаха изображаемого объекта
m_fRangeY = range;
m_fRangeX = float (m_xSize);
m_fRangeZ = float (m_zSize);
//====== Величина сдвига вдоль оси Z
m_zTrans = – 1.5f * m_fRangeZ;
//====== Генерируем координаты сетки (X-Z)
//====== и совмещаем с ординатами Y из буфера
for (z=front, i=0, n=0; i<m_zSize; i++, z+=l.f)
{
for (x=left, j=0; j<m_xSize; j++, x+=l.f, n++)
{
MinMax (*pf, fMinY, fMaxY);
m_cPoints[n] = CPoint3D(x, z, *pf++);
}
}
//====== Масштабирование ординат
float zoom = fMaxY > fMinY? range/ (fMaxY-fMinY)
: l.f;
for (n=0; n<m_xSize*m_zSize;n++)
{
m_cPoints [n]. у = zoom * (m_cPoints [n]. у – fMinY) – range/2 .f;
}
}

При изменении размеров контейнера методом (resize) все его данные разрушаются. В двойном цикле пробега по узлам сетки мы восстанавливаем (генерируем заново) координаты X и Z всех вершин четырехугольников. В отдельном цикле пробега по всему контейнеру происходит масштабирование ординат (умножение на предварительно вычисленный коэффициент zoom). В используемом алгоритме необходимо искать экстремумы функции у = f (x, z). С этой целью удобно иметь глобальную функцию MinMax, которая корректирует значение минимума или максимума, если входной параметр превышает существующие на сей момент экстремумы. Введите тело этой функции в начало файла реализации оконного класса (ChildView.cpp):

inline void MinMax (float d, floats Min, float& Max)
{
//====== Корректируем переданные по ссылке параметры
if (d > Max)
Max = d; // Претендент на максимум
else if (d < Min)
Min = d; // Претендент на минимум
}
Если Вы заметили ошибку, выделите, пожалуйста, необходимый текст и нажмите CTRL + Enter, чтобы сообщить об этом редактору.