@pluot/react 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/C059-BdIta.ttf-Bdc938Uh.js +4 -0
  2. package/dist/C059-Bold.ttf-D54yVEIG.js +4 -0
  3. package/dist/C059-Italic.ttf-DLWXDpX2.js +4 -0
  4. package/dist/C059-Roman.ttf-B_4E2VG-.js +4 -0
  5. package/dist/D050000L.ttf-DThw2vhA.js +4 -0
  6. package/dist/NimbusMonoPS-Bold.ttf-DaeY0qBr.js +4 -0
  7. package/dist/NimbusMonoPS-BoldItalic.ttf-BWf8L4h2.js +4 -0
  8. package/dist/NimbusMonoPS-Italic.ttf-B6sVyRe1.js +4 -0
  9. package/dist/NimbusMonoPS-Regular.ttf-4U95hi4W.js +4 -0
  10. package/dist/NimbusRoman-Bold.ttf-CYfJBZ-D.js +4 -0
  11. package/dist/NimbusRoman-BoldItalic.ttf-IKgFHn3u.js +4 -0
  12. package/dist/NimbusRoman-Italic.ttf-DphHuaaV.js +4 -0
  13. package/dist/NimbusRoman-Regular.ttf-CyDfJ_2L.js +4 -0
  14. package/dist/NimbusSans-Bold.ttf-BgToHb0k.js +4 -0
  15. package/dist/NimbusSans-BoldItalic.ttf-DBTyNZML.js +4 -0
  16. package/dist/NimbusSans-BoldOblique.ttf-BsmbX-RQ.js +4 -0
  17. package/dist/NimbusSans-Italic.ttf-DXn1aKmU.js +4 -0
  18. package/dist/NimbusSans-Oblique.ttf-BnOkISci.js +4 -0
  19. package/dist/NimbusSans-Regular.ttf-BPUWfSNR.js +4 -0
  20. package/dist/NimbusSansNarrow-BdOblique.ttf-pMMXNfDW.js +4 -0
  21. package/dist/NimbusSansNarrow-Bold.ttf-CTqrnZ6z.js +4 -0
  22. package/dist/NimbusSansNarrow-BoldOblique.ttf-CDZKzRWR.js +4 -0
  23. package/dist/NimbusSansNarrow-Oblique.ttf-CmqouKBN.js +4 -0
  24. package/dist/NimbusSansNarrow-Regular.ttf-DOZi8TRM.js +4 -0
  25. package/dist/P052-Bold.ttf-F7VVlUHU.js +4 -0
  26. package/dist/P052-BoldItalic.ttf-CTaOaPJC.js +4 -0
  27. package/dist/P052-Italic.ttf-CfqenRHn.js +4 -0
  28. package/dist/P052-Roman.ttf-B-tZ3T0p.js +4 -0
  29. package/dist/StandardSymbolsPS.ttf-DQ9k8vPT.js +4 -0
  30. package/dist/URWBookman-Demi.ttf-gvfD6bGd.js +4 -0
  31. package/dist/URWBookman-DemiItalic.ttf-CTYueWh5.js +4 -0
  32. package/dist/URWBookman-Light.ttf-DK879rFB.js +4 -0
  33. package/dist/URWBookman-LightItalic.ttf-BWVZJTZv.js +4 -0
  34. package/dist/URWGothic-Book.ttf-BjKm3X4s.js +4 -0
  35. package/dist/URWGothic-BookOblique.ttf-UGkGTKkJ.js +4 -0
  36. package/dist/URWGothic-Demi.ttf-CUhEK02w.js +4 -0
  37. package/dist/URWGothic-DemiOblique.ttf-CP_D_bVd.js +4 -0
  38. package/dist/Z003-MediumItalic.ttf-BXc6HvHA.js +4 -0
  39. package/dist/index.js +312 -38
  40. package/dist-tsc/Pluot.d.ts.map +1 -1
  41. package/dist-tsc/Pluot.js +40 -19
  42. package/package.json +2 -2
  43. package/src/Pluot.jsx +50 -23
@@ -1 +1 @@
1
- {"version":3,"file":"Pluot.d.ts","sourceRoot":"","sources":["../src/Pluot.jsx"],"names":[],"mappings":"AAiDA,uCAqXC"}
1
+ {"version":3,"file":"Pluot.d.ts","sourceRoot":"","sources":["../src/Pluot.jsx"],"names":[],"mappings":"AAiDA,uCAgZC"}
package/dist-tsc/Pluot.js CHANGED
@@ -37,7 +37,7 @@ export function Pluot(props) {
37
37
  const { width: widthProp, height: heightProp, plotId, plotType, store, storeName: storeNameProp, plotParams, viewMode = "2d", marginBottom = 100.0, marginLeft = 100.0, marginTop = 100.0, marginRight = 100.0, aspectRatioMode = "Contain", // "Ignore", "Contain", "Cover"
38
38
  aspectRatioAlignmentMode = "Start", // "Center", "Start", "End"
39
39
  format = "Raster", // "Raster", "Vector"
40
- minTimeout = 32, maxTimeout = 32, allowSimultaneousRenders = true, debugMargins = false, cameraMatrix: controlledCameraMatrix = null, setCameraMatrix: setControlledCameraMatrix = null, } = props;
40
+ minTimeout = 32, maxTimeout = 5000, allowSimultaneousRenders = true, debugMargins = false, cameraMatrix: controlledCameraMatrix = null, setCameraMatrix: setControlledCameraMatrix = null, enablePicking = true, backgroundColor = undefined, } = props;
41
41
  // If cameraMatrix is not provided, then we manage the camera matrix internally.
42
42
  const [uncontrolledCameraMatrix, setUncontrolledCameraMatrix] = useState(
43
43
  // Note: We use an initializer function here to avoid
@@ -85,7 +85,7 @@ export function Pluot(props) {
85
85
  const tempButtonRef = useRef(null);
86
86
  // We may want to update these things without triggering a re-render.
87
87
  const isRenderingRef = useRef(false);
88
- const currentTimeout = useRef(maxTimeout);
88
+ const currentTimeout = useRef(minTimeout);
89
89
  // TODO: do we want to use the backlog approach or not?
90
90
  // (Similar to the one used in the Vitessce heatmap)
91
91
  // Reference: https://github.com/vitessce/vitessce/blob/71f17fb605768e0428fb15ed87b3ea34bcbb4803/packages/view-types/heatmap/src/Heatmap.js#L368
@@ -130,21 +130,6 @@ export function Pluot(props) {
130
130
  }, cameraMatrix, event);
131
131
  setCameraMatrix(nextCameraMatrix);
132
132
  });
133
- // Set up FUNCTIONAL camera.
134
- useEffect(() => {
135
- // Set up the camera.
136
- const cameraEl = cameraElementRef.current;
137
- if (!cameraEl) {
138
- return () => { };
139
- }
140
- // Create a 2D camera for handling zoom and pan.
141
- cameraEl.addEventListener("mousemove", mouseMoveHandler);
142
- cameraEl.addEventListener("wheel", wheelHandler);
143
- return () => {
144
- cameraEl.removeEventListener("mousemove", mouseMoveHandler);
145
- cameraEl.removeEventListener("wheel", wheelHandler);
146
- };
147
- }, [viewMode]);
148
133
  // The picking callback.
149
134
  const pickFrame = useEffectEvent(async (screenCoordX, screenCoordY) => {
150
135
  const renderParams = {
@@ -179,6 +164,28 @@ export function Pluot(props) {
179
164
  // We also need to flip the Y coordinate so that positive is up.
180
165
  screenCoordX + marginLeft, marginBottom + (layerHeight - screenCoordY))));
181
166
  });
167
+ // Set up the camera and picking handlers.
168
+ useEffect(() => {
169
+ const cameraEl = cameraElementRef.current;
170
+ if (!cameraEl) {
171
+ return () => { };
172
+ }
173
+ // Create a 2D camera for handling zoom and pan.
174
+ cameraEl.addEventListener("mousemove", mouseMoveHandler);
175
+ cameraEl.addEventListener("wheel", wheelHandler);
176
+ // Set up an onClick handler for picking.
177
+ const clickHandler = (event) => {
178
+ if (enablePicking) {
179
+ pickFrame(event.offsetX, event.offsetY);
180
+ }
181
+ };
182
+ cameraEl.addEventListener("click", clickHandler);
183
+ return () => {
184
+ cameraEl.removeEventListener("mousemove", mouseMoveHandler);
185
+ cameraEl.removeEventListener("wheel", wheelHandler);
186
+ cameraEl.removeEventListener("click", clickHandler);
187
+ };
188
+ }, [viewMode, enablePicking]);
182
189
  // The renderFrame callback.
183
190
  // We use useEffectEvent because we want to "see"
184
191
  // the latest values of viewMatrix, plotProps, etc.
@@ -249,12 +256,17 @@ export function Pluot(props) {
249
256
  ctx.putImageData(imageData, 0, 0);
250
257
  const frameBailedEarly = arr.at(-1) === 1;
251
258
  if (frameBailedEarly) {
252
- currentTimeout.current = maxTimeout;
259
+ // We multiply the current timeout by two to implement an exponential backoff
260
+ // while the Rust side is bailing early.
261
+ // A downstream useEffect restarts the exponential backoff from scratch
262
+ // if any other plotting parameters change.
263
+ currentTimeout.current = Math.min(currentTimeout.current * 2, maxTimeout);
253
264
  incBacklogIteration(); // Increment this to force a re-render.
254
265
  setBailedEarly(true); // Update this to show the loading indicator.
255
266
  }
256
267
  else {
257
268
  // Successful render.
269
+ currentTimeout.current = minTimeout;
258
270
  setBailedEarly(false); // Update this to hide the loading indicator.
259
271
  // Clear the LRU cache for the store (via its store_name) corresponding to the rendered plot.
260
272
  const storeUsed = getStore(renderParams.store_name);
@@ -274,6 +286,15 @@ export function Pluot(props) {
274
286
  useEffect(() => {
275
287
  return () => throttledRender.cancel();
276
288
  }, [throttledRender]);
289
+ // Reset the backoff timeout whenever plot parameters change so the next
290
+ // sequence of bailed-early renders starts from the minimum again.
291
+ useEffect(() => {
292
+ currentTimeout.current = minTimeout;
293
+ }, [plotId, plotType, plotParams, storeName, format,
294
+ width, height, aspectRatioMode, aspectRatioAlignmentMode,
295
+ marginLeft, marginRight, marginTop, marginBottom,
296
+ cameraMatrix,
297
+ ]);
277
298
  // TODO: use react-query?
278
299
  useEffect(() => {
279
300
  if (!isWasmReady) {
@@ -291,7 +312,7 @@ export function Pluot(props) {
291
312
  throttledRender();
292
313
  }, [isWasmReady, didFirstRender, cameraMatrix, backlogIteration, plotId, plotType, plotParams, storeName, format,
293
314
  width, height, aspectRatioMode, aspectRatioAlignmentMode, marginLeft, marginRight, marginTop, marginBottom]);
294
- return (_jsxs(_Fragment, { children: [_jsxs("div", { style: { width, height, position: "relative" }, children: [!supportsWebGpu ? (_jsx("p", { children: supportsWebGpuMessage })) : null, _jsx("div", { ref: cameraElementRef, style: {
315
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { style: { width, height, position: "relative", backgroundColor }, children: [!supportsWebGpu ? (_jsx("p", { children: supportsWebGpuMessage })) : null, _jsx("div", { ref: cameraElementRef, style: {
295
316
  position: "absolute",
296
317
  top: marginTop,
297
318
  left: marginLeft,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pluot/react",
3
3
  "private": false,
4
- "version": "0.1.1",
4
+ "version": "0.1.2",
5
5
  "description": "React component for visualization with Pluot",
6
6
  "license": "Apache-2.0",
7
7
  "author": "Mark Keller",
@@ -36,7 +36,7 @@
36
36
  "mouse-wheel": "^1.0.2",
37
37
  "lz-string": "^1.5.0",
38
38
  "lodash-es": "^4.17.23",
39
- "@pluot/core": "0.1.1"
39
+ "@pluot/core": "0.1.2"
40
40
  },
41
41
  "devDependencies": {
42
42
  "vitest": "^3.2.4",
package/src/Pluot.jsx CHANGED
@@ -65,11 +65,13 @@ export function Pluot(props) {
65
65
  aspectRatioAlignmentMode = "Start", // "Center", "Start", "End"
66
66
  format = "Raster", // "Raster", "Vector"
67
67
  minTimeout = 32,
68
- maxTimeout = 32,
68
+ maxTimeout = 5000,
69
69
  allowSimultaneousRenders = true,
70
70
  debugMargins = false,
71
71
  cameraMatrix: controlledCameraMatrix = null,
72
72
  setCameraMatrix: setControlledCameraMatrix = null,
73
+ enablePicking = true,
74
+ backgroundColor = undefined,
73
75
  } = props;
74
76
 
75
77
  // If cameraMatrix is not provided, then we manage the camera matrix internally.
@@ -129,7 +131,7 @@ export function Pluot(props) {
129
131
 
130
132
  // We may want to update these things without triggering a re-render.
131
133
  const isRenderingRef = useRef(false);
132
- const currentTimeout = useRef(maxTimeout);
134
+ const currentTimeout = useRef(minTimeout);
133
135
 
134
136
  // TODO: do we want to use the backlog approach or not?
135
137
  // (Similar to the one used in the Vitessce heatmap)
@@ -183,25 +185,6 @@ export function Pluot(props) {
183
185
  setCameraMatrix(nextCameraMatrix);
184
186
  });
185
187
 
186
- // Set up FUNCTIONAL camera.
187
- useEffect(() => {
188
- // Set up the camera.
189
- const cameraEl = cameraElementRef.current;
190
-
191
- if (!cameraEl) {
192
- return () => {};
193
- }
194
-
195
- // Create a 2D camera for handling zoom and pan.
196
- cameraEl.addEventListener("mousemove", mouseMoveHandler);
197
- cameraEl.addEventListener("wheel", wheelHandler);
198
-
199
- return () => {
200
- cameraEl.removeEventListener("mousemove", mouseMoveHandler);
201
- cameraEl.removeEventListener("wheel", wheelHandler);
202
- };
203
- }, [viewMode]);
204
-
205
188
  // The picking callback.
206
189
  const pickFrame = useEffectEvent(async (screenCoordX, screenCoordY) => {
207
190
  const renderParams = {
@@ -242,6 +225,35 @@ export function Pluot(props) {
242
225
  )));
243
226
  });
244
227
 
228
+ // Set up the camera and picking handlers.
229
+ useEffect(() => {
230
+ const cameraEl = cameraElementRef.current;
231
+
232
+ if (!cameraEl) {
233
+ return () => {};
234
+ }
235
+
236
+ // Create a 2D camera for handling zoom and pan.
237
+ cameraEl.addEventListener("mousemove", mouseMoveHandler);
238
+ cameraEl.addEventListener("wheel", wheelHandler);
239
+
240
+ // Set up an onClick handler for picking.
241
+ const clickHandler = (event) => {
242
+ if (enablePicking) {
243
+ pickFrame(event.offsetX, event.offsetY);
244
+ }
245
+ };
246
+ cameraEl.addEventListener("click", clickHandler);
247
+
248
+ return () => {
249
+ cameraEl.removeEventListener("mousemove", mouseMoveHandler);
250
+ cameraEl.removeEventListener("wheel", wheelHandler);
251
+ cameraEl.removeEventListener("click", clickHandler);
252
+ };
253
+ }, [viewMode, enablePicking]);
254
+
255
+
256
+
245
257
 
246
258
  // The renderFrame callback.
247
259
  // We use useEffectEvent because we want to "see"
@@ -323,11 +335,16 @@ export function Pluot(props) {
323
335
 
324
336
  const frameBailedEarly = arr.at(-1) === 1;
325
337
  if (frameBailedEarly) {
326
- currentTimeout.current = maxTimeout;
338
+ // We multiply the current timeout by two to implement an exponential backoff
339
+ // while the Rust side is bailing early.
340
+ // A downstream useEffect restarts the exponential backoff from scratch
341
+ // if any other plotting parameters change.
342
+ currentTimeout.current = Math.min(currentTimeout.current * 2, maxTimeout);
327
343
  incBacklogIteration(); // Increment this to force a re-render.
328
344
  setBailedEarly(true); // Update this to show the loading indicator.
329
345
  } else {
330
346
  // Successful render.
347
+ currentTimeout.current = minTimeout;
331
348
  setBailedEarly(false); // Update this to hide the loading indicator.
332
349
 
333
350
  // Clear the LRU cache for the store (via its store_name) corresponding to the rendered plot.
@@ -355,6 +372,16 @@ export function Pluot(props) {
355
372
  return () => throttledRender.cancel();
356
373
  }, [throttledRender]);
357
374
 
375
+ // Reset the backoff timeout whenever plot parameters change so the next
376
+ // sequence of bailed-early renders starts from the minimum again.
377
+ useEffect(() => {
378
+ currentTimeout.current = minTimeout;
379
+ }, [plotId, plotType, plotParams, storeName, format,
380
+ width, height, aspectRatioMode, aspectRatioAlignmentMode,
381
+ marginLeft, marginRight, marginTop, marginBottom,
382
+ cameraMatrix,
383
+ ]);
384
+
358
385
  // TODO: use react-query?
359
386
  useEffect(() => {
360
387
  if (!isWasmReady) {
@@ -377,7 +404,7 @@ export function Pluot(props) {
377
404
 
378
405
  return (
379
406
  <>
380
- <div style={{ width, height, position: "relative" }}>
407
+ <div style={{ width, height, position: "relative", backgroundColor }}>
381
408
  {!supportsWebGpu ? (
382
409
  <p>{supportsWebGpuMessage}</p>
383
410
  ) : null}