![]() |
![]() |
|
![]() |
||||||||||||||||||
![]() |
![]() |
![]() |
||||||||||||||||
|
![]() |
|
![]() |
|
||||||||||||||
![]() |
![]() |
3-D Рассеивание при помощи OpenGL / DirectX / Microsoft DirectX |
![]() |
3-D Рассеивание при помощи OpenGLThis article was contributed by William Heitler. Компилятор: Visual C++ 6.0 ОписаниеПример использует OpenGL для создания 3-D рассеивания, которое можно посмотреть под любым углом при помощи мышки. Данные так же можно просматривать в номальной проекции так и в перспективной. Данные и цвет каждой точки загружаются из текстового файла. При изменении размера окна приложения, график тоже пропорционально меняет размер. Присутствует возможность выделить определённый регион точек и изменить его масштаб. Проект представляет из себя диалоговое приложение MFC. В диалоге содержатся все необходимые для управления элементы, а так же наследованный от CWnd класс, который включает в себя все возможности для рисования графики. На рисунке, пользователь выделил голубые точки путём нажатия в секции кнопки Make. Если после этого нажать Zoom, то график будет перемасштабирован, чтобы показать только выбранную группу точек. Чтобы облегчить масштабирование по другим измерениям, можно воспользоваться опцией Autoscale. Графические классыclass COpenGLWnd : public CWnd Это универсальный класс, основанный на CWnd, в котором присутствует всё необходимое для отображения OpenGL графики. Основные, отличительные особенности этого класса:
class CGLMouseRotate : public COpenGLWnd Класс содержит код для кнопок и мышки, позволяющий вращать изображение. Флаг определяет - разрешено ли вращение мышкой или нет. class CGLScatterGraph : public CGLMouseRotate Этот класс содержит необходимые функции для рисования графика рассеивания. Данные передаются в класс при помощи
CGLScatterGraph::SetData(int count,COLORREF col,float *pCoords, COLORREF *pColList),
где count это количество точек в графике, col -
цвет, используемый для рисования точек (используется
только если pColList==NULL), pCoords - указатель на
массив, содержащий x, y и z координаты каждой точки
(соответственно длина массива = count*3), и pColList -
это либо NULL, либо указатель на массив, содержащий
цвета для каждой точки отдельно. CGLSelectableScatterGraph : public CGLScatterGraph Этот класс позволяет выделять точки в графике путём рисования кривой вокруг них. Вход в режим выделения осуществляется при помощи CGLSelectableScatterGraph::StartMakeSel(). При этом запрещается возможность вращения мышкой и разрешает обычное рисование мышкой. Класс содержит список CPoints который описывает нарисованную пользователем поверхность. Вызов функции CGLSelectableScatterGraph::CancelSel() убирает выделение и разрешает вращение мышкой. Выделение поверхности реализовано в функции CGLSelectableScatterGraph::ZoomSel(). Отображение экранной поверхности в точки сделано при помощи режима обратной связи в OpenGL. Приведённый ниже код, демонстрирует эту реализацию:
BOOL CGLSelectableScatterGraph::ZoomSel() { int i,j,count; int id=0; GLint rv; GLfloat token; GLfloat *pBuf; // m_SelPts это CArray указателей CPoints содержащий координаты // предыдущего ресунка, сделанного пользователем if (m_SelPts.GetSize()>2) { // делаем регион из selPts, требуется стандартный массив CPoint *pt=new CPoint[m_SelPts.GetSize()]; for (i=0; i<m_SelPts.GetSize(); i++) pt[i]=m_SelPts.GetAt(i); CRgn rgn; VERIFY(rgn.CreatePolygonRgn(pt, m_SelPts.GetSize(), ALTERNATE)); delete [] pt; // получаем список координат при помощи режима обратной связи OpenGL // Верного способа для вычисления необходимого кол-ва памяти // для буфера обратной связи не известно, однако эксперименты показали, // что для рисования осей необходимо 96 значений float, + 4 float на // точку даёт некоторый запас, вслучае неправильного рассчёта !! pBuf=new GLfloat[200+m_Count*4]; // m_Count = количество точек // в графе BeginGLCommands(); // разрешаем контекст OpenGL glFeedbackBuffer(200+m_Count*4,GL_3D,pBuf); glRenderMode(GL_FEEDBACK); EndGLCommands(); // заставляет окно перерисоваться, таким образом вызывая код рисования // GL и в режиме обратной связи помещает список оконных координат для // каждого перерисовываемого элемента в pBuf Invalidate(); UpdateWindow(); BeginGLCommands(); rv=glRenderMode(GL_RENDER); // восстанавливаем стандартный режим // рисования GL int *pInSel=new int[m_Count]; // для каждой точки будет 1 если // внутри и 0 если за пределами поверхности GLint viewport[4]; glGetIntegerv(GL_VIEWPORT,viewport); count=rv; while (count) { token=pBuf[rv-count]; count--; if (token==GL_POINT_TOKEN) // игнорируем код рисования осей { GLdouble coords[3]; for (j=0; j<3; j++) { coords[j]=pBuf[rv-count]; count--; } CPoint pt; pt.x=int(coords[0]); pt.y=int(viewport[3]-coords[1]-1); pInSel[id++]=rgn.PtInRegion(pt); } } EndGLCommands(); // теперь устанавливаем масштаб по осям для рисования выделенных точек // (плюс все остальные в пределах осей) m_bAutoScaleX=m_bAutoScaleY=m_bAutoScaleZ=FALSE; // выключаем // автомасштабирование // находим максимальные и минимальные значения точек в пределах // выделенной поверхности float xMax,xMin,yMax,yMin,zMax,zMin; xMax=yMax=zMax=-FLT_MAX; xMin=yMin=zMin=FLT_MAX; // drawCount это счётчик точек, которые были нарисованы в пределах, // старых осей и поэтому появились в pSelList int drawCount=-1; for (i=0; i<m_Count; i++) { // определяем - где была нарисована точка (в старых ли осях) // и поэтому была помещена в список выделения (если в пределах, то 1 // либо 0 если за пределами) if (!PtWithinAxes(m_pDat[i*3],m_pDat[i*3+1],m_pDat[i*3+2])) continue; // игнорируем точки за пределами осей drawCount++; if (pInSel[drawCount]==0) // игнорируем точку, так как она за // пределами выделения continue; xMax=max(m_pDat[i*3],xMax); xMin=min(m_pDat[i*3],xMin); yMax=max(m_pDat[i*3+1],yMax); yMin=min(m_pDat[i*3+1],yMin); zMax=max(m_pDat[i*3+2],zMax); zMin=min(m_pDat[i*3+2],zMin); } // подгоняем масштаб по осям m_MaxX=NextAbove(xMax,5); m_MinX=NextBelow(xMin,5); m_MaxY=NextAbove(yMax,5); m_MinY=NextBelow(yMin,5); m_MaxZ=NextAbove(zMax,5); m_MinZ=NextBelow(zMin,5); Invalidate(); delete [] pBuf; delete [] pInSel; rgn.DeleteObject(); } m_bAllowMouseRotate=m_bOldAllowRotate; // восстанавливаем способность // вращения мышкой CancelSel(); // удаляем все точки из m_SelPts, очищаем выделение return TRUE; } Формат данныхДанные можно загружать из основной программы, используя кнопку Load, либо перетаскивая файл на основное окно. Программа умеет считывать файлы трёх форматов. Во всех форматах каждая точка занимает
отдельную строку в текстовом файле. Файлы test1.txt, test2.txt и test3.txt иллюстрирую все три формата. DownloadsСкачать демонстрационный проект - 59 Kb
|
![]() |
![]() |
![]() |
|