Форум сайта python.su
Есть необходимость нарисовать трёхмерную столбчатую диаграмму.
Первоначально было реализовано на matplotlib с использованием модулей преобразующих плоские графики в трёхмерные. У данного метода есть существенные недостатки - очень медленная отрисовка(порядка 1-2 на один поворот), а также просмотр возможен только с определённого диапазона углов, при выходе угла обзора за границы этого диапазона происходит существенное искажение графиков - похоже на неправильное наложение слоёв - происходит перекрытие информации.
Поэтому было принято решение попробовать реализовать данный функционал на базе OpenGL.
Оказалось, что OpenGL не умеет рисовать параллелограммы - только кубы.
На основе примеров pyOpenGl и информации в интернете о рисовании примитивов в opengl была написана
# -*- coding: utf-8 -*-
'''
Created on 21.10.2009
@author: Sinopteek
'''
# This is statement is required by the build system to query build info
if __name__ == '__build__':
raise Exception
import OpenGL.GL
OpenGL.FORWARD_COMPATIBLE_ONLY = True
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLE import *
from OpenGL.GLU import *
#import maintest
import random
lastx=0
lasty=0
# get notified of mouse motions
def MouseMotion (x, y):
global lastx, lasty
lastx = x
lasty = y
glutPostRedisplay ()
def JoinStyle (msg):
sys.exit(0)
def draw_by_opengl(DrawStuff,x_range=0,y_range=0,heigth = None):
if x_range == 0 or y_range == 0 or not heigth:
print "exit"
exit()
global glutDisplayFunc, glutMotionFunc
# initialize glut
glutInit(sys.argv)
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH)
glutInitWindowSize(640,480) #размер окна
glutCreateWindow("basic demo")
glutDisplayFunc(DrawStuff)
glutMotionFunc(MouseMotion)
# create popup menu */
glutCreateMenu (JoinStyle)
glutAddMenuEntry ("Exit", 99)
glutAttachMenu (GLUT_RIGHT_BUTTON)
# initialize GL */
glClearDepth (1.0)
glEnable (GL_DEPTH_TEST)
glClearColor (0.0, 0.0, 0.0, 0.0)
glShadeModel (GL_SMOOTH)
glMatrixMode (GL_PROJECTION)
# roughly, measured in centimeters */
glFrustum (-25.0, 25.0, -5.0, 5.0, 50.0, 1500.0)
glMatrixMode(GL_MODELVIEW)
glutMainLoop ()
def DrawBox(length=1.,width=1.,heigth=1.):
#рисует коробку по центру с заданными размерами
glBegin(GL_QUADS)
#// левая грань
glVertex3f( -length / 2, -heigth / 2, -width / 2)
glVertex3f( -length / 2, heigth / 2, -width / 2)
glVertex3f( -length / 2, heigth / 2, width / 2)
glVertex3f( -length / 2, -heigth / 2, width / 2)
#// правая грань
glVertex3f( length / 2, -heigth / 2, -width / 2)
glVertex3f( length / 2, -heigth / 2, width / 2)
glVertex3f( length / 2, heigth / 2, width / 2)
glVertex3f( length / 2, heigth / 2, -width / 2)
#// нижняя грань
glVertex3f( -length / 2, -heigth / 2, -width / 2)
glVertex3f( -length / 2, -heigth / 2, width / 2)
glVertex3f( length / 2, -heigth / 2, width / 2)
glVertex3f( length / 2, -heigth / 2, -width / 2)
#// верхняя грань
glVertex3f( -length / 2, heigth / 2, -width / 2)
glVertex3f( -length / 2, heigth / 2, width / 2)
glVertex3f( length / 2, heigth / 2, width / 2)
glVertex3f( length / 2, heigth / 2, -width / 2)
#// задняя грань
glVertex3f( -length / 2, -heigth / 2, -width / 2)
glVertex3f( length / 2, -heigth / 2, -width / 2)
glVertex3f( length / 2, heigth / 2, -width / 2)
glVertex3f( -length / 2, heigth / 2, -width / 2)
#// передняя грань
glVertex3f( -length / 2, -heigth / 2, width / 2)
glVertex3f( length / 2, -heigth / 2, width / 2)
glVertex3f( length / 2, heigth / 2, width / 2)
glVertex3f( -length / 2, heigth / 2, width / 2)
glEnd()
# draw the polycone shape
def DrawStuff():
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
# set up some matrices so that the object spins with the mouse
glPushMatrix () #переходим к новым координатам
glTranslatef (0, -10.0, -200.0) #положение камеры
glRotatef (10, 1.0, 0.0, 0.0) #угол наклона
#поворачиваем картину в зависимости от координат курсора мыши
glRotatef (lastx, 0.0, 1.0, 0.0)
glRotatef (lasty, 1.0, 0.0, 0.0)
# glLineWidth(5); #толщина линий 5
glColor3f(0.8,0.0,0.0); #красненький
#смещаем начальную точку отрисовки граффика так, чтобы центр граффика совпал с осью вращения
glTranslatef (-x_range/2.0, 0.0, -y_range/2.0)
#рисуем столбики
for x in range(x_range):
for y in range(y_range):
glTranslatef (0.0, heigth[x][y]/2, 0.0); #смещаем в центр параллелипипида
DrawBox(length=0.2,width=0.2,heigth=heigth[x][y]) #рисуем параллелипипид
glTranslatef (0.0, -heigth[x][y]/2, 0.0); #смещаем обратно на ось нуля
glTranslatef (0.0, 0.0, 1.0);
glTranslatef (1.0, 0.0, -1.0*y_range);
glTranslatef (-1.0*x_range, 0.0, 0.0);
#glTranslatef (-x_range/2.0, 0.0, 0.0)
glPopMatrix () #возыращаемся к старым координатам
glutSwapBuffers () #помечаем кадр как готовый к отрисовке
x_range = 158
y_range = 43
#подгатавливаем данные для граффика
heigth = []
for x in range(x_range):
heigth_str = []
for y in range(y_range):
heigth_str.append(random.random())
heigth.append(heigth_str)
draw_by_opengl(DrawStuff,heigth=heigth, x_range=x_range, y_range=y_range)
Отредактировано (Фев. 26, 2010 17:14:25)
Офлайн
http://www.vpython.org простенькая библиотека для рисования 3д, для твоих нужд должно хватить.
Кстати, опенгл умеет рисовать паралеллепипеды, для этого надо отмасштабировать куб по определенным осям.
Отредактировано (Фев. 26, 2010 16:04:35)
Офлайн
pasaranaxБегло смотрел, примеров мало. Насколько помню не нашол реализацию вращения мышью.
http://www.vpython.org простенькая библиотека для рисования 3д, для твоих нужд должно хватить.
pasaranaxили я пока не понял как сделать для единичного, или маштабирование происходит для всей картинки сразу, т.е. если я нарисую несколько кубов и применю маштабирование, то у меня вытянутся все кубы сразу.
Кстати, опенгл умеет рисовать паралеллепипеды, для этого надо отмасштабировать куб по определенным осям.
Офлайн
Во первых - GL_QUAD рисует четырехугольники, а не кубы. Т.е. указывайте любые координаты - и все будет в порядке.
Второе: есть две матрицы: GL_MODELVIEW и GL_PROJECTION
1. Поверните GL_PROJECTION так, как вам нужно - с фруструмом и прочими штучками.
2. Установите GL_MODELVIEW в единичную и отрисуйте свой график.
Чтобы шаг 2 был побыстрее - можно сделать display list. Техника морально устаревшая, но надежная. Если ничего поменялось - вызываем glCallList без его перегенерации.
Офлайн
Андрей Светловя знаю, там даже комментарии в коде есть. Правда я не понял как это мне должно помочь
Во первых - GL_QUAD рисует четырехугольники, а не кубы. Т.е. указывайте любые координаты - и все будет в порядке.
Андрей СветловВ процессе, правда пока без списков
Второе: есть две матрицы: GL_MODELVIEW и GL_PROJECTION
1. Поверните GL_PROJECTION так, как вам нужно - с фруструмом и прочими штучками.
2. Установите GL_MODELVIEW в единичную и отрисуйте свой график.
Чтобы шаг 2 был побыстрее - можно сделать display list. Техника морально устаревшая, но надежная. Если ничего поменялось - вызываем glCallList без его перегенерации.
def box(posx,posy,poz,sizex,sizey,sizez):
glPushMatrix() #сохраняем текущие координаты
glLoadIdentity () # clear the matrix
y=random.random()
glColor3f (random.random(), random.random(), random.random())
glTranslatef (posx,posy,poz)
#положение камеры, наведение в точку, установка вектора вверх(поворот камеры)
gluLookAt (0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0)
if not sizex == sizey == sizez:
glScalef (sizex,sizey,sizez) #модельная трансформация
glutWireCube (1.0)
else:
glutWireCube (sizex)
glPopMatrix() #возвращаем предыдущие координаты
Отредактировано (Март 1, 2010 14:23:25)
Офлайн
gluLookAt обычно ставится только один раз, в начале рисования схемы. Это часть задания матрицы GL_PROJECTION. Когда рисуете ящик - работаете только с GL_MODELVIEW.
Технически матрицы всего две. Для преобразования точки в трехмерном пространстве на экранные координаты используется их перемножение. Если объект имеет абсолютные координаты (чайник) - пляшем от единичной матрицы, сдвигая/поворачивая ее куда нужно. Если будем рисовать крышку от чайника вместе с самим - то берем матрицу для чайника и сдвигаем ее так, чтобы получили нулевую точку для крышки.
Офлайн
Андрей СветловЕсли закомментировать строку в функции рисования коробки, написанную мной, то он просто не отрисовывается - сейчас перепроверил специально.
gluLookAt обычно ставится только один раз, в начале рисования схемы. Это часть задания матрицы GL_PROJECTION. Когда рисуете ящик - работаете только с GL_MODELVIEW.
Технически матрицы всего две. Для преобразования точки в трехмерном пространстве на экранные координаты используется их перемножение. Если объект имеет абсолютные координаты (чайник) - пляшем от единичной матрицы, сдвигая/поворачивая ее куда нужно. Если будем рисовать крышку от чайника вместе с самим - то берем матрицу для чайника и сдвигаем ее так, чтобы получили нулевую точку для крышки.
import random
try:
from OpenGL.GLUT import *
from OpenGL.GL import *
from OpenGL.GLU import *
except:
print '''
ERROR: PyOpenGL not installed properly.
'''
def init():
glClearColor (0.0, 0.0, 0.0, 0.0) #фоновый цвет(используется при очищении)
glShadeModel (GL_FLAT)
def box(posx,posy,poz,sizex,sizey,sizez):
glPushMatrix() #сохраняем текущие координаты
glLoadIdentity () #очищаем матрицу
glColor3f (random.random(), random.random(), random.random())
glTranslatef (posx,posy,poz)
#положение камеры, наведение в точку, установка вектора вверх(поворот камеры)
gluLookAt (0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0)
if not sizex == sizey == sizez:
glScalef (sizex,sizey,sizez) #модельная трансформация
glutWireCube (1.0)
else:
glutWireCube (sizex)
glPopMatrix() #сохраняем текущие координаты
#если раскомментировать получается забавно(рисуется каждая коробка)
#работает только с прямым буфер(отключённой двойной буферизацией)
#glFlush () #начинаем рисовать немедленно
def display():
glClear (GL_COLOR_BUFFER_BIT) #очищаем буфер, в данном случае цветовой
glColor3f (1.0, 1.0, 1.0) #устанавливаем цвет(белый)
glLoadIdentity () # clear the matrix
# viewing transformation
gluLookAt (0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0)
glScalef (1.0, 2.0, 0.5) #модельная трансформация
glutWireCube (1.0)
for i in range(10000):
box(posx=random.random()-0.5,posy=random.random()-0.5,poz=random.random()-0.5,
sizex=random.random(),sizey=random.random(),sizez=random.random())
#можно использовать одну из двух в зависимости установок инициализации
if double_buffer:
glutSwapBuffers () #помечаем кадр как готовый к отрисовке
else:
glFlush () #начинаем рисовать немедленно
def reshape (w, h):
glViewport (0, 0, w, h) #выбираем всю область окна для рисования
glMatrixMode (GL_PROJECTION) #выбираем матрицу проекций
glLoadIdentity () #инициализируем матрицу перед использованием
#задаём плоскости отсечения
glFrustum (-1.0, 1.0, -1.0, 1.0, 1.5, 20.0)
glMatrixMode (GL_MODELVIEW) #выбираем видовую матрицу
glutInit(sys.argv)
#включить режим двойной буфферизации
double_buffer = False
#используется одна из двух моделей отображения
if double_buffer:
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH) #двойная буфферизация
else:
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB)
glutInitWindowSize (500, 500) #размер окна
glutInitWindowPosition (200, 100) #положение окна
glutCreateWindow (u'cubes тест') #создаём окно с именем
init ()
glutDisplayFunc(display) #задаём функцию которой будем рисовать содержимое окна
glutReshapeFunc(reshape) #функция для перерисовки окна
glutMainLoop() #запускаем рисование
Офлайн
делайте gluLookAt сразу после glFrustrum
Офлайн
А не подскажите диаграммы в matplotlib можно строить?
Офлайн
Откройте новую тему
Офлайн