Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MRG: Better mesh options #1329

Merged
merged 6 commits into from Sep 25, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 14 additions & 2 deletions examples/basics/visuals/mesh.py
Expand Up @@ -31,11 +31,12 @@ def __init__(self):
# Because vertices are pre-indexed, we get a different color
# every time a vertex is visited, resulting in sharp color
# differences between edges.
rng = np.random.RandomState(0)
verts = mdata.get_vertices(indexed='faces')
nf = verts.size//9
fcolor = np.ones((nf, 3, 4), dtype=np.float32)
fcolor[..., 0] = np.linspace(1, 0, nf)[:, np.newaxis]
fcolor[..., 1] = np.random.normal(size=nf)[:, np.newaxis]
fcolor[..., 1] = rng.randn(nf, 1)
fcolor[..., 2] = np.linspace(0, 1, nf)[:, np.newaxis]
mesh = visuals.MeshVisual(vertices=verts, face_colors=fcolor)
self.meshes.append(mesh)
Expand All @@ -49,14 +50,25 @@ def __init__(self):
nv = verts.size//3
vcolor = np.ones((nv, 4), dtype=np.float32)
vcolor[:, 0] = np.linspace(1, 0, nv)
vcolor[:, 1] = np.random.normal(size=nv)
vcolor[:, 1] = rng.randn(nv)
vcolor[:, 2] = np.linspace(0, 1, nv)
self.meshes.append(visuals.MeshVisual(verts, faces, vcolor))
self.meshes.append(visuals.MeshVisual(verts, faces, vcolor,
shading='flat'))
self.meshes.append(visuals.MeshVisual(verts, faces, vcolor,
shading='smooth'))

# Mesh with color indexed into a colormap
verts = mdata.get_vertices(None)
faces = mdata.get_faces()
values = rng.randn(len(verts))
mesh = visuals.MeshVisual(vertices=verts, faces=faces,
vertex_values=values, shading='smooth')
mesh.clim = [-1, 1]
mesh.cmap = 'viridis'
mesh.shininess = 0.01
self.meshes.append(mesh)

# Lay out meshes in a grid
grid = (3, 3)
s = 300. / max(grid)
Expand Down
29 changes: 28 additions & 1 deletion vispy/color/colormap.py
Expand Up @@ -672,6 +672,32 @@ def __init__(self, h_pos=20, h_neg=250, saturation=1.0, value=0.7,

super(_Diverging, self).__init__(colors)


class _RedYellowBlueCyan(Colormap):
"""A colormap which is goes red-yellow positive and blue-cyan negative

Parameters
---------
limits : array-like, optional
The limits for the fully transparent, opaque red, and yellow points.
"""

def __init__(self, limits=(0.33, 0.66, 1.0)):
limits = np.array(limits, float).ravel()
if len(limits) != 3:
raise ValueError('limits must have 3 values')
if (np.diff(limits) < 0).any() or (limits <= 0).any():
raise ValueError('limits must be strictly increasing and positive')
controls = np.array([-limits[2], -limits[1], -limits[0],
limits[0], limits[1], limits[2]])
controls = ((controls / limits[2]) + 1) / 2.
colors = [(0., 1., 1., 1.), (0., 0., 1., 1.), (0., 0., 1., 0.),
(1., 0., 0., 0.), (1., 0., 0., 1.), (1., 1., 0., 1.)]
colors = ColorArray(colors)
super(_RedYellowBlueCyan, self).__init__(
colors, controls=controls, interpolation='linear')


# https://github.com/matplotlib/matplotlib/pull/4707/files#diff-893cf0348279e9f4570488a7a297ab1eR774 # noqa
# Taken from original Viridis colormap data in matplotlib implementation
# Sampled 128 points from the raw data-set of 256 samples.
Expand Down Expand Up @@ -980,7 +1006,8 @@ def __init__(self, h_pos=20, h_neg=250, saturation=1.0, value=0.7,
single_hue=_SingleHue,
hsl=_HSL,
husl=_HUSL,
diverging=_Diverging
diverging=_Diverging,
RdYeBuCy=_RedYellowBlueCyan,
)


Expand Down
106 changes: 84 additions & 22 deletions vispy/geometry/meshdata.py
Expand Up @@ -39,6 +39,8 @@ class MeshData(object):
interpreted as (Nf, 3, 4) array of colors.
face_colors : ndarray, shape (Nf, 4)
Face colors.
vertex_values : ndarray, shape (Nv,)
Vertex values.

Notes
-----
Expand All @@ -63,7 +65,7 @@ class MeshData(object):
"""

def __init__(self, vertices=None, faces=None, edges=None,
vertex_colors=None, face_colors=None):
vertex_colors=None, face_colors=None, vertex_values=None):
self._vertices = None # (Nv,3) array of vertex coordinates
self._vertices_indexed_by_faces = None # (Nf, 3, 3) vertex coordinates
self._vertices_indexed_by_edges = None # (Ne, 2, 3) vertex coordinates
Expand All @@ -78,11 +80,14 @@ def __init__(self, vertices=None, faces=None, edges=None,
self._vertex_edges = None # maps vertex ID to a list of edge IDs

# Per-vertex data
self._vertex_normals = None # (Nv, 3) normals
self._vertex_normals = None # (Nv, 3) normals
self._vertex_normals_indexed_by_faces = None # (Nf, 3, 3) normals
self._vertex_colors = None # (Nv, 3) colors
self._vertex_colors = None # (Nv, 4) colors
self._vertex_colors_indexed_by_faces = None # (Nf, 3, 4) colors
self._vertex_colors_indexed_by_edges = None # (Nf, 2, 4) colors
self._vertex_values = None # (Nv,) values
self._vertex_values_indexed_by_faces = None # (Nv, 3) values
self._vertex_values_indexed_by_edges = None # (Nv, 2) values

# Per-face data
self._face_normals = None # (Nf, 3) face normals
Expand All @@ -94,23 +99,17 @@ def __init__(self, vertices=None, faces=None, edges=None,
# Per-edge data
self._edge_colors = None # (Ne, 4) edge colors
self._edge_colors_indexed_by_edges = None # (Ne, 2, 4) edge colors
# default color to use if no face/edge/vertex colors are given
# self._meshColor = (1, 1, 1, 0.1)

if vertices is not None:
if faces is None:
self.set_vertices(vertices, indexed='faces')
if vertex_colors is not None:
self.set_vertex_colors(vertex_colors, indexed='faces')
if face_colors is not None:
self.set_face_colors(face_colors, indexed='faces')
else:
self.set_vertices(vertices)
indexed = 'faces' if faces is None else None
self.set_vertices(vertices, indexed=indexed)
if faces is not None:
self.set_faces(faces)
if vertex_colors is not None:
self.set_vertex_colors(vertex_colors)
if face_colors is not None:
self.set_face_colors(face_colors)
if vertex_colors is not None:
self.set_vertex_colors(vertex_colors, indexed=indexed)
if face_colors is not None:
self.set_face_colors(face_colors, indexed=indexed)
if vertex_values is not None:
self.set_vertex_values(vertex_values, indexed=indexed)

def get_faces(self):
"""Array (Nf, 3) of vertex indices, three per triangular face.
Expand All @@ -121,7 +120,7 @@ def get_faces(self):

def get_edges(self, indexed=None):
"""Edges of the mesh

Parameters
----------
indexed : str | None
Expand All @@ -135,7 +134,7 @@ def get_edges(self, indexed=None):
edges : ndarray
The edges.
"""

if indexed is None:
if self._edges is None:
self._compute_edges(indexed=None)
Expand Down Expand Up @@ -213,7 +212,7 @@ def get_bounds(self):
return None
bounds = [(v[:, ax].min(), v[:, ax].max()) for ax in range(v.shape[1])]
return bounds

def set_vertices(self, verts=None, indexed=None, reset_normals=True):
"""Set the mesh vertices

Expand Down Expand Up @@ -265,6 +264,14 @@ def has_vertex_color(self):
return True
return False

def has_vertex_value(self):
"""Return True if this data set has vertex value information"""
for v in (self._vertex_values, self._vertex_values_indexed_by_faces,
self._vertex_values_indexed_by_edges):
if v is not None:
return True
return False

def has_face_color(self):
"""Return True if this data set has face color information"""
for v in (self._face_colors, self._face_colors_indexed_by_faces,
Expand Down Expand Up @@ -370,6 +377,31 @@ def get_vertex_colors(self, indexed=None):
else:
raise Exception("Invalid indexing mode. Accepts: None, 'faces'")

def get_vertex_values(self, indexed=None):
"""Get vertex colors

Parameters
----------
indexed : str | None
If None, return an array (Nv,) of vertex values.
If indexed=='faces', then instead return an indexed array
(Nf, 3).

Returns
-------
values : ndarray
The vertex values.
"""
if indexed is None:
return self._vertex_values
elif indexed == 'faces':
if self._vertex_values_indexed_by_faces is None:
self._vertex_values_indexed_by_faces = \
self._vertex_values[self.get_faces()]
return self._vertex_values_indexed_by_faces
else:
raise Exception("Invalid indexing mode. Accepts: None, 'faces'")

def set_vertex_colors(self, colors, indexed=None):
"""Set the vertex color array

Expand Down Expand Up @@ -400,6 +432,36 @@ def set_vertex_colors(self, colors, indexed=None):
else:
raise ValueError('indexed must be None or "faces"')

def set_vertex_values(self, values, indexed=None):
"""Set the vertex value array

Parameters
----------
values : array
Array of values. Must have shape (Nv,) (indexing by vertex)
or shape (Nf, 3) (vertices indexed by face).
indexed : str | None
Should be 'faces' if colors are indexed by faces.
"""
values = np.asarray(values)
if indexed is None:
if values.ndim != 1:
raise ValueError('values must be 1D if indexed is None')
if values.shape[0] != self.n_vertices:
raise ValueError('incorrect number of colors %s, expected %s'
% (values.shape[0], self.n_vertices))
self._vertex_values = values
self._vertex_values_indexed_by_faces = None
elif indexed == 'faces':
if values.ndim != 2:
raise ValueError('values must be 3D if indexed is "faces"')
if values.shape[0] != self.n_faces:
raise ValueError('incorrect number of faces')
self._vertex_values = None
self._vertex_values_indexed_by_faces = values
else:
raise ValueError('indexed must be None or "faces"')

def get_face_colors(self, indexed=None):
"""Get the face colors

Expand All @@ -410,7 +472,7 @@ def get_face_colors(self, indexed=None):
If indexed=='faces', then instead return an indexed array
(Nf, 3, 4) (note this is just the same array with each color
repeated three times).

Returns
-------
colors : ndarray
Expand Down