@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.
- package/dist/C059-BdIta.ttf-Bdc938Uh.js +4 -0
- package/dist/C059-Bold.ttf-D54yVEIG.js +4 -0
- package/dist/C059-Italic.ttf-DLWXDpX2.js +4 -0
- package/dist/C059-Roman.ttf-B_4E2VG-.js +4 -0
- package/dist/D050000L.ttf-DThw2vhA.js +4 -0
- package/dist/NimbusMonoPS-Bold.ttf-DaeY0qBr.js +4 -0
- package/dist/NimbusMonoPS-BoldItalic.ttf-BWf8L4h2.js +4 -0
- package/dist/NimbusMonoPS-Italic.ttf-B6sVyRe1.js +4 -0
- package/dist/NimbusMonoPS-Regular.ttf-4U95hi4W.js +4 -0
- package/dist/NimbusRoman-Bold.ttf-CYfJBZ-D.js +4 -0
- package/dist/NimbusRoman-BoldItalic.ttf-IKgFHn3u.js +4 -0
- package/dist/NimbusRoman-Italic.ttf-DphHuaaV.js +4 -0
- package/dist/NimbusRoman-Regular.ttf-CyDfJ_2L.js +4 -0
- package/dist/NimbusSans-Bold.ttf-BgToHb0k.js +4 -0
- package/dist/NimbusSans-BoldItalic.ttf-DBTyNZML.js +4 -0
- package/dist/NimbusSans-BoldOblique.ttf-BsmbX-RQ.js +4 -0
- package/dist/NimbusSans-Italic.ttf-DXn1aKmU.js +4 -0
- package/dist/NimbusSans-Oblique.ttf-BnOkISci.js +4 -0
- package/dist/NimbusSans-Regular.ttf-BPUWfSNR.js +4 -0
- package/dist/NimbusSansNarrow-BdOblique.ttf-pMMXNfDW.js +4 -0
- package/dist/NimbusSansNarrow-Bold.ttf-CTqrnZ6z.js +4 -0
- package/dist/NimbusSansNarrow-BoldOblique.ttf-CDZKzRWR.js +4 -0
- package/dist/NimbusSansNarrow-Oblique.ttf-CmqouKBN.js +4 -0
- package/dist/NimbusSansNarrow-Regular.ttf-DOZi8TRM.js +4 -0
- package/dist/P052-Bold.ttf-F7VVlUHU.js +4 -0
- package/dist/P052-BoldItalic.ttf-CTaOaPJC.js +4 -0
- package/dist/P052-Italic.ttf-CfqenRHn.js +4 -0
- package/dist/P052-Roman.ttf-B-tZ3T0p.js +4 -0
- package/dist/StandardSymbolsPS.ttf-DQ9k8vPT.js +4 -0
- package/dist/URWBookman-Demi.ttf-gvfD6bGd.js +4 -0
- package/dist/URWBookman-DemiItalic.ttf-CTYueWh5.js +4 -0
- package/dist/URWBookman-Light.ttf-DK879rFB.js +4 -0
- package/dist/URWBookman-LightItalic.ttf-BWVZJTZv.js +4 -0
- package/dist/URWGothic-Book.ttf-BjKm3X4s.js +4 -0
- package/dist/URWGothic-BookOblique.ttf-UGkGTKkJ.js +4 -0
- package/dist/URWGothic-Demi.ttf-CUhEK02w.js +4 -0
- package/dist/URWGothic-DemiOblique.ttf-CP_D_bVd.js +4 -0
- package/dist/Z003-MediumItalic.ttf-BXc6HvHA.js +4 -0
- package/dist/index.js +312 -38
- package/dist-tsc/Pluot.d.ts.map +1 -1
- package/dist-tsc/Pluot.js +40 -19
- package/package.json +2 -2
- package/src/Pluot.jsx +50 -23
package/dist-tsc/Pluot.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Pluot.d.ts","sourceRoot":"","sources":["../src/Pluot.jsx"],"names":[],"mappings":"AAiDA,
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
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}
|