| 22 | |
| 23 | # A class that will downsample the data and recompute when zoomed. |
| 24 | class DataDisplayDownsampler: |
| 25 | def __init__(self, xdata, y1data, y2data): |
| 26 | self.origY1Data = y1data |
| 27 | self.origY2Data = y2data |
| 28 | self.origXData = xdata |
| 29 | self.max_points = 50 |
| 30 | self.delta = xdata[-1] - xdata[0] |
| 31 | |
| 32 | def plot(self, ax): |
| 33 | x, y1, y2 = self._downsample(self.origXData.min(), self.origXData.max()) |
| 34 | (self.line,) = ax.plot(x, y1, 'o-') |
| 35 | self.poly_collection = ax.fill_between(x, y1, y2, step="pre", color="r") |
| 36 | |
| 37 | def _downsample(self, xstart, xend): |
| 38 | # get the points in the view range |
| 39 | mask = (self.origXData > xstart) & (self.origXData < xend) |
| 40 | # dilate the mask by one to catch the points just outside |
| 41 | # of the view range to not truncate the line |
| 42 | mask = np.convolve([1, 1, 1], mask, mode='same').astype(bool) |
| 43 | # sort out how many points to drop |
| 44 | ratio = max(np.sum(mask) // self.max_points, 1) |
| 45 | |
| 46 | # mask data |
| 47 | xdata = self.origXData[mask] |
| 48 | y1data = self.origY1Data[mask] |
| 49 | y2data = self.origY2Data[mask] |
| 50 | |
| 51 | # downsample data |
| 52 | xdata = xdata[::ratio] |
| 53 | y1data = y1data[::ratio] |
| 54 | y2data = y2data[::ratio] |
| 55 | |
| 56 | print(f"using {len(y1data)} of {np.sum(mask)} visible points") |
| 57 | |
| 58 | return xdata, y1data, y2data |
| 59 | |
| 60 | def update(self, ax): |
| 61 | # Update the artists |
| 62 | lims = ax.viewLim |
| 63 | if abs(lims.width - self.delta) > 1e-8: |
| 64 | self.delta = lims.width |
| 65 | xstart, xend = lims.intervalx |
| 66 | x, y1, y2 = self._downsample(xstart, xend) |
| 67 | self.line.set_data(x, y1) |
| 68 | self.poly_collection.set_data(x, y1, y2, step="pre") |
| 69 | ax.figure.canvas.draw_idle() |
| 70 | |
| 71 | |
| 72 | # Create a signal |
no outgoing calls
no test coverage detected
searching dependent graphs…