From 169582f7b795b7fc99327fb16cab708375a1e6a1 Mon Sep 17 00:00:00 2001 From: Fritz Lekschas Date: Sun, 12 Jan 2025 14:20:55 -0500 Subject: [PATCH] feat: add `renderPointsAsSquares` and `disableAlphaBlending` --- CHANGELOG.md | 4 ++++ README.md | 8 +++++-- src/index.js | 25 ++++++++++++++++---- src/types.d.ts | 2 ++ tests/get-set.test.js | 55 ++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 86 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9d96c2..4f5048c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.13.0 + +- Feat: expose `renderPointsAsSquares` and `disableAlphaBlending` to allow finer control over performance increasing settings ([#206](https://github.com/flekschas/regl-scatterplot/issues/206)) + ## 1.12.1 - Fix: destroy the encoding texture before recreating it diff --git a/README.md b/README.md index 73630c4..080dab4 100644 --- a/README.md +++ b/README.md @@ -823,6 +823,8 @@ can be read and written via [`scatterplot.get()`](#scatterplot.get) and [`scatte | annotationHVLineLimit | number | `1000` | the extent of horizontal or vertical lines | `true` | `false` | | antiAliasing | number | `0.5` | higher values result in more blurry points | `true` | `false` | | pixelAligned | number | `false` | if true, points are aligned with the pixel grid | `true` | `false` | +| renderPointsAsSquares | boolean | `false` | true of `performanceMode` is true. can only be set on init! | `true` | `false` | +| disableAlphaBlending | boolean | `false` | true of `performanceMode` is true. can only be set on init! | `true` | `false` | # Notes: @@ -863,10 +865,12 @@ can be read and written via [`scatterplot.get()`](#scatterplot.get) and [`scatte - If you need to draw more than 2 million points, you might want to set `performanceMode` to `true` during the initialization to boost the performance. In performance mode, points will be drawn as simple squares and - color blending is disabled. This should allow you to draw up to 20 million + alpha blending is disabled. This should allow you to draw up to 20 million points (or more depending on your hardware). Make sure to reduce the `pointSize` as you render more and more points (e.g., `0.25` for 20 million - works for me) to ensure good performance. + works for me) to ensure good performance. You can also enable squared points + and disable alpha blending individually via `renderPointsAsSquares` and + `disableAlphaBlending` respectively. # colorBy, opacityBy, sizeBy: diff --git a/src/index.js b/src/index.js index b505567..5afddfb 100644 --- a/src/index.js +++ b/src/index.js @@ -310,6 +310,13 @@ const createScatterplot = ( spatialIndexUseWorker = DEFAULT_SPATIAL_INDEX_USE_WORKER, } = initialProperties; + const renderPointsAsSquares = Boolean( + initialProperties.renderPointsAsSquares || performanceMode, + ); + const disableAlphaBlending = Boolean( + initialProperties.disableAlphaBlending || performanceMode, + ); + mouseMode = limit(MOUSE_MODES, MOUSE_MODE_PANZOOM)(mouseMode); if (!renderer) { @@ -1596,9 +1603,9 @@ const createScatterplot = ( let alpha = ((opacityByDensityFill * W * H) / (numPointsInView * p * p)) * min(1, s); - // In performanceMode we use squares, otherwise we use circles, which only - // take up (pi r^2) of the unit square - alpha *= performanceMode ? 1 : 1 / (0.25 * Math.PI); + // Unless `renderPointsAsSquares` is true, we use circles, which only take + // up (pi r^2) of the unit square + alpha *= renderPointsAsSquares ? 1 : 1 / (0.25 * Math.PI); // If the pixels shrink below the minimum permitted size, then we adjust the opacity instead // and apply clamping of the point size in the vertex shader. Note that we add 0.5 since we @@ -1641,11 +1648,11 @@ const createScatterplot = ( getPointOpacityScale = getPointOpacityScaleBase, ) => renderer.regl({ - frag: performanceMode ? POINT_SIMPLE_FS : POINT_FS, + frag: renderPointsAsSquares ? POINT_SIMPLE_FS : POINT_FS, vert: createVertexShader(globalState), blend: { - enable: !performanceMode, + enable: !disableAlphaBlending, func: { // biome-ignore lint/style/useNamingConvention: Regl specific srcRGB: 'src alpha', @@ -3611,6 +3618,14 @@ const createScatterplot = ( return performanceMode; } + if (property === 'renderPointsAsSquares') { + return renderPointsAsSquares; + } + + if (property === 'disableAlphaBlending') { + return disableAlphaBlending; + } + if (property === 'gamma') { return renderer.gamma; } diff --git a/src/types.d.ts b/src/types.d.ts index 59b7fbf..78bf430 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -210,6 +210,8 @@ export type Properties = { lassoLongPressIndicatorParentElement: HTMLElement; camera: Camera2D; performanceMode: boolean; + renderPointsAsSquares: boolean; + disableAlphaBlending: boolean; opacityByDensityDebounceTime: number; spatialIndex: ArrayBuffer; spatialIndexUseWorker: undefined | boolean; diff --git a/tests/get-set.test.js b/tests/get-set.test.js index b5bbfc9..e80b24c 100644 --- a/tests/get-set.test.js +++ b/tests/get-set.test.js @@ -728,7 +728,6 @@ test('get("isDrawing")', async () => { scatterplot.destroy(); }); - test('set() after destroy', async () => { const scatterplot = createScatterplot({ canvas: createCanvas() }); @@ -738,3 +737,57 @@ test('set() after destroy', async () => { await expect(whenSet).rejects.toThrow(ERROR_INSTANCE_IS_DESTROYED); }); + + +test('get() and set() performance properties', async () => { + const scatterplotA = createScatterplot({ canvas: createCanvas() }); + + expect(scatterplotA.get('performanceMode')).toBe(false); + expect(scatterplotA.get('renderPointsAsSquares')).toBe(false); + expect(scatterplotA.get('disableAlphaBlending')).toBe(false); + + scatterplotA.set({ + 'performanceMode': true, + 'renderPointsAsSquares': true, + 'disableAlphaBlending': true, + }); + + expect(scatterplotA.get('performanceMode')).toBe(false); + expect(scatterplotA.get('renderPointsAsSquares')).toBe(false); + expect(scatterplotA.get('disableAlphaBlending')).toBe(false); + + scatterplotA.destroy(); + + const scatterplotB = createScatterplot({ + canvas: createCanvas(), + performanceMode: true, + }); + + expect(scatterplotB.get('performanceMode')).toBe(true); + expect(scatterplotB.get('renderPointsAsSquares')).toBe(true); + expect(scatterplotB.get('disableAlphaBlending')).toBe(true); + + scatterplotB.destroy(); + + const scatterplotC = createScatterplot({ + canvas: createCanvas(), + renderPointsAsSquares: true, + }); + + expect(scatterplotC.get('performanceMode')).toBe(false); + expect(scatterplotC.get('renderPointsAsSquares')).toBe(true); + expect(scatterplotC.get('disableAlphaBlending')).toBe(false); + + scatterplotC.destroy(); + + const scatterplotD = createScatterplot({ + canvas: createCanvas(), + disableAlphaBlending: true, + }); + + expect(scatterplotD.get('performanceMode')).toBe(false); + expect(scatterplotD.get('renderPointsAsSquares')).toBe(false); + expect(scatterplotD.get('disableAlphaBlending')).toBe(true); + + scatterplotD.destroy(); +});