flekschas / regl-scatterplot

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

Constant point size when zooming #169

Closed kiledal closed 4 days ago

kiledal commented 7 months ago

Is there a way to fix the size of points, so that point sizes are unaffected by the zoom? I have an extremely dense plot which requires significant zooming to read meaningful information. But it seems that I have to choose between having very large points after zooming in, or very small points when zoomed out.

flekschas commented 7 months ago

Unfortunately there is not at the moment. One feature that I've been working on that would probably help you a lot is https://github.com/flekschas/regl-scatterplot/pull/143 but I didn't have the dime to finish it yet.

We could introduce a property to control the point scaling. Right now the points are scaled using the inverse hyperbolic arcsin function which seems to work well in most cases but I can see extreme cases where one might want a different zoom scale factor. It should be trivial to add such a zoom scale factor. I would expect it to behave as follows. 0 means zoom-based scaling is turned off. 1 means zoom-based scaling works as it does today (this should be the default). And values above 1 should result in increased scaling. The factor would have to be added to https://github.com/flekschas/regl-scatterplot/blob/master/src/index.js#L1374C1-L1382C5

Maybe all we need is to change line 1377 as follows:

return (
  (Math.asinh(max(1, ((camera.scaling[0] - 1) * pointScaleFactor) + 1)) / Math.asinh(1)) *
  window.devicePixelRatio
)

Example:

pointScaleFactor = 1
Array.from({ length: 10 }, (_, cameraScaling) => Math.asinh(Math.max(1.0, (cameraScaling * pointScaleFactor) + 1)) / Math.asinh(1))
 // => [1, 1.638, 2.063, 2.377, 2.624, 2.827, 3, 3.150, 3.283, 3.402]

pointScaleFactor = 0
Array.from({ length: 10 }, (_, cameraScaling) => Math.asinh(Math.max(1.0, (cameraScaling * pointScaleFactor) + 1)) / Math.asinh(1))
// => [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

pointScaleFactor = 2
Array.from({ length: 10 }, (_, cameraScaling) => Math.asinh(Math.max(1.0, (cameraScaling * pointScaleFactor) + 1)) / Math.asinh(1))
// => [1, 2.063, 2.624, 3, 3.283, 3.509, 3.698, 3.860, 4.001, 4.127]
flekschas commented 6 months ago

Any thoughts @kiledal or is this not needed anymore?

danr commented 5 months ago

I think zooming with point sizes in data units would be very useful. I have two use-cases in mind:

  1. Making heatmaps. Using regl-scatterplot would be more light-weight than higlass.
  2. Plotting multi-well plate layouts. My research group does biological experiments in 384-well plates and this would enable plotting datum based on the well positions on the plate.

Admittedly, for both of these use cases it would look better if the shape could be changed from circle to square, but that's besides this very issue. (Edit: I now see that squares are used in performance mode)

flekschas commented 5 months ago

@danr Can you clarify what you mean by zooming with data units? Would my proposal be useful for your use cases or are you thinking of something else?

Implementing the above change should be quick but before I add the feature I want to be sure it's actually useful :)

flekschas commented 5 months ago

Oh and yeah, the performance mode renders points as squares, which you might be able to use for rendering heatmaps (I haven't tried that yet but it sounds like a cool try)

danr commented 5 months ago

@flekschas I used terminology from Bokeh, they use screen units and data units:

Defined here: https://docs.bokeh.org/en/latest/docs/user_guide/styling/visuals.html#screen-units-and-data-space-units

Interestingly, your asinh-sized points are neither of these!

flekschas commented 5 months ago

Interesting! So screen units refer to constant CSS pixel point sizes and data units refer to data-driven point sizes.

The asinh transformation only transforms the camera scale, so regl-scatterplot effectively implements screen units with geometric zooming. There's no concept of rescaling the point size to the data domain at the moment.

abast commented 1 week ago

I essentially would want two modes:

(1) the point size is kept constant, independent of zoom (2) the point size is proportional to the zoom, i.e. if the user zooms in 2x, the points should be 2x larger.

What usecase is not captured by this / what is the idea behind the asinh formula?