Первоначально было реализовано на 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)
Данная программа работает существенно быстрее, чем вариант с использованием matplotlib и отсутствуют проблемы с неправильным перекрытием столбцов
Есть желание повысить производительность. В связи с этим возникает несколько вопросов:
может быть есть другие способы отрисовки параллелограммов?
может быть можно как-то по другому реализовать вращение диаграммы?
данный пример нагружает видеокарту слабо, в сравнении с теми же вращающимися шестерёнками из примеров, возможно необходимо как-то дополнительно инициализировать?
или же может быть существуют другие способы решить существующую задачу: необходимо нарисовать кучу столбиков (контроль всех трёх координат, тот же cube из стандартной библиотеки рисуется прямо в центре) и реализовать вращение получившийся диаграммы для удобного просмотра?