Allow GLMeshItem to draw edges from MeshData with face-indexed vertexes.
This commit is contained in:
parent
c8f03e828e
commit
00418e4921
@ -67,6 +67,7 @@ pyqtgraph-0.9.9 [unreleased]
|
|||||||
- Fixed PySide crash caused by emitting signal from GraphicsObject.itemChange
|
- Fixed PySide crash caused by emitting signal from GraphicsObject.itemChange
|
||||||
- Fixed possible infinite loop from FiniteCache
|
- Fixed possible infinite loop from FiniteCache
|
||||||
- Allow images with NaN in ImageView
|
- Allow images with NaN in ImageView
|
||||||
|
- MeshData can generate edges from face-indexed vertexes
|
||||||
|
|
||||||
pyqtgraph-0.9.8 2013-11-24
|
pyqtgraph-0.9.8 2013-11-24
|
||||||
|
|
||||||
|
@ -53,19 +53,24 @@ m1.translate(5, 5, 0)
|
|||||||
m1.setGLOptions('additive')
|
m1.setGLOptions('additive')
|
||||||
w.addItem(m1)
|
w.addItem(m1)
|
||||||
|
|
||||||
|
|
||||||
## Example 2:
|
## Example 2:
|
||||||
## Array of vertex positions, three per face
|
## Array of vertex positions, three per face
|
||||||
|
verts = np.empty((36, 3, 3), dtype=np.float32)
|
||||||
|
theta = np.linspace(0, 2*np.pi, 37)[:-1]
|
||||||
|
verts[:,0] = np.vstack([2*np.cos(theta), 2*np.sin(theta), [0]*36]).T
|
||||||
|
verts[:,1] = np.vstack([4*np.cos(theta+0.2), 4*np.sin(theta+0.2), [-1]*36]).T
|
||||||
|
verts[:,2] = np.vstack([4*np.cos(theta-0.2), 4*np.sin(theta-0.2), [1]*36]).T
|
||||||
|
|
||||||
## Colors are specified per-vertex
|
## Colors are specified per-vertex
|
||||||
|
|
||||||
verts = verts[faces] ## Same mesh geometry as example 2, but now we are passing in 12 vertexes
|
|
||||||
colors = np.random.random(size=(verts.shape[0], 3, 4))
|
colors = np.random.random(size=(verts.shape[0], 3, 4))
|
||||||
#colors[...,3] = 1.0
|
m2 = gl.GLMeshItem(vertexes=verts, vertexColors=colors, smooth=False, shader='balloon',
|
||||||
|
drawEdges=True, edgeColor=(1, 1, 0, 1))
|
||||||
m2 = gl.GLMeshItem(vertexes=verts, vertexColors=colors, smooth=False, shader='balloon')
|
|
||||||
m2.translate(-5, 5, 0)
|
m2.translate(-5, 5, 0)
|
||||||
w.addItem(m2)
|
w.addItem(m2)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Example 3:
|
## Example 3:
|
||||||
## sphere
|
## sphere
|
||||||
|
|
||||||
@ -79,7 +84,7 @@ colors[:,1] = np.linspace(0, 1, colors.shape[0])
|
|||||||
md.setFaceColors(colors)
|
md.setFaceColors(colors)
|
||||||
m3 = gl.GLMeshItem(meshdata=md, smooth=False)#, shader='balloon')
|
m3 = gl.GLMeshItem(meshdata=md, smooth=False)#, shader='balloon')
|
||||||
|
|
||||||
m3.translate(-5, -5, 0)
|
m3.translate(5, -5, 0)
|
||||||
w.addItem(m3)
|
w.addItem(m3)
|
||||||
|
|
||||||
|
|
||||||
@ -115,45 +120,6 @@ w.addItem(m6)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
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..")
|
|
||||||
# verts = pg.isosurface(data, data.max()/4.)
|
|
||||||
# print dir(gl.MeshData)
|
|
||||||
# md = gl.GLMeshItem(vertexes=verts)
|
|
||||||
#
|
|
||||||
# colors = np.ones((md.vertexes(indexed='faces').shape[0], 4), dtype=float)
|
|
||||||
# colors[:,3] = 0.3
|
|
||||||
# colors[:,2] = np.linspace(0, 1, colors.shape[0])
|
|
||||||
# m1 = gl.GLMeshItem(meshdata=md, color=colors, smooth=False)
|
|
||||||
#
|
|
||||||
# w.addItem(m1)
|
|
||||||
# m1.translate(-25, -25, -20)
|
|
||||||
#
|
|
||||||
# m2 = gl.GLMeshItem(vertexes=verts, color=colors, smooth=True)
|
|
||||||
#
|
|
||||||
# w.addItem(m2)
|
|
||||||
# m2.translate(-25, -25, -50)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Start Qt event loop unless running in interactive mode.
|
## Start Qt event loop unless running in interactive mode.
|
||||||
|
@ -84,64 +84,11 @@ class MeshData(object):
|
|||||||
if faceColors is not None:
|
if faceColors is not None:
|
||||||
self.setFaceColors(faceColors)
|
self.setFaceColors(faceColors)
|
||||||
|
|
||||||
#self.setFaces(vertexes=vertexes, faces=faces, vertexColors=vertexColors, faceColors=faceColors)
|
|
||||||
|
|
||||||
|
|
||||||
#def setFaces(self, vertexes=None, faces=None, vertexColors=None, faceColors=None):
|
|
||||||
#"""
|
|
||||||
#Set the faces in this data set.
|
|
||||||
#Data may be provided either as an Nx3x3 array of floats (9 float coordinate values per face)::
|
|
||||||
|
|
||||||
#faces = [ [(x, y, z), (x, y, z), (x, y, z)], ... ]
|
|
||||||
|
|
||||||
#or as an Nx3 array of ints (vertex integers) AND an Mx3 array of floats (3 float coordinate values per vertex)::
|
|
||||||
|
|
||||||
#faces = [ (p1, p2, p3), ... ]
|
|
||||||
#vertexes = [ (x, y, z), ... ]
|
|
||||||
|
|
||||||
#"""
|
|
||||||
#if not isinstance(vertexes, np.ndarray):
|
|
||||||
#vertexes = np.array(vertexes)
|
|
||||||
#if vertexes.dtype != np.float:
|
|
||||||
#vertexes = vertexes.astype(float)
|
|
||||||
#if faces is None:
|
|
||||||
#self._setIndexedFaces(vertexes, vertexColors, faceColors)
|
|
||||||
#else:
|
|
||||||
#self._setUnindexedFaces(faces, vertexes, vertexColors, faceColors)
|
|
||||||
##print self.vertexes().shape
|
|
||||||
##print self.faces().shape
|
|
||||||
|
|
||||||
|
|
||||||
#def setMeshColor(self, color):
|
|
||||||
#"""Set the color of the entire mesh. This removes any per-face or per-vertex colors."""
|
|
||||||
#color = fn.Color(color)
|
|
||||||
#self._meshColor = color.glColor()
|
|
||||||
#self._vertexColors = None
|
|
||||||
#self._faceColors = None
|
|
||||||
|
|
||||||
|
|
||||||
#def __iter__(self):
|
|
||||||
#"""Iterate over all faces, yielding a list of three tuples [(position, normal, color), ...] for each face."""
|
|
||||||
#vnorms = self.vertexNormals()
|
|
||||||
#vcolors = self.vertexColors()
|
|
||||||
#for i in range(self._faces.shape[0]):
|
|
||||||
#face = []
|
|
||||||
#for j in [0,1,2]:
|
|
||||||
#vind = self._faces[i,j]
|
|
||||||
#pos = self._vertexes[vind]
|
|
||||||
#norm = vnorms[vind]
|
|
||||||
#if vcolors is None:
|
|
||||||
#color = self._meshColor
|
|
||||||
#else:
|
|
||||||
#color = vcolors[vind]
|
|
||||||
#face.append((pos, norm, color))
|
|
||||||
#yield face
|
|
||||||
|
|
||||||
#def __len__(self):
|
|
||||||
#return len(self._faces)
|
|
||||||
|
|
||||||
def faces(self):
|
def faces(self):
|
||||||
"""Return an array (Nf, 3) of vertex indexes, three per triangular face in the mesh."""
|
"""Return an array (Nf, 3) of vertex indexes, three per triangular face in the mesh.
|
||||||
|
|
||||||
|
If faces have not been computed for this mesh, the function returns None.
|
||||||
|
"""
|
||||||
return self._faces
|
return self._faces
|
||||||
|
|
||||||
def edges(self):
|
def edges(self):
|
||||||
@ -162,8 +109,6 @@ class MeshData(object):
|
|||||||
self._vertexColorsIndexedByFaces = None
|
self._vertexColorsIndexedByFaces = None
|
||||||
self._faceColorsIndexedByFaces = None
|
self._faceColorsIndexedByFaces = None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def vertexes(self, indexed=None):
|
def vertexes(self, indexed=None):
|
||||||
"""Return an array (N,3) of the positions of vertexes in the mesh.
|
"""Return an array (N,3) of the positions of vertexes in the mesh.
|
||||||
By default, each unique vertex appears only once in the array.
|
By default, each unique vertex appears only once in the array.
|
||||||
@ -208,7 +153,6 @@ class MeshData(object):
|
|||||||
self._faceNormals = None
|
self._faceNormals = None
|
||||||
self._faceNormalsIndexedByFaces = None
|
self._faceNormalsIndexedByFaces = None
|
||||||
|
|
||||||
|
|
||||||
def hasFaceIndexedData(self):
|
def hasFaceIndexedData(self):
|
||||||
"""Return True if this object already has vertex positions indexed by face"""
|
"""Return True if this object already has vertex positions indexed by face"""
|
||||||
return self._vertexesIndexedByFaces is not None
|
return self._vertexesIndexedByFaces is not None
|
||||||
@ -230,7 +174,6 @@ class MeshData(object):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def faceNormals(self, indexed=None):
|
def faceNormals(self, indexed=None):
|
||||||
"""
|
"""
|
||||||
Return an array (Nf, 3) of normal vectors for each face.
|
Return an array (Nf, 3) of normal vectors for each face.
|
||||||
@ -366,7 +309,6 @@ class MeshData(object):
|
|||||||
## This is done by collapsing into a list of 'unique' vertexes (difference < 1e-14)
|
## This is done by collapsing into a list of 'unique' vertexes (difference < 1e-14)
|
||||||
|
|
||||||
## I think generally this should be discouraged..
|
## I think generally this should be discouraged..
|
||||||
|
|
||||||
faces = self._vertexesIndexedByFaces
|
faces = self._vertexesIndexedByFaces
|
||||||
verts = {} ## used to remember the index of each vertex position
|
verts = {} ## used to remember the index of each vertex position
|
||||||
self._faces = np.empty(faces.shape[:2], dtype=np.uint)
|
self._faces = np.empty(faces.shape[:2], dtype=np.uint)
|
||||||
@ -427,8 +369,8 @@ class MeshData(object):
|
|||||||
#pass
|
#pass
|
||||||
|
|
||||||
def _computeEdges(self):
|
def _computeEdges(self):
|
||||||
|
if not self.hasFaceIndexedData:
|
||||||
## generate self._edges from self._faces
|
## generate self._edges from self._faces
|
||||||
#print self._faces
|
|
||||||
nf = len(self._faces)
|
nf = len(self._faces)
|
||||||
edges = np.empty(nf*3, dtype=[('i', np.uint, 2)])
|
edges = np.empty(nf*3, dtype=[('i', np.uint, 2)])
|
||||||
edges['i'][0:nf] = self._faces[:,:2]
|
edges['i'][0:nf] = self._faces[:,:2]
|
||||||
@ -443,6 +385,19 @@ class MeshData(object):
|
|||||||
# remove duplicate entries
|
# remove duplicate entries
|
||||||
self._edges = np.unique(edges)['i']
|
self._edges = np.unique(edges)['i']
|
||||||
#print self._edges
|
#print self._edges
|
||||||
|
elif self._vertexesIndexedByFaces is not None:
|
||||||
|
verts = self._vertexesIndexedByFaces
|
||||||
|
edges = np.empty((verts.shape[0], 3, 2), dtype=np.uint)
|
||||||
|
nf = verts.shape[0]
|
||||||
|
edges[:,0,0] = np.arange(nf) * 3
|
||||||
|
edges[:,0,1] = edges[:,0,0] + 1
|
||||||
|
edges[:,1,0] = edges[:,0,1]
|
||||||
|
edges[:,1,1] = edges[:,1,0] + 1
|
||||||
|
edges[:,2,0] = edges[:,1,1]
|
||||||
|
edges[:,2,1] = edges[:,0,0]
|
||||||
|
self._edges = edges
|
||||||
|
else:
|
||||||
|
raise Exception("MeshData cannot generate edges--no faces in this data.")
|
||||||
|
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
|
@ -153,8 +153,12 @@ class GLMeshItem(GLGraphicsItem):
|
|||||||
self.colors = md.faceColors(indexed='faces')
|
self.colors = md.faceColors(indexed='faces')
|
||||||
|
|
||||||
if self.opts['drawEdges']:
|
if self.opts['drawEdges']:
|
||||||
|
if not md.hasFaceIndexedData():
|
||||||
self.edges = md.edges()
|
self.edges = md.edges()
|
||||||
self.edgeVerts = md.vertexes()
|
self.edgeVerts = md.vertexes()
|
||||||
|
else:
|
||||||
|
self.edges = md.edges()
|
||||||
|
self.edgeVerts = md.vertexes(indexed='faces')
|
||||||
return
|
return
|
||||||
|
|
||||||
def paint(self):
|
def paint(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user