flekschas / regl-scatterplot

Scalable WebGL-based scatter plot library build with Regl
https://flekschas.github.io/regl-scatterplot/
MIT License
192 stars 24 forks source link

Wondering how to get it to work when the canvas element does not already exist? #81

Closed armsp closed 2 years ago

armsp commented 2 years ago

This may not be a bug at all...and I am just very new to react. I have been trying to use your library to show some UMAP projected vectors at a certain position in my webpage. However, everytime I get the error that canvas is null. I tried searching around and mixed and matched a few things but couldn't get it to work. Its been 2 days and I think I need help. I have the following code right now -

import createScatterplot from 'regl-scatterplot';
import React, { useRef, useEffect } from 'react'

const PreLabeledDocVectors = props => {
    const canvasRef = useRef(null);
    // const { width, height } = canvas.getBoundingClientRect();

    const points = new Array(100)
    .fill()
    .map(() => [-1 + Math.random() * 2, -1 + Math.random() * 2, "#efefef"]);
    const canvas = canvasRef.current;
    const context = canvas.getContext('2d');
    const scatterplot = createScatterplot({
        canvasRef,
        width: 20,
        height: 20,
        pointSize: 5,
      });
  useEffect((points, scatterplot) => {
    canvasRef.current.getCanvas()._canvas.id = 'some-id';
    scatterplot.draw(points);
  }, [scatterplot.draw])

  // scatterplot.draw(points);

    return (
        <div>
            pre labeled doc vectors scatter plot using reactgl scatter
            <canvas ref={canvasRef}/>
        </div>
    );
}

export default PreLabeledDocVectors;

Any idea how to get it to work when someone wants to use your library to show a scatterplot inside a certain component amongst many

flekschas commented 2 years ago

The following code does not work because canvasRef is not a valid property.

createScatterplot({
  canvasRef,
  width: 20,
  height: 20,
  pointSize: 5,
});

See https://github.com/flekschas/regl-scatterplot#basic-example, you need to pass the actual canvas element to regl-scatterplot as the canvas property. Not a React ref object.

The following should work, however, make sure that canvasRef.current is truly a canvas element and not null!

createScatterplot({
  canvas: canvasRef.current,
  width: 20,
  height: 20,
  pointSize: 5,
});

Personally, I would probably implement the React component like this (be aware, I haven't tested the code below!):

const Component = ({ points }) => {
  const scatterplotRef = useRef();
  const refHandler = (canvas) => {
    if (!canvas) return;
    const scatterplot = createScatterplot({
      canvas,
      width: 20,
      height: 20,
      pointSize: 5,
    });
    scatterplot.draw(points);
    scatterplotRef.current = scatterplot;
    return () => {
       scatterplot.destroy();
       scatterplotRef.current = undefined;
    }
  }

  useEffect(() => {
    if (!scatterplotRef.current) return;
    scatterplotRef.current.draw(points);
  }, [points]);

  return <canvas ref={refHandler} />;
}
armsp commented 2 years ago

Thank you so very much!

EDIT: @flekschas It works when the data is hardcoded...as in making from let's say something like Math.Random. When I load an array from a file...basically anything that returns a promise, then it fails to work. I do not get any error, just a blank screen. I have normalized my data like you said one other issue.

Any idea how to make it work so that works even with a promise?

flekschas commented 2 years ago

Please open a new ticket with a detailed description. The original issue seems to have been fixed.

My hunch is that you're passing a promise to regl-scatterplot instead of the real data. That's not supported. Simply wait until the promise is resolved first before attempting to call scatterplot.draw(). But I am just guessing here.