flekschas / regl-scatterplot

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

Center points and zoom to fit the viewport #163

Closed masalinas closed 6 months ago

masalinas commented 6 months ago

Exist any option that implements the functionality that exist for example in google maps called:

fitBounds

This option calculate the maximun zoom and center all markers in the viewport, to have this option would be usefull in the plot to center all points and apply a maximun zoom to fit the viewport,

For example In my plot the points are painted not centered and the I must to center them and apply more zoom as you see all the time:

image

Then I center the points and apply some zoom, is posible to make this changes automatically?:

image

flekschas commented 6 months ago

Unfortunately, I'm not planning to add support for this in regl-scatterplot directly as I think this should be handled by the wrapper application.

In the interest of keeping as regl-scatterplot as performant as possible, I decided to let the wrapper application handle data preprocessing. Why? There can be various ways one might want to preprocess the data. The problem is that for large data (several million data points) preprocessing can have a significant impact on the start up time if it's applied by default. E.g., in your case, regl-scatterplot would have to loop over the data twice. For your application, this might be a neat feature but what if someone already has normalized and preprocessed data? Then we're doing unnecessary work. Hence, I decided to let the wrapper application take care of this.

IMPORTANT: Your points positions need to be normalized to [-1, 1] (normalized device coordinates). Why? Regl-scatterplot is designed to be a lower-level library, whose primary purpose is speed. As such it expects you to normalize the data upfront.

In your case, I assume the zoom issue is due to the fact that the data isn't centered around zero and normalized to [-1, 1].

Try doing the following before drawing the points:

const myPoints = [...];

let minX = Infinity;
let minY = Infinity;
let maxX = -Infinity;
let maxY = -Infinity;

myPoints.forEach((point, i) => {
  minX = Math.min(minX, point.x);
  minY = Math.min(minY, point.y);
  maxX = Math.max(maxX, point.x);
  maxY = Math.max(maxX, point.y);
});

const extentX = maxX - minX;
const extentY = maxY - minY;

const normalizedPoints = myPoints.map((point) => [
  ((point.x - minX) / extentX * 2) - 1,
  ((point.y - minY) / extentY * 2) - 1
]);

const scatterplot = createScatterplot({
  // The default setting
  cameraDistance: 1,
  // Slightly zoomed in
  cameraDistance: 0.5,
  // Slightly zoomed out
  cameraDistance: 2,
})
scatterplot.draw(normalizedPoints);
flekschas commented 6 months ago

Also, if you don't want to preprocess the data, you can still use scatterplot.zoomToPoints() or scatterplot.zoomToArea().

These methods essentially allow you to zoom to any area (not just the bounds of your data) but you can do the following for instance:

const myPoints = [...];

let minX = Infinity;
let minY = Infinity;
let maxX = -Infinity;
let maxY = -Infinity;

myPoints.forEach((point, i) => {
  minX = Math.min(minX, point.x);
  minY = Math.min(minY, point.y);
  maxX = Math.max(maxX, point.x);
  maxY = Math.max(maxX, point.y);
});

const extentX = maxX - minX;
const extentY = maxY - minY;

scatterplot.draw(myPoints);
scatterplot.zoomToArea({ x: minX, y: minY, width: extentX, height: extentY });
masalinas commented 6 months ago

Thanks I will apply the second option to fit ny points in the viewport