OpenGL scenegraph updates
- volumetric rendering - isosurfaces, mesh rendering - basic transformation and parent/child functionality
This commit is contained in:
parent
269374ef84
commit
920fd9333e
67
examples/GLMeshItem.py
Normal file
67
examples/GLMeshItem.py
Normal file
@ -0,0 +1,67 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
## This example uses the isosurface function to convert a scalar field
|
||||
## (a hydrogen orbital) into a mesh for 3D display.
|
||||
|
||||
## Add path to library (just for examples; you do not need this)
|
||||
import sys, os
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||
|
||||
from pyqtgraph.Qt import QtCore, QtGui
|
||||
import pyqtgraph as pg
|
||||
import pyqtgraph.opengl as gl
|
||||
|
||||
app = QtGui.QApplication([])
|
||||
w = gl.GLViewWidget()
|
||||
w.show()
|
||||
|
||||
g = gl.GLGridItem()
|
||||
g.scale(2,2,1)
|
||||
w.addItem(g)
|
||||
|
||||
import numpy as np
|
||||
|
||||
def psi(i, j, k, offset=(25, 25, 50)):
|
||||
x = i-offset[0]
|
||||
y = j-offset[1]
|
||||
z = k-offset[2]
|
||||
th = np.arctan2(z, (x**2+y**2)**0.5)
|
||||
phi = np.arctan2(y, x)
|
||||
r = (x**2 + y**2 + z **2)**0.5
|
||||
a0 = 1
|
||||
#ps = (1./81.) * (2./np.pi)**0.5 * (1./a0)**(3/2) * (6 - r/a0) * (r/a0) * np.exp(-r/(3*a0)) * np.cos(th)
|
||||
ps = (1./81.) * 1./(6.*np.pi)**0.5 * (1./a0)**(3/2) * (r/a0)**2 * np.exp(-r/(3*a0)) * (3 * np.cos(th)**2 - 1)
|
||||
|
||||
return ps
|
||||
|
||||
#return ((1./81.) * (1./np.pi)**0.5 * (1./a0)**(3/2) * (r/a0)**2 * (r/a0) * np.exp(-r/(3*a0)) * np.sin(th) * np.cos(th) * np.exp(2 * 1j * phi))**2
|
||||
|
||||
|
||||
print "Generating scalar field.."
|
||||
data = np.abs(np.fromfunction(psi, (50,50,100)))
|
||||
|
||||
|
||||
#data = np.fromfunction(lambda i,j,k: np.sin(0.2*((i-25)**2+(j-15)**2+k**2)**0.5), (50,50,50));
|
||||
print "Generating isosurface.."
|
||||
faces = pg.isosurface(data, data.max()/4.)
|
||||
m = gl.GLMeshItem(faces)
|
||||
w.addItem(m)
|
||||
m.translate(-25, -25, -50)
|
||||
|
||||
|
||||
|
||||
#data = np.zeros((5,5,5))
|
||||
#data[2,2,1:4] = 1
|
||||
#data[2,1:4,2] = 1
|
||||
#data[1:4,2,2] = 1
|
||||
#tr.translate(-2.5, -2.5, 0)
|
||||
#data = np.ones((2,2,2))
|
||||
#data[0, 1, 0] = 0
|
||||
#faces = pg.isosurface(data, 0.5)
|
||||
#m = gl.GLMeshItem(faces)
|
||||
#w.addItem(m)
|
||||
#m.setTransform(tr)
|
||||
|
||||
## Start Qt event loop unless running in interactive mode.
|
||||
if sys.flags.interactive != 1:
|
||||
app.exec_()
|
@ -8,12 +8,21 @@ import pyqtgraph.opengl as gl
|
||||
|
||||
app = QtGui.QApplication([])
|
||||
w = gl.GLViewWidget()
|
||||
w.opts['distance'] = 20
|
||||
w.show()
|
||||
|
||||
ax = gl.GLAxisItem()
|
||||
ax.setSize(5,5,5)
|
||||
w.addItem(ax)
|
||||
|
||||
b = gl.GLBoxItem()
|
||||
w.addItem(b)
|
||||
|
||||
v = gl.GLVolumeItem()
|
||||
w.addItem(v)
|
||||
ax2 = gl.GLAxisItem()
|
||||
ax2.setParentItem(b)
|
||||
|
||||
b.translate(1,1,1)
|
||||
|
||||
## Start Qt event loop unless running in interactive mode.
|
||||
if sys.flags.interactive != 1:
|
||||
app.exec_()
|
||||
|
57
examples/GLVolumeItem.py
Normal file
57
examples/GLVolumeItem.py
Normal file
@ -0,0 +1,57 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
## Add path to library (just for examples; you do not need this)
|
||||
import sys, os
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||
|
||||
from pyqtgraph.Qt import QtCore, QtGui
|
||||
import pyqtgraph.opengl as gl
|
||||
|
||||
app = QtGui.QApplication([])
|
||||
w = gl.GLViewWidget()
|
||||
w.opts['distance'] = 200
|
||||
w.show()
|
||||
|
||||
|
||||
#b = gl.GLBoxItem()
|
||||
#w.addItem(b)
|
||||
g = gl.GLGridItem()
|
||||
g.scale(10, 10, 1)
|
||||
w.addItem(g)
|
||||
|
||||
import numpy as np
|
||||
## Hydrogen electron probability density
|
||||
def psi(i, j, k, offset=(50,50,100)):
|
||||
x = i-offset[0]
|
||||
y = j-offset[1]
|
||||
z = k-offset[2]
|
||||
th = np.arctan2(z, (x**2+y**2)**0.5)
|
||||
phi = np.arctan2(y, x)
|
||||
r = (x**2 + y**2 + z **2)**0.5
|
||||
a0 = 2
|
||||
#ps = (1./81.) * (2./np.pi)**0.5 * (1./a0)**(3/2) * (6 - r/a0) * (r/a0) * np.exp(-r/(3*a0)) * np.cos(th)
|
||||
ps = (1./81.) * 1./(6.*np.pi)**0.5 * (1./a0)**(3/2) * (r/a0)**2 * np.exp(-r/(3*a0)) * (3 * np.cos(th)**2 - 1)
|
||||
|
||||
return ps
|
||||
|
||||
#return ((1./81.) * (1./np.pi)**0.5 * (1./a0)**(3/2) * (r/a0)**2 * (r/a0) * np.exp(-r/(3*a0)) * np.sin(th) * np.cos(th) * np.exp(2 * 1j * phi))**2
|
||||
|
||||
|
||||
data = np.fromfunction(psi, (100,100,200))
|
||||
positive = np.log(np.clip(data, 0, data.max())**2)
|
||||
negative = np.log(np.clip(-data, 0, -data.min())**2)
|
||||
|
||||
d2 = np.empty(data.shape + (4,), dtype=np.ubyte)
|
||||
d2[..., 0] = positive * (255./positive.max())
|
||||
d2[..., 1] = negative * (255./negative.max())
|
||||
d2[..., 2] = d2[...,1]
|
||||
d2[..., 3] = d2[..., 0]*0.3 + d2[..., 1]*0.3
|
||||
d2[..., 3] = (d2[..., 3].astype(float) / 255.) **2 * 255
|
||||
|
||||
v = gl.GLVolumeItem(d2)
|
||||
v.translate(-50,-50,-100)
|
||||
w.addItem(v)
|
||||
|
||||
|
||||
## Start Qt event loop unless running in interactive mode.
|
||||
if sys.flags.interactive != 1:
|
||||
app.exec_()
|
@ -67,6 +67,21 @@ y = np.sin(np.linspace(0, 10, 1000)) + np.random.normal(size=1000, scale=0.1)
|
||||
p7.plot(y, fillLevel=-0.3, brush=(50,50,200,100))
|
||||
|
||||
|
||||
x2 = np.linspace(-100, 100, 1000)
|
||||
data2 = np.sin(x2) / x2
|
||||
p8 = win.addPlot(title="Region Selection")
|
||||
p8.plot(data2, pen=(255,255,255,200))
|
||||
lr = pg.LinearRegionItem([400,700])
|
||||
lr.setZValue(-10)
|
||||
p8.addItem(lr)
|
||||
|
||||
p9 = win.addPlot(title="Zoom on selected region")
|
||||
p9.plot(data2)
|
||||
def update():
|
||||
p9.setXRange(*lr.getRegion())
|
||||
lr.sigRegionChanged.connect(update)
|
||||
update()
|
||||
|
||||
## Start Qt event loop unless running in interactive mode.
|
||||
if sys.flags.interactive != 1:
|
||||
app.exec_()
|
||||
|
534
functions.py
534
functions.py
@ -120,6 +120,18 @@ def siEval(s):
|
||||
return v * 1000**n
|
||||
|
||||
|
||||
class Color(QtGui.QColor):
|
||||
def __init__(self, *args):
|
||||
QtGui.QColor.__init__(self, mkColor(*args))
|
||||
|
||||
def glColor(self):
|
||||
"""Return (r,g,b,a) normalized for use in opengl"""
|
||||
return (self.red()/255., self.green()/255., self.blue()/255., self.alpha()/255.)
|
||||
|
||||
def __getitem__(self, ind):
|
||||
return (self.red, self.green, self.blue, self.alpha)[ind]()
|
||||
|
||||
|
||||
def mkColor(*args):
|
||||
"""
|
||||
Convenience function for constructing QColor from a variety of argument types. Accepted arguments are:
|
||||
@ -632,4 +644,526 @@ def rescaleData(data, scale, offset):
|
||||
data = newData.reshape(data.shape)
|
||||
return data
|
||||
|
||||
|
||||
#def isosurface(data, level):
|
||||
#"""
|
||||
#Generate isosurface from volumetric data using marching tetrahedra algorithm.
|
||||
#See Paul Bourke, "Polygonising a Scalar Field Using Tetrahedrons" (http://local.wasp.uwa.edu.au/~pbourke/geometry/polygonise/)
|
||||
|
||||
#*data* 3D numpy array of scalar values
|
||||
#*level* The level at which to generate an isosurface
|
||||
#"""
|
||||
|
||||
#facets = []
|
||||
|
||||
### mark everything below the isosurface level
|
||||
#mask = data < level
|
||||
|
||||
#### make eight sub-fields
|
||||
#fields = np.empty((2,2,2), dtype=object)
|
||||
#slices = [slice(0,-1), slice(1,None)]
|
||||
#for i in [0,1]:
|
||||
#for j in [0,1]:
|
||||
#for k in [0,1]:
|
||||
#fields[i,j,k] = mask[slices[i], slices[j], slices[k]]
|
||||
|
||||
|
||||
|
||||
### split each cell into 6 tetrahedra
|
||||
### these all have the same 'orienation'; points 1,2,3 circle
|
||||
### clockwise around point 0
|
||||
#tetrahedra = [
|
||||
#[(0,1,0), (1,1,1), (0,1,1), (1,0,1)],
|
||||
#[(0,1,0), (0,1,1), (0,0,1), (1,0,1)],
|
||||
#[(0,1,0), (0,0,1), (0,0,0), (1,0,1)],
|
||||
#[(0,1,0), (0,0,0), (1,0,0), (1,0,1)],
|
||||
#[(0,1,0), (1,0,0), (1,1,0), (1,0,1)],
|
||||
#[(0,1,0), (1,1,0), (1,1,1), (1,0,1)]
|
||||
#]
|
||||
|
||||
### each tetrahedron will be assigned an index
|
||||
### which determines how to generate its facets.
|
||||
### this structure is:
|
||||
### facets[index][facet1, facet2, ...]
|
||||
### where each facet is triangular and its points are each
|
||||
### interpolated between two points on the tetrahedron
|
||||
### facet = [(p1a, p1b), (p2a, p2b), (p3a, p3b)]
|
||||
### facet points always circle clockwise if you are looking
|
||||
### at them from below the isosurface.
|
||||
#indexFacets = [
|
||||
#[], ## all above
|
||||
#[[(0,1), (0,2), (0,3)]], # 0 below
|
||||
#[[(1,0), (1,3), (1,2)]], # 1 below
|
||||
#[[(0,2), (1,3), (1,2)], [(0,2), (0,3), (1,3)]], # 0,1 below
|
||||
#[[(2,0), (2,1), (2,3)]], # 2 below
|
||||
#[[(0,3), (1,2), (2,3)], [(0,3), (0,1), (1,2)]], # 0,2 below
|
||||
#[[(1,0), (2,3), (2,0)], [(1,0), (1,3), (2,3)]], # 1,2 below
|
||||
#[[(3,0), (3,1), (3,2)]], # 3 above
|
||||
#[[(3,0), (3,2), (3,1)]], # 3 below
|
||||
#[[(1,0), (2,0), (2,3)], [(1,0), (2,3), (1,3)]], # 0,3 below
|
||||
#[[(0,3), (2,3), (1,2)], [(0,3), (1,2), (0,1)]], # 1,3 below
|
||||
#[[(2,0), (2,3), (2,1)]], # 0,1,3 below
|
||||
#[[(0,2), (1,2), (1,3)], [(0,2), (1,3), (0,3)]], # 2,3 below
|
||||
#[[(1,0), (1,2), (1,3)]], # 0,2,3 below
|
||||
#[[(0,1), (0,3), (0,2)]], # 1,2,3 below
|
||||
#[] ## all below
|
||||
#]
|
||||
|
||||
#for tet in tetrahedra:
|
||||
|
||||
### get the 4 fields for this tetrahedron
|
||||
#tetFields = [fields[c] for c in tet]
|
||||
|
||||
### generate an index for each grid cell
|
||||
#index = tetFields[0] + tetFields[1]*2 + tetFields[2]*4 + tetFields[3]*8
|
||||
|
||||
### add facets
|
||||
#for i in xrange(index.shape[0]): # data x-axis
|
||||
#for j in xrange(index.shape[1]): # data y-axis
|
||||
#for k in xrange(index.shape[2]): # data z-axis
|
||||
#for f in indexFacets[index[i,j,k]]: # faces to generate for this tet
|
||||
#pts = []
|
||||
#for l in [0,1,2]: # points in this face
|
||||
#p1 = tet[f[l][0]] # tet corner 1
|
||||
#p2 = tet[f[l][1]] # tet corner 2
|
||||
#pts.append([(p1[x]+p2[x])*0.5+[i,j,k][x]+0.5 for x in [0,1,2]]) ## interpolate between tet corners
|
||||
#facets.append(pts)
|
||||
|
||||
#return facets
|
||||
|
||||
|
||||
|
||||
|
||||
def isosurface(data, level):
|
||||
"""
|
||||
Generate isosurface from volumetric data using marching tetrahedra algorithm.
|
||||
See Paul Bourke, "Polygonising a Scalar Field"
|
||||
(http://local.wasp.uwa.edu.au/~pbourke/geometry/polygonise/)
|
||||
|
||||
*data* 3D numpy array of scalar values
|
||||
*level* The level at which to generate an isosurface
|
||||
|
||||
This function is SLOW; plenty of room for optimization here.
|
||||
"""
|
||||
|
||||
## map from grid cell index to edge index.
|
||||
## grid cell index tells us which corners are below the isosurface,
|
||||
## edge index tells us which edges are cut by the isosurface.
|
||||
## (Data stolen from Bourk; see above.)
|
||||
edgeTable = [
|
||||
0x0 , 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c,
|
||||
0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
|
||||
0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c,
|
||||
0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
|
||||
0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c,
|
||||
0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
|
||||
0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac,
|
||||
0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
|
||||
0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c,
|
||||
0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
|
||||
0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc,
|
||||
0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
|
||||
0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c,
|
||||
0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
|
||||
0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc ,
|
||||
0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
|
||||
0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc,
|
||||
0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
|
||||
0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c,
|
||||
0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
|
||||
0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc,
|
||||
0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
|
||||
0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c,
|
||||
0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460,
|
||||
0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac,
|
||||
0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0,
|
||||
0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c,
|
||||
0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230,
|
||||
0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c,
|
||||
0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190,
|
||||
0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c,
|
||||
0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 ]
|
||||
|
||||
## Table of triangles to use for filling each grid cell.
|
||||
## Each set of three integers tells us which three edges to
|
||||
## draw a triangle between.
|
||||
## (Data stolen from Bourk; see above.)
|
||||
triTable = [
|
||||
[],
|
||||
[0, 8, 3],
|
||||
[0, 1, 9],
|
||||
[1, 8, 3, 9, 8, 1],
|
||||
[1, 2, 10],
|
||||
[0, 8, 3, 1, 2, 10],
|
||||
[9, 2, 10, 0, 2, 9],
|
||||
[2, 8, 3, 2, 10, 8, 10, 9, 8],
|
||||
[3, 11, 2],
|
||||
[0, 11, 2, 8, 11, 0],
|
||||
[1, 9, 0, 2, 3, 11],
|
||||
[1, 11, 2, 1, 9, 11, 9, 8, 11],
|
||||
[3, 10, 1, 11, 10, 3],
|
||||
[0, 10, 1, 0, 8, 10, 8, 11, 10],
|
||||
[3, 9, 0, 3, 11, 9, 11, 10, 9],
|
||||
[9, 8, 10, 10, 8, 11],
|
||||
[4, 7, 8],
|
||||
[4, 3, 0, 7, 3, 4],
|
||||
[0, 1, 9, 8, 4, 7],
|
||||
[4, 1, 9, 4, 7, 1, 7, 3, 1],
|
||||
[1, 2, 10, 8, 4, 7],
|
||||
[3, 4, 7, 3, 0, 4, 1, 2, 10],
|
||||
[9, 2, 10, 9, 0, 2, 8, 4, 7],
|
||||
[2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4],
|
||||
[8, 4, 7, 3, 11, 2],
|
||||
[11, 4, 7, 11, 2, 4, 2, 0, 4],
|
||||
[9, 0, 1, 8, 4, 7, 2, 3, 11],
|
||||
[4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1],
|
||||
[3, 10, 1, 3, 11, 10, 7, 8, 4],
|
||||
[1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4],
|
||||
[4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3],
|
||||
[4, 7, 11, 4, 11, 9, 9, 11, 10],
|
||||
[9, 5, 4],
|
||||
[9, 5, 4, 0, 8, 3],
|
||||
[0, 5, 4, 1, 5, 0],
|
||||
[8, 5, 4, 8, 3, 5, 3, 1, 5],
|
||||
[1, 2, 10, 9, 5, 4],
|
||||
[3, 0, 8, 1, 2, 10, 4, 9, 5],
|
||||
[5, 2, 10, 5, 4, 2, 4, 0, 2],
|
||||
[2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8],
|
||||
[9, 5, 4, 2, 3, 11],
|
||||
[0, 11, 2, 0, 8, 11, 4, 9, 5],
|
||||
[0, 5, 4, 0, 1, 5, 2, 3, 11],
|
||||
[2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5],
|
||||
[10, 3, 11, 10, 1, 3, 9, 5, 4],
|
||||
[4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10],
|
||||
[5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3],
|
||||
[5, 4, 8, 5, 8, 10, 10, 8, 11],
|
||||
[9, 7, 8, 5, 7, 9],
|
||||
[9, 3, 0, 9, 5, 3, 5, 7, 3],
|
||||
[0, 7, 8, 0, 1, 7, 1, 5, 7],
|
||||
[1, 5, 3, 3, 5, 7],
|
||||
[9, 7, 8, 9, 5, 7, 10, 1, 2],
|
||||
[10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3],
|
||||
[8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2],
|
||||
[2, 10, 5, 2, 5, 3, 3, 5, 7],
|
||||
[7, 9, 5, 7, 8, 9, 3, 11, 2],
|
||||
[9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11],
|
||||
[2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7],
|
||||
[11, 2, 1, 11, 1, 7, 7, 1, 5],
|
||||
[9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11],
|
||||
[5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0],
|
||||
[11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0],
|
||||
[11, 10, 5, 7, 11, 5],
|
||||
[10, 6, 5],
|
||||
[0, 8, 3, 5, 10, 6],
|
||||
[9, 0, 1, 5, 10, 6],
|
||||
[1, 8, 3, 1, 9, 8, 5, 10, 6],
|
||||
[1, 6, 5, 2, 6, 1],
|
||||
[1, 6, 5, 1, 2, 6, 3, 0, 8],
|
||||
[9, 6, 5, 9, 0, 6, 0, 2, 6],
|
||||
[5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8],
|
||||
[2, 3, 11, 10, 6, 5],
|
||||
[11, 0, 8, 11, 2, 0, 10, 6, 5],
|
||||
[0, 1, 9, 2, 3, 11, 5, 10, 6],
|
||||
[5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11],
|
||||
[6, 3, 11, 6, 5, 3, 5, 1, 3],
|
||||
[0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6],
|
||||
[3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9],
|
||||
[6, 5, 9, 6, 9, 11, 11, 9, 8],
|
||||
[5, 10, 6, 4, 7, 8],
|
||||
[4, 3, 0, 4, 7, 3, 6, 5, 10],
|
||||
[1, 9, 0, 5, 10, 6, 8, 4, 7],
|
||||
[10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4],
|
||||
[6, 1, 2, 6, 5, 1, 4, 7, 8],
|
||||
[1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7],
|
||||
[8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6],
|
||||
[7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9],
|
||||
[3, 11, 2, 7, 8, 4, 10, 6, 5],
|
||||
[5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11],
|
||||
[0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6],
|
||||
[9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6],
|
||||
[8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6],
|
||||
[5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11],
|
||||
[0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7],
|
||||
[6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9],
|
||||
[10, 4, 9, 6, 4, 10],
|
||||
[4, 10, 6, 4, 9, 10, 0, 8, 3],
|
||||
[10, 0, 1, 10, 6, 0, 6, 4, 0],
|
||||
[8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10],
|
||||
[1, 4, 9, 1, 2, 4, 2, 6, 4],
|
||||
[3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4],
|
||||
[0, 2, 4, 4, 2, 6],
|
||||
[8, 3, 2, 8, 2, 4, 4, 2, 6],
|
||||
[10, 4, 9, 10, 6, 4, 11, 2, 3],
|
||||
[0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6],
|
||||
[3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10],
|
||||
[6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1],
|
||||
[9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3],
|
||||
[8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1],
|
||||
[3, 11, 6, 3, 6, 0, 0, 6, 4],
|
||||
[6, 4, 8, 11, 6, 8],
|
||||
[7, 10, 6, 7, 8, 10, 8, 9, 10],
|
||||
[0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10],
|
||||
[10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0],
|
||||
[10, 6, 7, 10, 7, 1, 1, 7, 3],
|
||||
[1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7],
|
||||
[2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9],
|
||||
[7, 8, 0, 7, 0, 6, 6, 0, 2],
|
||||
[7, 3, 2, 6, 7, 2],
|
||||
[2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7],
|
||||
[2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7],
|
||||
[1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11],
|
||||
[11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1],
|
||||
[8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6],
|
||||
[0, 9, 1, 11, 6, 7],
|
||||
[7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0],
|
||||
[7, 11, 6],
|
||||
[7, 6, 11],
|
||||
[3, 0, 8, 11, 7, 6],
|
||||
[0, 1, 9, 11, 7, 6],
|
||||
[8, 1, 9, 8, 3, 1, 11, 7, 6],
|
||||
[10, 1, 2, 6, 11, 7],
|
||||
[1, 2, 10, 3, 0, 8, 6, 11, 7],
|
||||
[2, 9, 0, 2, 10, 9, 6, 11, 7],
|
||||
[6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8],
|
||||
[7, 2, 3, 6, 2, 7],
|
||||
[7, 0, 8, 7, 6, 0, 6, 2, 0],
|
||||
[2, 7, 6, 2, 3, 7, 0, 1, 9],
|
||||
[1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6],
|
||||
[10, 7, 6, 10, 1, 7, 1, 3, 7],
|
||||
[10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8],
|
||||
[0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7],
|
||||
[7, 6, 10, 7, 10, 8, 8, 10, 9],
|
||||
[6, 8, 4, 11, 8, 6],
|
||||
[3, 6, 11, 3, 0, 6, 0, 4, 6],
|
||||
[8, 6, 11, 8, 4, 6, 9, 0, 1],
|
||||
[9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6],
|
||||
[6, 8, 4, 6, 11, 8, 2, 10, 1],
|
||||
[1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6],
|
||||
[4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9],
|
||||
[10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3],
|
||||
[8, 2, 3, 8, 4, 2, 4, 6, 2],
|
||||
[0, 4, 2, 4, 6, 2],
|
||||
[1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8],
|
||||
[1, 9, 4, 1, 4, 2, 2, 4, 6],
|
||||
[8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1],
|
||||
[10, 1, 0, 10, 0, 6, 6, 0, 4],
|
||||
[4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3],
|
||||
[10, 9, 4, 6, 10, 4],
|
||||
[4, 9, 5, 7, 6, 11],
|
||||
[0, 8, 3, 4, 9, 5, 11, 7, 6],
|
||||
[5, 0, 1, 5, 4, 0, 7, 6, 11],
|
||||
[11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5],
|
||||
[9, 5, 4, 10, 1, 2, 7, 6, 11],
|
||||
[6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5],
|
||||
[7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2],
|
||||
[3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6],
|
||||
[7, 2, 3, 7, 6, 2, 5, 4, 9],
|
||||
[9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7],
|
||||
[3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0],
|
||||
[6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8],
|
||||
[9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7],
|
||||
[1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4],
|
||||
[4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10],
|
||||
[7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10],
|
||||
[6, 9, 5, 6, 11, 9, 11, 8, 9],
|
||||
[3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5],
|
||||
[0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11],
|
||||
[6, 11, 3, 6, 3, 5, 5, 3, 1],
|
||||
[1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6],
|
||||
[0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10],
|
||||
[11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5],
|
||||
[6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3],
|
||||
[5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2],
|
||||
[9, 5, 6, 9, 6, 0, 0, 6, 2],
|
||||
[1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8],
|
||||
[1, 5, 6, 2, 1, 6],
|
||||
[1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6],
|
||||
[10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0],
|
||||
[0, 3, 8, 5, 6, 10],
|
||||
[10, 5, 6],
|
||||
[11, 5, 10, 7, 5, 11],
|
||||
[11, 5, 10, 11, 7, 5, 8, 3, 0],
|
||||
[5, 11, 7, 5, 10, 11, 1, 9, 0],
|
||||
[10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1],
|
||||
[11, 1, 2, 11, 7, 1, 7, 5, 1],
|
||||
[0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11],
|
||||
[9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7],
|
||||
[7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2],
|
||||
[2, 5, 10, 2, 3, 5, 3, 7, 5],
|
||||
[8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5],
|
||||
[9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2],
|
||||
[9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2],
|
||||
[1, 3, 5, 3, 7, 5],
|
||||
[0, 8, 7, 0, 7, 1, 1, 7, 5],
|
||||
[9, 0, 3, 9, 3, 5, 5, 3, 7],
|
||||
[9, 8, 7, 5, 9, 7],
|
||||
[5, 8, 4, 5, 10, 8, 10, 11, 8],
|
||||
[5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0],
|
||||
[0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5],
|
||||
[10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4],
|
||||
[2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8],
|
||||
[0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11],
|
||||
[0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5],
|
||||
[9, 4, 5, 2, 11, 3],
|
||||
[2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4],
|
||||
[5, 10, 2, 5, 2, 4, 4, 2, 0],
|
||||
[3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9],
|
||||
[5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2],
|
||||
[8, 4, 5, 8, 5, 3, 3, 5, 1],
|
||||
[0, 4, 5, 1, 0, 5],
|
||||
[8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5],
|
||||
[9, 4, 5],
|
||||
[4, 11, 7, 4, 9, 11, 9, 10, 11],
|
||||
[0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11],
|
||||
[1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11],
|
||||
[3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4],
|
||||
[4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2],
|
||||
[9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3],
|
||||
[11, 7, 4, 11, 4, 2, 2, 4, 0],
|
||||
[11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4],
|
||||
[2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9],
|
||||
[9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7],
|
||||
[3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10],
|
||||
[1, 10, 2, 8, 7, 4],
|
||||
[4, 9, 1, 4, 1, 7, 7, 1, 3],
|
||||
[4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1],
|
||||
[4, 0, 3, 7, 4, 3],
|
||||
[4, 8, 7],
|
||||
[9, 10, 8, 10, 11, 8],
|
||||
[3, 0, 9, 3, 9, 11, 11, 9, 10],
|
||||
[0, 1, 10, 0, 10, 8, 8, 10, 11],
|
||||
[3, 1, 10, 11, 3, 10],
|
||||
[1, 2, 11, 1, 11, 9, 9, 11, 8],
|
||||
[3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9],
|
||||
[0, 2, 11, 8, 0, 11],
|
||||
[3, 2, 11],
|
||||
[2, 3, 8, 2, 8, 10, 10, 8, 9],
|
||||
[9, 10, 2, 0, 9, 2],
|
||||
[2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8],
|
||||
[1, 10, 2],
|
||||
[1, 3, 8, 9, 1, 8],
|
||||
[0, 9, 1],
|
||||
[0, 3, 8],
|
||||
[]
|
||||
]
|
||||
|
||||
## translation between edge index and
|
||||
## the vertex indexes that bound the edge
|
||||
edgeKey = [
|
||||
[(0,0,0), (1,0,0)],
|
||||
[(1,0,0), (1,1,0)],
|
||||
[(1,1,0), (0,1,0)],
|
||||
[(0,1,0), (0,0,0)],
|
||||
[(0,0,1), (1,0,1)],
|
||||
[(1,0,1), (1,1,1)],
|
||||
[(1,1,1), (0,1,1)],
|
||||
[(0,1,1), (0,0,1)],
|
||||
[(0,0,0), (0,0,1)],
|
||||
[(1,0,0), (1,0,1)],
|
||||
[(1,1,0), (1,1,1)],
|
||||
[(0,1,0), (0,1,1)],
|
||||
]
|
||||
|
||||
|
||||
|
||||
facets = []
|
||||
|
||||
## mark everything below the isosurface level
|
||||
mask = data < level
|
||||
|
||||
### make eight sub-fields and compute indexes for grid cells
|
||||
index = np.zeros([x-1 for x in data.shape], dtype=np.ubyte)
|
||||
fields = np.empty((2,2,2), dtype=object)
|
||||
slices = [slice(0,-1), slice(1,None)]
|
||||
for i in [0,1]:
|
||||
for j in [0,1]:
|
||||
for k in [0,1]:
|
||||
fields[i,j,k] = mask[slices[i], slices[j], slices[k]]
|
||||
vertIndex = i - 2*j*i + 3*j + 4*k ## this is just to match Bourk's vertex numbering scheme
|
||||
#print i,j,k," : ", fields[i,j,k], 2**vertIndex
|
||||
index += fields[i,j,k] * 2**vertIndex
|
||||
#print index
|
||||
#print index
|
||||
|
||||
## add facets
|
||||
for i in xrange(index.shape[0]): # data x-axis
|
||||
for j in xrange(index.shape[1]): # data y-axis
|
||||
for k in xrange(index.shape[2]): # data z-axis
|
||||
tris = triTable[index[i,j,k]]
|
||||
for l in range(0, len(tris), 3): ## faces for this grid cell
|
||||
edges = tris[l:l+3]
|
||||
pts = []
|
||||
for m in [0,1,2]: # points in this face
|
||||
p1 = edgeKey[edges[m]][0]
|
||||
p2 = edgeKey[edges[m]][1]
|
||||
v1 = data[i+p1[0], j+p1[1], k+p1[2]]
|
||||
v2 = data[i+p2[0], j+p2[1], k+p2[2]]
|
||||
f = (level-v1) / (v2-v1)
|
||||
fi = 1.0 - f
|
||||
p = ( ## interpolate between corners
|
||||
p1[0]*fi + p2[0]*f + i + 0.5,
|
||||
p1[1]*fi + p2[1]*f + j + 0.5,
|
||||
p1[2]*fi + p2[2]*f + k + 0.5
|
||||
)
|
||||
pts.append(p)
|
||||
facets.append(pts)
|
||||
|
||||
return facets
|
||||
|
||||
|
||||
def meshNormals(data):
|
||||
"""
|
||||
Return list of normal vectors and list of faces which reference the normals
|
||||
data must be list of triangles; each triangle is a list of three points
|
||||
[ [(x,y,z), (x,y,z), (x,y,z)], ...]
|
||||
Return values are
|
||||
normals: [(x,y,z), ...]
|
||||
faces: [(n1, n2, n3), ...]
|
||||
"""
|
||||
|
||||
normals = []
|
||||
points = {}
|
||||
for i, face in enumerate(data):
|
||||
## compute face normal
|
||||
pts = [QtGui.QVector3D(*x) for x in face]
|
||||
norm = QtGui.QVector3D.crossProduct(pts[1]-pts[0], pts[2]-pts[0])
|
||||
normals.append(norm)
|
||||
|
||||
## remember each point was associated with this normal
|
||||
for p in face:
|
||||
p = tuple(map(lambda x: np.round(x, 8), p))
|
||||
if p not in points:
|
||||
points[p] = []
|
||||
points[p].append(i)
|
||||
|
||||
## compute averages
|
||||
avgLookup = {}
|
||||
avgNorms = []
|
||||
for k,v in points.iteritems():
|
||||
norms = [normals[i] for i in v]
|
||||
a = norms[0]
|
||||
if len(v) > 1:
|
||||
for n in norms[1:]:
|
||||
a = a + n
|
||||
a = a / len(v)
|
||||
avgLookup[k] = len(avgNorms)
|
||||
avgNorms.append(a)
|
||||
|
||||
## generate return array
|
||||
faces = []
|
||||
for i, face in enumerate(data):
|
||||
f = []
|
||||
for p in face:
|
||||
p = tuple(map(lambda x: np.round(x, 8), p))
|
||||
f.append(avgLookup[p])
|
||||
faces.append(tuple(f))
|
||||
|
||||
return avgNorms, faces
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -297,7 +297,18 @@ class AxisItem(GraphicsWidget):
|
||||
pw = 10 ** (np.floor(np.log10(dif))-1)
|
||||
scaledIntervals = intervals * pw
|
||||
scaledTickCounts = dif / scaledIntervals
|
||||
i1 = np.argwhere(scaledTickCounts < optimalTickCount)[0,0]
|
||||
try:
|
||||
i1 = np.argwhere(scaledTickCounts < optimalTickCount)[0,0]
|
||||
except:
|
||||
print "AxisItem can't determine tick spacing:"
|
||||
print "scaledTickCounts", scaledTickCounts
|
||||
print "optimalTickCount", optimalTickCount
|
||||
print "dif", dif
|
||||
print "scaledIntervals", scaledIntervals
|
||||
print "intervals", intervals
|
||||
print "pw", pw
|
||||
print "pixelSpacing", pixelSpacing
|
||||
i1 = 1
|
||||
|
||||
distBetweenIntervals = (optimalTickCount-scaledTickCounts[i1]) / (scaledTickCounts[i1-1]-scaledTickCounts[i1])
|
||||
|
||||
|
@ -6,6 +6,8 @@ class GLGraphicsItem(QtCore.QObject):
|
||||
self.__parent = None
|
||||
self.__view = None
|
||||
self.__children = set()
|
||||
self.__transform = QtGui.QMatrix4x4()
|
||||
self.__visible = True
|
||||
self.setParentItem(parentItem)
|
||||
self.setDepthValue(0)
|
||||
|
||||
@ -16,6 +18,11 @@ class GLGraphicsItem(QtCore.QObject):
|
||||
item.__children.add(self)
|
||||
self.__parent = item
|
||||
|
||||
if self.__parent is not None and self.view() is not self.__parent.view():
|
||||
if self.view() is not None:
|
||||
self.view().removeItem(self)
|
||||
self.__parent.view().addItem(self)
|
||||
|
||||
def parentItem(self):
|
||||
return self.__parent
|
||||
|
||||
@ -42,6 +49,69 @@ class GLGraphicsItem(QtCore.QObject):
|
||||
"""Return the depth value of this item. See setDepthValue for mode information."""
|
||||
return self.__depthValue
|
||||
|
||||
def setTransform(self, tr):
|
||||
self.__transform = tr
|
||||
self.update()
|
||||
|
||||
def applyTransform(self, tr, local):
|
||||
"""
|
||||
Multiply this object's transform by *tr*.
|
||||
If local is True, then *tr* is multiplied on the right of the current transform:
|
||||
newTransform = transform * tr
|
||||
If local is False, then *tr* is instead multiplied on the left:
|
||||
newTransform = tr * transform
|
||||
"""
|
||||
if local:
|
||||
self.setTransform(self.transform() * tr)
|
||||
else:
|
||||
self.setTransform(tr * self.transform())
|
||||
|
||||
def transform(self):
|
||||
return self.__transform
|
||||
|
||||
def translate(self, dx, dy, dz, local=False):
|
||||
"""
|
||||
Translate the object by (*dx*, *dy*, *dz*) in its parent's coordinate system.
|
||||
If *local* is True, then translation takes place in local coordinates.
|
||||
"""
|
||||
tr = QtGui.QMatrix4x4()
|
||||
tr.translate(dx, dy, dz)
|
||||
self.applyTransform(tr, local=local)
|
||||
|
||||
def rotate(self, angle, x, y, z, local=False):
|
||||
"""
|
||||
Rotate the object around the axis specified by (x,y,z).
|
||||
*angle* is in degrees.
|
||||
|
||||
"""
|
||||
tr = QtGui.QMatrix4x4()
|
||||
tr.rotate(angle, x, y, z)
|
||||
self.applyTransform(tr, local=local)
|
||||
|
||||
def scale(self, x, y, z, local=True):
|
||||
"""
|
||||
Scale the object by (*dx*, *dy*, *dz*) in its local coordinate system.
|
||||
If *local* is False, then scale takes place in the parent's coordinates.
|
||||
"""
|
||||
tr = QtGui.QMatrix4x4()
|
||||
tr.scale(x, y, z)
|
||||
self.applyTransform(tr, local=local)
|
||||
|
||||
|
||||
def hide(self):
|
||||
self.setVisible(False)
|
||||
|
||||
def show(self):
|
||||
self.setVisible(True)
|
||||
|
||||
def setVisible(self, vis):
|
||||
self.__visible = vis
|
||||
self.update()
|
||||
|
||||
def visible(self):
|
||||
return self.__visible
|
||||
|
||||
|
||||
def initializeGL(self):
|
||||
"""
|
||||
Called after an item is added to a GLViewWidget.
|
||||
@ -57,4 +127,15 @@ class GLGraphicsItem(QtCore.QObject):
|
||||
but the caller will take care of pushing/popping.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def update(self):
|
||||
v = self.view()
|
||||
if v is None:
|
||||
return
|
||||
v.updateGL()
|
||||
|
||||
def mapFromParent(self, point):
|
||||
tr = self.transform()
|
||||
if tr is None:
|
||||
return point
|
||||
return tr.inverted()[0].map(point)
|
@ -33,14 +33,15 @@ class GLViewWidget(QtOpenGL.QGLWidget):
|
||||
#print "set view", item, self, item.view()
|
||||
self.updateGL()
|
||||
|
||||
def removeItem(self, item):
|
||||
self.items.remove(item)
|
||||
item._setView(None)
|
||||
self.updateGL()
|
||||
|
||||
|
||||
def initializeGL(self):
|
||||
glClearColor(0.0, 0.0, 0.0, 0.0)
|
||||
glEnable(GL_DEPTH_TEST)
|
||||
|
||||
glEnable( GL_ALPHA_TEST )
|
||||
self.resizeGL(self.width(), self.height())
|
||||
self.generateAxes()
|
||||
#self.generatePoints()
|
||||
|
||||
def resizeGL(self, w, h):
|
||||
glViewport(0, 0, w, h)
|
||||
@ -75,15 +76,7 @@ class GLViewWidget(QtOpenGL.QGLWidget):
|
||||
def paintGL(self):
|
||||
self.setProjection()
|
||||
self.setModelview()
|
||||
|
||||
glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT )
|
||||
glDisable( GL_DEPTH_TEST )
|
||||
#print "draw list:", self.axisList
|
||||
glCallList(self.axisList) ## draw axes
|
||||
#glCallList(self.pointList)
|
||||
#self.drawPoints()
|
||||
#self.drawAxes()
|
||||
|
||||
self.drawItemTree()
|
||||
|
||||
def drawItemTree(self, item=None):
|
||||
@ -94,14 +87,19 @@ class GLViewWidget(QtOpenGL.QGLWidget):
|
||||
items.append(item)
|
||||
items.sort(lambda a,b: cmp(a.depthValue(), b.depthValue()))
|
||||
for i in items:
|
||||
if not i.visible():
|
||||
continue
|
||||
if i is item:
|
||||
i.paint()
|
||||
else:
|
||||
glMatrixMode(GL_MODELVIEW)
|
||||
glPushMatrix()
|
||||
i.paint()
|
||||
tr = i.transform()
|
||||
a = np.array(tr.copyDataTo()).reshape((4,4))
|
||||
glMultMatrixf(a.transpose())
|
||||
self.drawItemTree(i)
|
||||
glMatrixMode(GL_MODELVIEW)
|
||||
glPopMatrix()
|
||||
else:
|
||||
self.drawItemTree(i)
|
||||
|
||||
|
||||
def cameraPosition(self):
|
||||
@ -118,65 +116,7 @@ class GLViewWidget(QtOpenGL.QGLWidget):
|
||||
)
|
||||
|
||||
return pos
|
||||
|
||||
|
||||
|
||||
def generateAxes(self):
|
||||
self.axisList = glGenLists(1)
|
||||
glNewList(self.axisList, GL_COMPILE)
|
||||
|
||||
#glShadeModel(GL_FLAT)
|
||||
#glFrontFace(GL_CCW)
|
||||
#glEnable( GL_LIGHT_MODEL_TWO_SIDE )
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
||||
glEnable( GL_BLEND )
|
||||
glEnable( GL_ALPHA_TEST )
|
||||
#glAlphaFunc( GL_ALWAYS,0.5 )
|
||||
glEnable( GL_POINT_SMOOTH )
|
||||
glDisable( GL_DEPTH_TEST )
|
||||
glBegin( GL_LINES )
|
||||
|
||||
glColor4f(1, 1, 1, .3)
|
||||
for x in range(-10, 11):
|
||||
glVertex3f(x, -10, 0)
|
||||
glVertex3f(x, 10, 0)
|
||||
for y in range(-10, 11):
|
||||
glVertex3f(-10, y, 0)
|
||||
glVertex3f( 10, y, 0)
|
||||
|
||||
|
||||
glColor4f(0, 1, 0, .6) # z is green
|
||||
glVertex3f(0, 0, 0)
|
||||
glVertex3f(0, 0, 5)
|
||||
|
||||
glColor4f(1, 1, 0, .6) # y is yellow
|
||||
glVertex3f(0, 0, 0)
|
||||
glVertex3f(0, 5, 0)
|
||||
|
||||
glColor4f(0, 0, 1, .6) # x is blue
|
||||
glVertex3f(0, 0, 0)
|
||||
glVertex3f(5, 0, 0)
|
||||
glEnd()
|
||||
glEndList()
|
||||
|
||||
def generatePoints(self):
|
||||
self.pointList = glGenLists(1)
|
||||
glNewList(self.pointList, GL_COMPILE)
|
||||
width = 7
|
||||
alpha = 0.02
|
||||
n = 40
|
||||
glPointSize( width )
|
||||
glBegin(GL_POINTS)
|
||||
for x in range(-n, n+1):
|
||||
r = (n-x)/(2.*n)
|
||||
glColor4f(r, r, r, alpha)
|
||||
for y in range(-n, n+1):
|
||||
for z in range(-n, n+1):
|
||||
glVertex3f(x, y, z)
|
||||
glEnd()
|
||||
glEndList()
|
||||
|
||||
|
||||
def mousePressEvent(self, ev):
|
||||
self.mousePos = ev.pos()
|
||||
|
||||
|
25
opengl/MeshData.py
Normal file
25
opengl/MeshData.py
Normal file
@ -0,0 +1,25 @@
|
||||
class MeshData(object):
|
||||
"""
|
||||
Class for storing 3D mesh data. May contain:
|
||||
- list of vertex locations
|
||||
- list of edges
|
||||
- list of triangles
|
||||
- colors per vertex, edge, or tri
|
||||
- normals per vertex or tri
|
||||
"""
|
||||
|
||||
def __init__(self ...):
|
||||
|
||||
|
||||
def generateFaceNormals(self):
|
||||
|
||||
|
||||
def generateVertexNormals(self):
|
||||
"""
|
||||
Assigns each vertex the average of its connected face normals.
|
||||
If face normals have not been computed yet, then generateFaceNormals will be called.
|
||||
"""
|
||||
|
||||
|
||||
def reverseNormals(self):
|
||||
|
51
opengl/items/GLAxisItem.py
Normal file
51
opengl/items/GLAxisItem.py
Normal file
@ -0,0 +1,51 @@
|
||||
from OpenGL.GL import *
|
||||
from .. GLGraphicsItem import GLGraphicsItem
|
||||
from pyqtgraph import QtGui
|
||||
|
||||
__all__ = ['GLAxisItem']
|
||||
|
||||
class GLAxisItem(GLGraphicsItem):
|
||||
def __init__(self, size=None):
|
||||
GLGraphicsItem.__init__(self)
|
||||
if size is None:
|
||||
size = QtGui.QVector3D(1,1,1)
|
||||
self.setSize(size=size)
|
||||
|
||||
def setSize(self, x=None, y=None, z=None, size=None):
|
||||
"""
|
||||
Set the size of the axes (in its local coordinate system; this does not affect the transform)
|
||||
Arguments can be x,y,z or size=QVector3D().
|
||||
"""
|
||||
if size is not None:
|
||||
x = size.x()
|
||||
y = size.y()
|
||||
z = size.z()
|
||||
self.__size = [x,y,z]
|
||||
self.update()
|
||||
|
||||
def size(self):
|
||||
return self.__size[:]
|
||||
|
||||
|
||||
def paint(self):
|
||||
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
||||
glEnable( GL_BLEND )
|
||||
glEnable( GL_ALPHA_TEST )
|
||||
glEnable( GL_POINT_SMOOTH )
|
||||
#glDisable( GL_DEPTH_TEST )
|
||||
glBegin( GL_LINES )
|
||||
|
||||
x,y,z = self.size()
|
||||
glColor4f(0, 1, 0, .6) # z is green
|
||||
glVertex3f(0, 0, 0)
|
||||
glVertex3f(0, 0, z)
|
||||
|
||||
glColor4f(1, 1, 0, .6) # y is yellow
|
||||
glVertex3f(0, 0, 0)
|
||||
glVertex3f(0, y, 0)
|
||||
|
||||
glColor4f(0, 0, 1, .6) # x is blue
|
||||
glVertex3f(0, 0, 0)
|
||||
glVertex3f(x, 0, 0)
|
||||
glEnd()
|
@ -1,9 +1,42 @@
|
||||
from OpenGL.GL import *
|
||||
from .. GLGraphicsItem import GLGraphicsItem
|
||||
from pyqtgraph.Qt import QtGui
|
||||
import pyqtgraph as pg
|
||||
|
||||
__all__ = ['GLBoxItem']
|
||||
|
||||
class GLBoxItem(GLGraphicsItem):
|
||||
def __init__(self, size=None, color=None):
|
||||
GLGraphicsItem.__init__(self)
|
||||
if size is None:
|
||||
size = QtGui.QVector3D(1,1,1)
|
||||
self.setSize(size=size)
|
||||
if color is None:
|
||||
color = (255,255,255,80)
|
||||
self.setColor(color)
|
||||
|
||||
def setSize(self, x=None, y=None, z=None, size=None):
|
||||
"""
|
||||
Set the size of the box (in its local coordinate system; this does not affect the transform)
|
||||
Arguments can be x,y,z or size=QVector3D().
|
||||
"""
|
||||
if size is not None:
|
||||
x = size.x()
|
||||
y = size.y()
|
||||
z = size.z()
|
||||
self.__size = [x,y,z]
|
||||
self.update()
|
||||
|
||||
def size(self):
|
||||
return self.__size[:]
|
||||
|
||||
def setColor(self, *args):
|
||||
"""Set the color of the box. Arguments are the same as those accepted by functions.mkColor()"""
|
||||
self.__color = pg.Color(*args)
|
||||
|
||||
def color(self):
|
||||
return self.__color
|
||||
|
||||
def paint(self):
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
||||
glEnable( GL_BLEND )
|
||||
@ -13,34 +46,34 @@ class GLBoxItem(GLGraphicsItem):
|
||||
glDisable( GL_DEPTH_TEST )
|
||||
glBegin( GL_LINES )
|
||||
|
||||
glColor4f(1, 1, 1, .3)
|
||||
w = 10
|
||||
glVertex3f(-w, -w, -w)
|
||||
glVertex3f(-w, -w, w)
|
||||
glVertex3f( w, -w, -w)
|
||||
glVertex3f( w, -w, w)
|
||||
glVertex3f(-w, w, -w)
|
||||
glVertex3f(-w, w, w)
|
||||
glVertex3f( w, w, -w)
|
||||
glVertex3f( w, w, w)
|
||||
glColor4f(*self.color().glColor())
|
||||
x,y,z = self.size()
|
||||
glVertex3f(0, 0, 0)
|
||||
glVertex3f(0, 0, z)
|
||||
glVertex3f(x, 0, 0)
|
||||
glVertex3f(x, 0, z)
|
||||
glVertex3f(0, y, 0)
|
||||
glVertex3f(0, y, z)
|
||||
glVertex3f(x, y, 0)
|
||||
glVertex3f(x, y, z)
|
||||
|
||||
glVertex3f(-w, -w, -w)
|
||||
glVertex3f(-w, w, -w)
|
||||
glVertex3f( w, -w, -w)
|
||||
glVertex3f( w, w, -w)
|
||||
glVertex3f(-w, -w, w)
|
||||
glVertex3f(-w, w, w)
|
||||
glVertex3f( w, -w, w)
|
||||
glVertex3f( w, w, w)
|
||||
glVertex3f(0, 0, 0)
|
||||
glVertex3f(0, y, 0)
|
||||
glVertex3f(x, 0, 0)
|
||||
glVertex3f(x, y, 0)
|
||||
glVertex3f(0, 0, z)
|
||||
glVertex3f(0, y, z)
|
||||
glVertex3f(x, 0, z)
|
||||
glVertex3f(x, y, z)
|
||||
|
||||
glVertex3f(-w, -w, -w)
|
||||
glVertex3f( w, -w, -w)
|
||||
glVertex3f(-w, w, -w)
|
||||
glVertex3f( w, w, -w)
|
||||
glVertex3f(-w, -w, w)
|
||||
glVertex3f( w, -w, w)
|
||||
glVertex3f(-w, w, w)
|
||||
glVertex3f( w, w, w)
|
||||
glVertex3f(0, 0, 0)
|
||||
glVertex3f(x, 0, 0)
|
||||
glVertex3f(0, y, 0)
|
||||
glVertex3f(x, y, 0)
|
||||
glVertex3f(0, 0, z)
|
||||
glVertex3f(x, 0, z)
|
||||
glVertex3f(0, y, z)
|
||||
glVertex3f(x, y, z)
|
||||
|
||||
glEnd()
|
||||
|
||||
|
48
opengl/items/GLGridItem.py
Normal file
48
opengl/items/GLGridItem.py
Normal file
@ -0,0 +1,48 @@
|
||||
from OpenGL.GL import *
|
||||
from .. GLGraphicsItem import GLGraphicsItem
|
||||
from pyqtgraph import QtGui
|
||||
|
||||
__all__ = ['GLGridItem']
|
||||
|
||||
class GLGridItem(GLGraphicsItem):
|
||||
def __init__(self, size=None, color=None):
|
||||
GLGraphicsItem.__init__(self)
|
||||
if size is None:
|
||||
size = QtGui.QVector3D(1,1,1)
|
||||
self.setSize(size=size)
|
||||
|
||||
def setSize(self, x=None, y=None, z=None, size=None):
|
||||
"""
|
||||
Set the size of the axes (in its local coordinate system; this does not affect the transform)
|
||||
Arguments can be x,y,z or size=QVector3D().
|
||||
"""
|
||||
if size is not None:
|
||||
x = size.x()
|
||||
y = size.y()
|
||||
z = size.z()
|
||||
self.__size = [x,y,z]
|
||||
self.update()
|
||||
|
||||
def size(self):
|
||||
return self.__size[:]
|
||||
|
||||
|
||||
def paint(self):
|
||||
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
||||
glEnable( GL_BLEND )
|
||||
glEnable( GL_ALPHA_TEST )
|
||||
glEnable( GL_POINT_SMOOTH )
|
||||
#glDisable( GL_DEPTH_TEST )
|
||||
glBegin( GL_LINES )
|
||||
|
||||
x,y,z = self.size()
|
||||
glColor4f(1, 1, 1, .3)
|
||||
for x in range(-10, 11):
|
||||
glVertex3f(x, -10, 0)
|
||||
glVertex3f(x, 10, 0)
|
||||
for y in range(-10, 11):
|
||||
glVertex3f(-10, y, 0)
|
||||
glVertex3f( 10, y, 0)
|
||||
|
||||
glEnd()
|
93
opengl/items/GLMeshItem.py
Normal file
93
opengl/items/GLMeshItem.py
Normal file
@ -0,0 +1,93 @@
|
||||
from OpenGL.GL import *
|
||||
from .. GLGraphicsItem import GLGraphicsItem
|
||||
from pyqtgraph.Qt import QtGui
|
||||
import pyqtgraph as pg
|
||||
from .. import shaders
|
||||
import numpy as np
|
||||
|
||||
|
||||
|
||||
__all__ = ['GLMeshItem']
|
||||
|
||||
class GLMeshItem(GLGraphicsItem):
|
||||
def __init__(self, faces):
|
||||
self.faces = faces
|
||||
self.normals, self.faceNormals = pg.meshNormals(faces)
|
||||
|
||||
GLGraphicsItem.__init__(self)
|
||||
|
||||
def initializeGL(self):
|
||||
|
||||
#balloonVertexShader = shaders.compileShader("""
|
||||
#varying vec3 normal;
|
||||
#void main() {
|
||||
#normal = normalize(gl_NormalMatrix * gl_Normal);
|
||||
#//vec4 color = normal;
|
||||
#//normal.w = min(color.w + 2.0 * color.w * pow(normal.x*normal.x + normal.y*normal.y, 2.0), 1.0);
|
||||
#gl_FrontColor = gl_Color;
|
||||
#gl_BackColor = gl_Color;
|
||||
#gl_Position = ftransform();
|
||||
#}""", GL_VERTEX_SHADER)
|
||||
#balloonFragmentShader = shaders.compileShader("""
|
||||
#varying vec3 normal;
|
||||
#void main() {
|
||||
#vec4 color = gl_Color;
|
||||
#color.w = min(color.w + 2.0 * color.w * pow(normal.x*normal.x + normal.y*normal.y, 5.0), 1.0);
|
||||
#gl_FragColor = color;
|
||||
#}""", GL_FRAGMENT_SHADER)
|
||||
#self.shader = shaders.compileProgram(balloonVertexShader, balloonFragmentShader)
|
||||
|
||||
self.shader = shaders.getShader('balloon')
|
||||
|
||||
l = glGenLists(1)
|
||||
self.triList = l
|
||||
glNewList(l, GL_COMPILE)
|
||||
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
||||
glEnable( GL_BLEND )
|
||||
glEnable( GL_ALPHA_TEST )
|
||||
#glAlphaFunc( GL_ALWAYS,0.5 )
|
||||
glEnable( GL_POINT_SMOOTH )
|
||||
glDisable( GL_DEPTH_TEST )
|
||||
glColor4f(1, 1, 1, .1)
|
||||
glBegin( GL_TRIANGLES )
|
||||
for i, f in enumerate(self.faces):
|
||||
pts = [QtGui.QVector3D(*x) for x in f]
|
||||
if pts[0] is None:
|
||||
print f
|
||||
continue
|
||||
#norm = QtGui.QVector3D.crossProduct(pts[1]-pts[0], pts[2]-pts[0])
|
||||
for j in [0,1,2]:
|
||||
norm = self.normals[self.faceNormals[i][j]]
|
||||
glNormal3f(norm.x(), norm.y(), norm.z())
|
||||
#j = (i+1) % 3
|
||||
glVertex3f(*f[j])
|
||||
glEnd()
|
||||
glEndList()
|
||||
|
||||
|
||||
l = glGenLists(1)
|
||||
self.meshList = l
|
||||
glNewList(l, GL_COMPILE)
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
||||
glEnable( GL_BLEND )
|
||||
glEnable( GL_ALPHA_TEST )
|
||||
#glAlphaFunc( GL_ALWAYS,0.5 )
|
||||
glEnable( GL_POINT_SMOOTH )
|
||||
glEnable( GL_DEPTH_TEST )
|
||||
glColor4f(1, 1, 1, .3)
|
||||
glBegin( GL_LINES )
|
||||
for f in self.faces:
|
||||
for i in [0,1,2]:
|
||||
j = (i+1) % 3
|
||||
glVertex3f(*f[i])
|
||||
glVertex3f(*f[j])
|
||||
glEnd()
|
||||
glEndList()
|
||||
|
||||
|
||||
def paint(self):
|
||||
shaders.glUseProgram(self.shader)
|
||||
glCallList(self.triList)
|
||||
shaders.glUseProgram(0)
|
||||
#glCallList(self.meshList)
|
@ -6,23 +6,28 @@ import numpy as np
|
||||
__all__ = ['GLVolumeItem']
|
||||
|
||||
class GLVolumeItem(GLGraphicsItem):
|
||||
def __init__(self, data, sliceDensity=1, smooth=True):
|
||||
self.sliceDensity = sliceDensity
|
||||
self.smooth = smooth
|
||||
self.data = data
|
||||
GLGraphicsItem.__init__(self)
|
||||
|
||||
def initializeGL(self):
|
||||
n = 128
|
||||
self.data = np.random.randint(0, 255, size=4*n**3).astype(np.uint8).reshape((n,n,n,4))
|
||||
self.data[...,3] *= 0.1
|
||||
for i in range(n):
|
||||
self.data[i,:,:,0] = i*256./n
|
||||
glEnable(GL_TEXTURE_3D)
|
||||
self.texture = glGenTextures(1)
|
||||
glBindTexture(GL_TEXTURE_3D, self.texture)
|
||||
#glTexImage3D( GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, void *data );
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
|
||||
if self.smooth:
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
|
||||
else:
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER)
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER)
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER)
|
||||
#glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_BORDER_COLOR, ) ## black/transparent by default
|
||||
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, n, n, n, 0, GL_RGBA, GL_UNSIGNED_BYTE, self.data)
|
||||
shape = self.data.shape
|
||||
|
||||
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, shape[0], shape[1], shape[2], 0, GL_RGBA, GL_UNSIGNED_BYTE, self.data.transpose((2,1,0,3)))
|
||||
glDisable(GL_TEXTURE_3D)
|
||||
|
||||
self.lists = {}
|
||||
@ -40,14 +45,16 @@ class GLVolumeItem(GLGraphicsItem):
|
||||
glEnable(GL_TEXTURE_3D)
|
||||
glBindTexture(GL_TEXTURE_3D, self.texture)
|
||||
|
||||
glDisable(GL_DEPTH_TEST)
|
||||
glEnable(GL_DEPTH_TEST)
|
||||
#glDisable(GL_CULL_FACE)
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
||||
glEnable( GL_BLEND )
|
||||
glEnable( GL_ALPHA_TEST )
|
||||
glColor4f(1,1,1,1)
|
||||
|
||||
view = self.view()
|
||||
cam = view.cameraPosition()
|
||||
center = QtGui.QVector3D(*[x/2. for x in self.data.shape[:3]])
|
||||
cam = self.mapFromParent(view.cameraPosition()) - center
|
||||
cam = np.array([cam.x(), cam.y(), cam.z()])
|
||||
ax = np.argmax(abs(cam))
|
||||
d = 1 if cam[ax] > 0 else -1
|
||||
@ -55,7 +62,6 @@ class GLVolumeItem(GLGraphicsItem):
|
||||
glDisable(GL_TEXTURE_3D)
|
||||
|
||||
def drawVolume(self, ax, d):
|
||||
slices = 256
|
||||
N = 5
|
||||
|
||||
imax = [0,1,2]
|
||||
@ -63,31 +69,35 @@ class GLVolumeItem(GLGraphicsItem):
|
||||
|
||||
tp = [[0,0,0],[0,0,0],[0,0,0],[0,0,0]]
|
||||
vp = [[0,0,0],[0,0,0],[0,0,0],[0,0,0]]
|
||||
tp[0][imax[0]] = 0
|
||||
tp[0][imax[1]] = 0
|
||||
tp[1][imax[0]] = 1
|
||||
tp[1][imax[1]] = 0
|
||||
tp[2][imax[0]] = 1
|
||||
tp[2][imax[1]] = 1
|
||||
tp[3][imax[0]] = 0
|
||||
tp[3][imax[1]] = 1
|
||||
nudge = [0.5/x for x in self.data.shape]
|
||||
tp[0][imax[0]] = 0+nudge[imax[0]]
|
||||
tp[0][imax[1]] = 0+nudge[imax[1]]
|
||||
tp[1][imax[0]] = 1-nudge[imax[0]]
|
||||
tp[1][imax[1]] = 0+nudge[imax[1]]
|
||||
tp[2][imax[0]] = 1-nudge[imax[0]]
|
||||
tp[2][imax[1]] = 1-nudge[imax[1]]
|
||||
tp[3][imax[0]] = 0+nudge[imax[0]]
|
||||
tp[3][imax[1]] = 1-nudge[imax[1]]
|
||||
|
||||
vp[0][imax[0]] = -N
|
||||
vp[0][imax[1]] = -N
|
||||
vp[1][imax[0]] = N
|
||||
vp[1][imax[1]] = -N
|
||||
vp[2][imax[0]] = N
|
||||
vp[2][imax[1]] = N
|
||||
vp[3][imax[0]] = -N
|
||||
vp[3][imax[1]] = N
|
||||
vp[0][imax[0]] = 0
|
||||
vp[0][imax[1]] = 0
|
||||
vp[1][imax[0]] = self.data.shape[imax[0]]
|
||||
vp[1][imax[1]] = 0
|
||||
vp[2][imax[0]] = self.data.shape[imax[0]]
|
||||
vp[2][imax[1]] = self.data.shape[imax[1]]
|
||||
vp[3][imax[0]] = 0
|
||||
vp[3][imax[1]] = self.data.shape[imax[1]]
|
||||
slices = self.data.shape[ax] * self.sliceDensity
|
||||
r = range(slices)
|
||||
if d == -1:
|
||||
r = r[::-1]
|
||||
|
||||
glBegin(GL_QUADS)
|
||||
tzVals = np.linspace(nudge[ax], 1.0-nudge[ax], slices)
|
||||
vzVals = np.linspace(0, self.data.shape[ax], slices)
|
||||
for i in r:
|
||||
z = float(i)/(slices-1.)
|
||||
w = float(i)*10./(slices-1.) - 5.
|
||||
z = tzVals[i]
|
||||
w = vzVals[i]
|
||||
|
||||
tp[0][ax] = z
|
||||
tp[1][ax] = z
|
||||
|
41
opengl/shaders.py
Normal file
41
opengl/shaders.py
Normal file
@ -0,0 +1,41 @@
|
||||
from OpenGL.GL import *
|
||||
from OpenGL.GL import shaders
|
||||
|
||||
## For centralizing and managing vertex/fragment shader programs.
|
||||
|
||||
|
||||
Shaders = {
|
||||
'balloon': ( ## increases fragment alpha as the normal turns orthogonal to the view
|
||||
"""
|
||||
varying vec3 normal;
|
||||
void main() {
|
||||
normal = normalize(gl_NormalMatrix * gl_Normal);
|
||||
//vec4 color = normal;
|
||||
//normal.w = min(color.w + 2.0 * color.w * pow(normal.x*normal.x + normal.y*normal.y, 2.0), 1.0);
|
||||
gl_FrontColor = gl_Color;
|
||||
gl_BackColor = gl_Color;
|
||||
gl_Position = ftransform();
|
||||
}
|
||||
""",
|
||||
"""
|
||||
varying vec3 normal;
|
||||
void main() {
|
||||
vec4 color = gl_Color;
|
||||
color.w = min(color.w + 2.0 * color.w * pow(normal.x*normal.x + normal.y*normal.y, 5.0), 1.0);
|
||||
gl_FragColor = color;
|
||||
}
|
||||
"""
|
||||
),
|
||||
}
|
||||
CompiledShaders = {}
|
||||
|
||||
def getShader(name):
|
||||
global Shaders, CompiledShaders
|
||||
|
||||
if name not in CompiledShaders:
|
||||
vshader, fshader = Shaders[name]
|
||||
vcomp = shaders.compileShader(vshader, GL_VERTEX_SHADER)
|
||||
fcomp = shaders.compileShader(fshader, GL_FRAGMENT_SHADER)
|
||||
prog = shaders.compileProgram(vcomp, fcomp)
|
||||
CompiledShaders[name] = prog, vcomp, fcomp
|
||||
return CompiledShaders[name][0]
|
Loading…
Reference in New Issue
Block a user