leeoniya / uPlot

📈 A small, fast chart for time series, lines, areas, ohlc & bars
MIT License
8.83k stars 385 forks source link

Preserve chart / canvas with paths #951

Open cmrabet opened 6 months ago

cmrabet commented 6 months ago

I'm implementing a real time heatmap that gets > 100k points per minute.

I'm using rectangles to plot each value, using a path builder. For performance issues, I only want to add the last Y (column) of values to the canvas, so I don't have to iterate through a constantly growing array of values. This is working, however the canvas (chart) is getting cleared every iteration, so only the last Y column gets plotted.

I don't want this, I want the previous plotted values to remain. How can I achieve this?

Here is the code:

 private pointPathBuilder(): uPlot.Series.PathBuilder {
    return (u) => {
      const seriesData = u.data as unknown as [number[], number[], number[]];
      const [xValues, yValues, aValues] = seriesData;
      const ctx = u.ctx;
      const xSize = getPixelsPerUnit(u, 'x');
      const ySize = getPixelsPerUnit(u, 'y');

      const dataLength = xValues.length;
      // Plot the last 32 values
      const start = dataLength - this.channels;
      const end = start + this.channels;

      if (dataLength < this.channels) {
        return null;
      }

      // for (let i = 0; i < 32; i++) {
      for (let i = start; i < end; i++) {
        const xValue = xValues[i];
        const yValue = yValues[i] ?? 0;
        const aValue = aValues[i] ?? 0;
        const color = this.interpolateColor(aValue, this.rainBowPalet);

        const x = u.valToPos(xValue, 'x', true);
        const y = u.valToPos(yValue, 'y', true);

        if (!isPointInChart(u, x, y, xSize)) {
          continue;
        }

        ctx.save();
        ctx.fillStyle = color;
        ctx.beginPath();
        ctx.rect(x, y, xSize, -ySize);
        ctx.fill();
        ctx.restore();
      }

      return null;
    };
  }

Thanks!

leeoniya commented 6 months ago

first, let's see how far we can get without incremental rendering :)

are you able to share an image (or even streaming screen rec) of the the final result, and maybe attach a representative 100k sanitized dataset?

it sounds like you have 32 "y" cells (channels), so then also 3125 "x" cells rendered to yield 100k total rendered in view? and you're adding 1666 cells per second (32 y rows * 52 x columns)?