Move graphics compression routines to new module
							parent
							
								
									d737adb1b8
								
							
						
					
					
						commit
						b318ebc221
					
				| 
						 | 
					@ -0,0 +1,129 @@
 | 
				
			||||||
 | 
					# piker: trading gear for hackers
 | 
				
			||||||
 | 
					# Copyright (C) Tyler Goodlet (in stewardship for pikers)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					# it under the terms of the GNU Affero General Public License as published by
 | 
				
			||||||
 | 
					# the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					# (at your option) any later version.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					# but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					# GNU Affero General Public License for more details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# You should have received a copy of the GNU Affero General Public License
 | 
				
			||||||
 | 
					# along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					import numpy as np
 | 
				
			||||||
 | 
					from numba import (
 | 
				
			||||||
 | 
					    jit, float64, optional, int64,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def downsample(
 | 
				
			||||||
 | 
					    x: np.ndarray,
 | 
				
			||||||
 | 
					    y: np.ndarray,
 | 
				
			||||||
 | 
					    bins: int,
 | 
				
			||||||
 | 
					    method: str = 'peak',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					) -> tuple[np.ndarray, np.ndarray]:
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    Downsample x/y data for lesser curve graphics gen.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The "peak" method is originally copied verbatim from
 | 
				
			||||||
 | 
					    ``pyqtgraph.PlotDataItem.getDisplayDataset()``.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    # py3.10 syntax
 | 
				
			||||||
 | 
					    match method:
 | 
				
			||||||
 | 
					        case 'peak':
 | 
				
			||||||
 | 
					            ds = bins
 | 
				
			||||||
 | 
					            n = len(x) // ds
 | 
				
			||||||
 | 
					            x1 = np.empty((n, 2))
 | 
				
			||||||
 | 
					            # start of x-values; try to select a somewhat centered point
 | 
				
			||||||
 | 
					            stx = ds//2
 | 
				
			||||||
 | 
					            x1[:] = x[stx:stx+n*ds:ds, np.newaxis]
 | 
				
			||||||
 | 
					            x = x1.reshape(n*2)
 | 
				
			||||||
 | 
					            y1 = np.empty((n, 2))
 | 
				
			||||||
 | 
					            y2 = y[:n*ds].reshape((n, ds))
 | 
				
			||||||
 | 
					            y1[:, 0] = y2.max(axis=1)
 | 
				
			||||||
 | 
					            y1[:, 1] = y2.min(axis=1)
 | 
				
			||||||
 | 
					            y = y1.reshape(n*2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case '4px':
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # Ex. from infinite on downsampling viewable graphics.
 | 
				
			||||||
 | 
					            # "one thing i remembered about the binning - if you are
 | 
				
			||||||
 | 
					            # picking a range within your timeseries the start and end bin
 | 
				
			||||||
 | 
					            # should be one more bin size outside the visual range, then
 | 
				
			||||||
 | 
					            # you get better visual fidelity at the edges of the graph"
 | 
				
			||||||
 | 
					            # "i didn't show it in the sample code, but it's accounted for
 | 
				
			||||||
 | 
					            # in the start and end indices and number of bins"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            def build_subchart(
 | 
				
			||||||
 | 
					                self,
 | 
				
			||||||
 | 
					                subchart,
 | 
				
			||||||
 | 
					                width,  # width of screen?
 | 
				
			||||||
 | 
					                chart_type,
 | 
				
			||||||
 | 
					                lower,  # x start?
 | 
				
			||||||
 | 
					                upper,  # x end?
 | 
				
			||||||
 | 
					                xvals,
 | 
				
			||||||
 | 
					                yvals
 | 
				
			||||||
 | 
					            ):
 | 
				
			||||||
 | 
					                pts_per_pixel = len(xvals) / width
 | 
				
			||||||
 | 
					                if pts_per_pixel > 1:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    # this is mutated in-place
 | 
				
			||||||
 | 
					                    data = np.zeros((width, 4), yvals.dtype)
 | 
				
			||||||
 | 
					                    bins = np.zeros(width, xvals.dtype)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    nb = subset_by_x(
 | 
				
			||||||
 | 
					                        xvals,
 | 
				
			||||||
 | 
					                        yvals,
 | 
				
			||||||
 | 
					                        bins,
 | 
				
			||||||
 | 
					                        data,
 | 
				
			||||||
 | 
					                        lower,
 | 
				
			||||||
 | 
					                        # this is scaling the x-range by
 | 
				
			||||||
 | 
					                        # the width of the screen?
 | 
				
			||||||
 | 
					                        (upper-lower)/float(width),
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return x, y
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@jit(nopython=True)
 | 
				
			||||||
 | 
					def subset_by_x(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    xs: np.ndarray,
 | 
				
			||||||
 | 
					    ys: np.ndarray,
 | 
				
			||||||
 | 
					    bins: np.ndarray,
 | 
				
			||||||
 | 
					    data: np.ndarray,
 | 
				
			||||||
 | 
					    x_start: int,
 | 
				
			||||||
 | 
					    step: float,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					) -> int:
 | 
				
			||||||
 | 
					    count = len(xs)
 | 
				
			||||||
 | 
					    # nbins = len(bins)
 | 
				
			||||||
 | 
					    bincount = 0
 | 
				
			||||||
 | 
					    x_left = start
 | 
				
			||||||
 | 
					    # Find the first bin
 | 
				
			||||||
 | 
					    while xs[0] >= x_left + step:
 | 
				
			||||||
 | 
					        x_left += step
 | 
				
			||||||
 | 
					    bins[bincount] = x_left
 | 
				
			||||||
 | 
					    data[bincount] = ys[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for i in range(count):
 | 
				
			||||||
 | 
					        x = xs[i]
 | 
				
			||||||
 | 
					        y = ys[i]
 | 
				
			||||||
 | 
					        if x < x_left + step:   # Interval is [bin, bin+1)
 | 
				
			||||||
 | 
					            data[bincount, 1] = min(y, data[bincount, 1])
 | 
				
			||||||
 | 
					            data[bincount, 2] = max(y, data[bincount, 2])
 | 
				
			||||||
 | 
					            data[bincount, 3] = y
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            # Find the next bin
 | 
				
			||||||
 | 
					            while x >= x_left + step:
 | 
				
			||||||
 | 
					                x_left += step
 | 
				
			||||||
 | 
					            bincount += 1
 | 
				
			||||||
 | 
					            bins[bincount] = x_left
 | 
				
			||||||
 | 
					            data[bincount] = y
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return bincount
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
# piker: trading gear for hackers
 | 
					# piker: trading gear for hackers
 | 
				
			||||||
# Copyright (C) Tyler Goodlet (in stewardship for piker0)
 | 
					# Copyright (C) Tyler Goodlet (in stewardship for pikers)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# This program is free software: you can redistribute it and/or modify
 | 
					# This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
# it under the terms of the GNU Affero General Public License as published by
 | 
					# it under the terms of the GNU Affero General Public License as published by
 | 
				
			||||||
| 
						 | 
					@ -95,38 +95,6 @@ _line_styles: dict[str, int] = {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def downsample(
 | 
					 | 
				
			||||||
    x: np.ndarray,
 | 
					 | 
				
			||||||
    y: np.ndarray,
 | 
					 | 
				
			||||||
    bins: int,
 | 
					 | 
				
			||||||
    method: str = 'peak',
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
) -> tuple[np.ndarray, np.ndarray]:
 | 
					 | 
				
			||||||
    '''
 | 
					 | 
				
			||||||
    Downsample x/y data for lesser curve graphics gen.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    The "peak" method is originally copied verbatim from
 | 
					 | 
				
			||||||
    ``pyqtgraph.PlotDataItem.getDisplayDataset()``.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    '''
 | 
					 | 
				
			||||||
    match method:
 | 
					 | 
				
			||||||
        case 'peak':
 | 
					 | 
				
			||||||
            ds = bins
 | 
					 | 
				
			||||||
            n = len(x) // ds
 | 
					 | 
				
			||||||
            x1 = np.empty((n, 2))
 | 
					 | 
				
			||||||
            # start of x-values; try to select a somewhat centered point
 | 
					 | 
				
			||||||
            stx = ds//2
 | 
					 | 
				
			||||||
            x1[:] = x[stx:stx+n*ds:ds, np.newaxis]
 | 
					 | 
				
			||||||
            x = x1.reshape(n*2)
 | 
					 | 
				
			||||||
            y1 = np.empty((n, 2))
 | 
					 | 
				
			||||||
            y2 = y[:n*ds].reshape((n, ds))
 | 
					 | 
				
			||||||
            y1[:, 0] = y2.max(axis=1)
 | 
					 | 
				
			||||||
            y1[:, 1] = y2.min(axis=1)
 | 
					 | 
				
			||||||
            y = y1.reshape(n*2)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return x, y
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# TODO: got a feeling that dropping this inheritance gets us even more speedups
 | 
					# TODO: got a feeling that dropping this inheritance gets us even more speedups
 | 
				
			||||||
class FastAppendCurve(pg.PlotCurveItem):
 | 
					class FastAppendCurve(pg.PlotCurveItem):
 | 
				
			||||||
    '''
 | 
					    '''
 | 
				
			||||||
| 
						 | 
					@ -305,8 +273,6 @@ class FastAppendCurve(pg.PlotCurveItem):
 | 
				
			||||||
                    finiteCheck=False,
 | 
					                    finiteCheck=False,
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            path = self.path
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # other merging ideas:
 | 
					            # other merging ideas:
 | 
				
			||||||
            # https://stackoverflow.com/questions/8936225/how-to-merge-qpainterpaths
 | 
					            # https://stackoverflow.com/questions/8936225/how-to-merge-qpainterpaths
 | 
				
			||||||
            if self._step_mode:
 | 
					            if self._step_mode:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue