diff --git a/examples/pColorMeshItem.py b/examples/pColorMeshItem.py index a2eec60e..87ab8120 100644 --- a/examples/pColorMeshItem.py +++ b/examples/pColorMeshItem.py @@ -21,40 +21,45 @@ view = win.addViewBox() ## Create data -x = np.array([[1,1,1,1], - [2,2,2,2], - [3,3,3,3], - [4,4,4,4], - [5,5,5,5]]) -y = np.array([[4,8,12,16], - [2,4,6,8], - [3,6,9,12], - [5,10,15,20], - [6,12,18,24]]) -z = np.array([[1,2,3], - [5,6,7], - [9,10,11], - [13,14,15]]) + +# x and y being the coordinates of the polygons, they share the same shape +# However the shape can be different in both dimension +xn = 50 # nb points along x +yn = 40 # nb points along y +x = np.repeat(np.arange(1, xn+1), yn).reshape(xn, yn) +y = np.tile(np.arange(1, yn+1), xn).reshape(xn, yn) + +# z being the color of the polygons its shape must be decreased by one in each dimension +z = np.exp(-(x*xn)**2/1000)[:-1,:-1] ## Create image item -pcmi = pg.PColorMeshItem(x, y, z) +pcmi = pg.PColorMeshItem() view.addItem(pcmi) +## Set the animation +fps = 25 # Frame per second of the animation -fps = 1 -i = 0 +# Wave parameters +wave_amplitude = 3 +wave_speed = 0.3 +wave_length = 10 +color_speed = 0.3 +i=0 def updateData(): - global pcmi, x, y, z, i - - ## Display the data - pcmi.setData(x-i, y, z) - - QtCore.QTimer.singleShot(fps*1000, updateData) - i += 1 - print(i) + global i + ## Display the new data set + new_x = x + new_y = y+wave_amplitude*np.cos(x/wave_length+i) + new_z = np.exp(-(x-np.cos(i*color_speed)*xn)**2/1000)[:-1,:-1] + pcmi.setData(new_x, + new_y, + new_z) + + i += wave_speed + QtCore.QTimer.singleShot(1000/fps, updateData) updateData() diff --git a/pyqtgraph/graphicsItems/PColorMeshItem.py b/pyqtgraph/graphicsItems/PColorMeshItem.py index 09030e32..a71baa2f 100644 --- a/pyqtgraph/graphicsItems/PColorMeshItem.py +++ b/pyqtgraph/graphicsItems/PColorMeshItem.py @@ -23,66 +23,97 @@ class PColorMeshItem(GraphicsObject): """ **Bases:** :class:`GraphicsObject ` - TODO + Create a pseudocolor plot with convex polygons. """ sigImageChanged = QtCore.Signal() sigRemoveRequested = QtCore.Signal(object) # self; emitted when 'remove' is selected from context menu - def __init__(self, x=None, y=None, z=None, cmap='viridis'): + def __init__(self, x=None, y=None, z=None, + cmap='viridis'): """ - See :func:`setImage ` for all allowed initialization arguments. + + + Parameters + ---------- + x, y : np.ndarray + 2D array containing the coordinates of the polygons + z : np.ndarray + 2D array containing the value which will be maped into the polygons + colors. + cmap : str, default 'viridis + Colormap used to map the z value to colors. """ + GraphicsObject.__init__(self) self.x = x self.y = y self.z = z - self.qpicture = None ## rendered image for display + self.qpicture = None ## rendered image for display + self.axisOrder = getConfigOption('imageAxisOrder') - if cmap in list(Gradients.keys()): + if cmap in Gradients.keys(): self.cmap = cmap else: raise NameError('Undefined colormap') - + + # If some data have been sent we directly display it if x is not None and y is not None and z is not None: self.setData(x, y, z) def setData(self, x, y, z): - ## pre-computing a QPicture object allows paint() to run much more quickly, - ## rather than re-drawing the shapes every time. + + + # We test of the view has changed + if np.any(self.x != x) or np.any(self.y != y) or np.any(self.z != z): + self.informViewBoundsChanged() + + # Replace data + self.x = x + self.y = y + self.z = z + profile = debug.Profiler() self.qpicture = QtGui.QPicture() p = QtGui.QPainter(self.qpicture) - p.setPen(fn.mkPen('w')) + # We set the pen of all polygons once + p.setPen(QtGui.QColor(0, 0, 0, 0)) - # Prepare colormap - pos = [i[0] for i in Gradients[self.cmap]['ticks']] + ## Prepare colormap + # First we get the LookupTable + pos = [i[0] for i in Gradients[self.cmap]['ticks']] color = [i[1] for i in Gradients[self.cmap]['ticks']] - cmap = ColorMap(pos, color) - lut = cmap.getLookupTable(0.0, 1.0, 256) - norm = ((z - z.min())/z.max()*len(lut)).astype(int) - - xfn = z.shape[0] - yfn = z.shape[1] - for xi in range(xfn): - for yi in range(yfn): + cmap = ColorMap(pos, color) + lut = cmap.getLookupTable(0.0, 1.0, 256) + # Second we associate each z value, that we normalize, to the lut + norm = z - z.min() + norm = norm/norm.max() + norm = (norm*(len(lut)-1)).astype(int) + + # Go through all the data and draw the polygons accordingly + for xi in range(z.shape[0]): + for yi in range(z.shape[1]): + # Set the color of the polygon first + # print(xi, yi, norm[xi][yi]) + c = lut[norm[xi][yi]] + p.setBrush(QtGui.QColor(c[0], c[1], c[2])) + + # DrawConvexPlygon is faster p.drawConvexPolygon(QtCore.QPointF(x[xi][yi], y[xi][yi]), QtCore.QPointF(x[xi+1][yi], y[xi+1][yi]), QtCore.QPointF(x[xi+1][yi+1], y[xi+1][yi+1]), QtCore.QPointF(x[xi][yi+1], y[xi][yi+1])) - c = lut[norm[xi][yi]] - p.setBrush(QtGui.QColor(c[0], c[1], c[2])) - p.end() + p.end() self.update() @@ -106,14 +137,14 @@ class PColorMeshItem(GraphicsObject): def width(self): if self.x is None: return None - return len(self.x) + return np.max(self.x) def height(self): if self.y is None: return None - return len(self.y) + return np.max(self.y) @@ -122,4 +153,4 @@ class PColorMeshItem(GraphicsObject): if self.z is None: return QtCore.QRectF(0., 0., 0., 0.) - return QtCore.QRectF(0., 0., float(self.width()), float(self.height())) \ No newline at end of file + return QtCore.QRectF(0., 0., float(self.width()), float(self.height()))