@pluot/react 0.1.0 → 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/README.md +2 -0
- 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 +1533 -4956
- package/dist-tsc/Pluot.d.ts.map +1 -1
- package/dist-tsc/Pluot.js +101 -195
- package/package.json +9 -3
- package/src/Pluot.jsx +118 -228
- package/src/index.js +1 -1
package/src/Pluot.jsx
CHANGED
|
@@ -7,9 +7,10 @@ import {
|
|
|
7
7
|
initialize, getIsWasmReady,
|
|
8
8
|
render_wasm, pick_wasm,
|
|
9
9
|
setStore, getStore,
|
|
10
|
-
create2dCamera, create3dCamera,
|
|
11
10
|
getBounds, getCameraMatrixFromBounds,
|
|
12
11
|
checkWebGpuFeatureDetection,
|
|
12
|
+
onMouseMove2d, onWheel2d,
|
|
13
|
+
onMouseMove3d, onWheel3d,
|
|
13
14
|
} from '@pluot/core';
|
|
14
15
|
|
|
15
16
|
// Needed due to "SyntaxError: Named export 'decompressFromUint8Array' not found.
|
|
@@ -64,11 +65,43 @@ export function Pluot(props) {
|
|
|
64
65
|
aspectRatioAlignmentMode = "Start", // "Center", "Start", "End"
|
|
65
66
|
format = "Raster", // "Raster", "Vector"
|
|
66
67
|
minTimeout = 32,
|
|
67
|
-
maxTimeout =
|
|
68
|
+
maxTimeout = 5000,
|
|
68
69
|
allowSimultaneousRenders = true,
|
|
69
70
|
debugMargins = false,
|
|
71
|
+
cameraMatrix: controlledCameraMatrix = null,
|
|
72
|
+
setCameraMatrix: setControlledCameraMatrix = null,
|
|
73
|
+
enablePicking = true,
|
|
74
|
+
backgroundColor = undefined,
|
|
70
75
|
} = props;
|
|
71
76
|
|
|
77
|
+
// If cameraMatrix is not provided, then we manage the camera matrix internally.
|
|
78
|
+
const [uncontrolledCameraMatrix, setUncontrolledCameraMatrix] = useState(
|
|
79
|
+
// Note: We use an initializer function here to avoid
|
|
80
|
+
// sharing the same Float32Array among multiple Pluot
|
|
81
|
+
// component instances that may be rendered on the same page.
|
|
82
|
+
() => Float32Array.from(
|
|
83
|
+
// If the cameraMatrix prop was provided, use that for the initial camera matrix;
|
|
84
|
+
// otherwise use the default matrix.
|
|
85
|
+
controlledCameraMatrix === null
|
|
86
|
+
? (viewMode === "2d" ? DEFAULT_VIEW : DEFAULT_3D_VIEW)
|
|
87
|
+
: controlledCameraMatrix
|
|
88
|
+
)
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// Decide which camera matrix and setter to use.
|
|
92
|
+
// If the user provides the cameraMatrix prop but NOT the setCameraMatrix setter,
|
|
93
|
+
// then interpret the prop as the "initial" camera settings, but still treat as uncontrolled.
|
|
94
|
+
const isControlledCamera = typeof setControlledCameraMatrix === "function";
|
|
95
|
+
// Alternatively, if the user provides the setCameraMatrix setter, but NOT
|
|
96
|
+
// the cameraMatrix, interpret this as they want to use the default camera
|
|
97
|
+
// value initially, but they still want a controlled camera matrix.
|
|
98
|
+
const cameraMatrix = isControlledCamera && controlledCameraMatrix !== null
|
|
99
|
+
? controlledCameraMatrix
|
|
100
|
+
: uncontrolledCameraMatrix;
|
|
101
|
+
const setCameraMatrix = isControlledCamera
|
|
102
|
+
? setControlledCameraMatrix
|
|
103
|
+
: setUncontrolledCameraMatrix;
|
|
104
|
+
|
|
72
105
|
const width = Math.floor(widthProp);
|
|
73
106
|
const height = Math.floor(heightProp);
|
|
74
107
|
|
|
@@ -92,13 +125,13 @@ export function Pluot(props) {
|
|
|
92
125
|
|
|
93
126
|
const svgRef = useRef(null);
|
|
94
127
|
const canvasRef = useRef(null);
|
|
95
|
-
const
|
|
128
|
+
const cameraElementRef = useRef(null);
|
|
96
129
|
|
|
97
130
|
const tempButtonRef = useRef(null);
|
|
98
131
|
|
|
99
132
|
// We may want to update these things without triggering a re-render.
|
|
100
133
|
const isRenderingRef = useRef(false);
|
|
101
|
-
const currentTimeout = useRef(
|
|
134
|
+
const currentTimeout = useRef(minTimeout);
|
|
102
135
|
|
|
103
136
|
// TODO: do we want to use the backlog approach or not?
|
|
104
137
|
// (Similar to the one used in the Vitessce heatmap)
|
|
@@ -112,232 +145,45 @@ export function Pluot(props) {
|
|
|
112
145
|
|
|
113
146
|
const [pickingResult, setPickingResult] = useState(null);
|
|
114
147
|
|
|
115
|
-
// TODO: handle a viewMatrix that is provided and set via props,
|
|
116
|
-
// to enable usage as a controlled component
|
|
117
|
-
// (e.g., for linked views with shared cameras).
|
|
118
|
-
const [viewMatrix, setViewMatrix] = useState(
|
|
119
|
-
// Note: We use an initializer function here to avoid
|
|
120
|
-
// sharing the same Float32Array among multiple Pluot
|
|
121
|
-
// component instances that may be rendered on the same page.
|
|
122
|
-
() => new Float32Array(DEFAULT_VIEW)
|
|
123
|
-
);
|
|
124
148
|
|
|
125
149
|
useLayoutEffect(() => {
|
|
126
150
|
initialize().then(() => setIsWasmReady(getIsWasmReady()));
|
|
127
151
|
}, []);
|
|
128
152
|
|
|
129
|
-
useEffect(() => {
|
|
130
|
-
// Reset view matrix on plot change.
|
|
131
|
-
// Create a new Float32Array to avoid sharing a mutable array
|
|
132
|
-
// among multiple Pluot component instances.
|
|
133
|
-
setViewMatrix(new Float32Array(viewMode === "2d" ? DEFAULT_VIEW : DEFAULT_3D_VIEW));
|
|
134
|
-
//viewMatrixRef.current = new Float32Array(DEFAULT_VIEW);
|
|
135
|
-
}, [plotId, viewMode]);
|
|
136
|
-
|
|
137
153
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
function onCameraEvent(camera, event) {
|
|
151
|
-
camera.tick();
|
|
152
|
-
// Reference: https://github.com/flekschas/regl-scatterplot/blob/17a650c352fad313d1574472b2fdc5f58b9e1eca/src/index.js#L1648
|
|
153
|
-
|
|
154
|
-
setViewMatrix(prev => {
|
|
155
|
-
// Since camera events happen even on mousemove events that do not change the matrix,
|
|
156
|
-
// we check for equality here to avoid unnecessary state updates and plot re-renders.
|
|
157
|
-
if (isEqual(prev, camera.view)) {
|
|
158
|
-
return prev;
|
|
159
|
-
}
|
|
160
|
-
return mat4.clone(camera.view)
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
currentTimeout.current = minTimeout;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const camera = create2dCamera(cameraEl, {
|
|
167
|
-
isFixed: false,
|
|
168
|
-
distance: 0.0,
|
|
169
|
-
//target: [0.0, 0.0],
|
|
170
|
-
//viewCenter: [0.5, 0.5], // Should this be used when the coordinate system is (0 to 1) rather than (-1 to 1)?
|
|
171
|
-
viewCenter: [0.0, 0.0],
|
|
172
|
-
defaultMouseDownMoveAction: "pan",
|
|
173
|
-
|
|
174
|
-
onKeyDown: (event) => {
|
|
175
|
-
onCameraEvent(camera, event);
|
|
176
|
-
},
|
|
177
|
-
onKeyUp: (event) => {
|
|
178
|
-
onCameraEvent(camera, event);
|
|
179
|
-
},
|
|
180
|
-
onMouseDown: (event) => {
|
|
181
|
-
onCameraEvent(camera, event);
|
|
182
|
-
},
|
|
183
|
-
onMouseUp: (event) => {
|
|
184
|
-
onCameraEvent(camera, event);
|
|
185
|
-
},
|
|
186
|
-
onMouseMove: (event) => {
|
|
187
|
-
onCameraEvent(camera, event);
|
|
188
|
-
},
|
|
189
|
-
onWheel: (event) => {
|
|
190
|
-
onCameraEvent(camera, event);
|
|
154
|
+
const wheelHandler = useEffectEvent((event) => {
|
|
155
|
+
const onWheel = viewMode === "3d" ? onWheel3d : onWheel2d;
|
|
156
|
+
const nextCameraMatrix = onWheel({
|
|
157
|
+
width,
|
|
158
|
+
height,
|
|
159
|
+
aspectRatioMode,
|
|
160
|
+
aspectRatioAlignmentMode,
|
|
161
|
+
margins: {
|
|
162
|
+
marginTop,
|
|
163
|
+
marginBottom,
|
|
164
|
+
marginLeft,
|
|
165
|
+
marginRight,
|
|
191
166
|
},
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
// Set the initial view matrix.
|
|
198
|
-
// We need to ensure we create a new copy of the array.
|
|
199
|
-
camera.setView(new Float32Array(viewMatrix));
|
|
200
|
-
|
|
201
|
-
const tempHandler = e => {
|
|
202
|
-
// camera.setScaleBounds([[xScaleMin, xScaleMax], [yScaleMin, yScaleMax]])
|
|
203
|
-
//camera.lookAt([2.0, 2.0], 2.0);
|
|
204
|
-
//onCameraEvent(camera, null);
|
|
205
|
-
|
|
206
|
-
// Only zoom/pan the X axis; keep Y unchanged
|
|
207
|
-
const nextCameraMatrix = getCameraMatrixFromBounds(
|
|
208
|
-
{ yMin: 0.0, yMax: 100.0 },
|
|
209
|
-
new Float32Array(viewMatrix),
|
|
210
|
-
{
|
|
211
|
-
width,
|
|
212
|
-
height,
|
|
213
|
-
aspectRatioMode,
|
|
214
|
-
aspectRatioAlignmentMode,
|
|
215
|
-
margins: {
|
|
216
|
-
marginTop,
|
|
217
|
-
marginBottom,
|
|
218
|
-
marginLeft,
|
|
219
|
-
marginRight
|
|
220
|
-
},
|
|
221
|
-
},
|
|
222
|
-
);
|
|
223
|
-
|
|
224
|
-
console.log("done", nextCameraMatrix)
|
|
225
|
-
|
|
226
|
-
camera.setView(nextCameraMatrix);
|
|
227
|
-
onCameraEvent(camera, null);
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
tempButtonRef.current.addEventListener('click', tempHandler);
|
|
231
|
-
|
|
232
|
-
// Set up an onClick handler.
|
|
233
|
-
//
|
|
234
|
-
const clickHandler = (event) => {
|
|
235
|
-
pickFrame(event.offsetX, event.offsetY);
|
|
236
|
-
};
|
|
237
|
-
cameraEl.addEventListener("click", clickHandler);
|
|
238
|
-
|
|
239
|
-
dispose = () => {
|
|
240
|
-
camera.dispose();
|
|
241
|
-
cameraEl.removeEventListener("click", clickHandler);
|
|
242
|
-
|
|
243
|
-
tempButtonRef.current.removeEventListener('click', tempHandler);
|
|
244
|
-
};
|
|
245
|
-
} else if (viewMode === "3d") {
|
|
246
|
-
function onCameraEvent(camera, event) {
|
|
247
|
-
camera.tick();
|
|
248
|
-
// Note: the 3D camera stores the matrix in camera.matrix (not camera.view).
|
|
249
|
-
setViewMatrix(prev => {
|
|
250
|
-
// Since camera events happen even on mousemove events that do not change the matrix,
|
|
251
|
-
// we check for equality here to avoid unnecessary state updates and plot re-renders.
|
|
252
|
-
if (isEqual(prev, camera.matrix)) {
|
|
253
|
-
return prev;
|
|
254
|
-
}
|
|
255
|
-
return mat4.clone(camera.matrix)
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
const camera = create3dCamera(cameraEl, {
|
|
260
|
-
mode: "orbit",
|
|
261
|
-
zoomSpeed: -5,
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
// TODO:
|
|
265
|
-
// - fork 3d-view-controls and remove usage of "global" - then clean up vite config.
|
|
266
|
-
// - define a camera.dispsose option.
|
|
267
|
-
|
|
268
|
-
// Reference: https://github.com/flekschas/dom-2d-camera/blob/cd59ea035a0ea72c2c0535fa3721f8127946576c/src/index.js#L237C3-L315C71
|
|
269
|
-
const keyUpHandler = (event) => {
|
|
270
|
-
// TODO
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
const keyDownHandler = (event) => {
|
|
274
|
-
// TODO
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
const mouseUpHandler = (event) => {
|
|
278
|
-
// TODO
|
|
279
|
-
};
|
|
280
|
-
|
|
281
|
-
const mouseDownHandler = (event) => {
|
|
282
|
-
// TODO
|
|
283
|
-
};
|
|
284
|
-
|
|
285
|
-
// TODO: use react state?
|
|
286
|
-
var lastX = 0;
|
|
287
|
-
var lastY = 0;
|
|
288
|
-
|
|
289
|
-
// Reference: https://github.com/mikolalysenko/3d-view/blob/8269e02337bba1923173a750aa7f3f0f76c91ba5/example/minimal.js#L67
|
|
290
|
-
const mouseMoveHandler = (event) => {
|
|
291
|
-
/*
|
|
292
|
-
var dx = (event.clientX - lastX) / width;
|
|
293
|
-
var dy = -(event.clientY - lastY) / height;
|
|
294
|
-
if (event.which === 1) {
|
|
295
|
-
if (event.shiftKey) {
|
|
296
|
-
//zoom
|
|
297
|
-
camera.rotate(now(), 0, 0, dx);
|
|
298
|
-
} else {
|
|
299
|
-
//rotate
|
|
300
|
-
camera.rotate(now(), dx, dy);
|
|
301
|
-
}
|
|
302
|
-
} else if (event.which === 3) {
|
|
303
|
-
//pan
|
|
304
|
-
camera.pan(now(), dx, dy);
|
|
305
|
-
}
|
|
306
|
-
lastX = event.clientX;
|
|
307
|
-
lastY = event.clientY;
|
|
308
|
-
*/
|
|
309
|
-
onCameraEvent(camera, event);
|
|
310
|
-
};
|
|
311
|
-
|
|
312
|
-
const wheelHandler = (event) => {
|
|
313
|
-
//camera.pan(now(), 0, 0, event.deltaY);
|
|
314
|
-
onCameraEvent(camera, event);
|
|
315
|
-
};
|
|
316
|
-
|
|
317
|
-
cameraEl.addEventListener("keydown", keyDownHandler);
|
|
318
|
-
cameraEl.addEventListener("keyup", keyUpHandler);
|
|
319
|
-
cameraEl.addEventListener("mousedown", mouseDownHandler);
|
|
320
|
-
cameraEl.addEventListener("mouseup", mouseUpHandler);
|
|
321
|
-
cameraEl.addEventListener("mousemove", mouseMoveHandler);
|
|
322
|
-
cameraEl.addEventListener("wheel", wheelHandler);
|
|
323
|
-
|
|
324
|
-
dispose = () => {
|
|
325
|
-
cameraEl.removeEventListener("keydown", keyDownHandler);
|
|
326
|
-
cameraEl.removeEventListener("keyup", keyUpHandler);
|
|
327
|
-
cameraEl.removeEventListener("mousedown", mouseDownHandler);
|
|
328
|
-
cameraEl.removeEventListener("mouseup", mouseUpHandler);
|
|
329
|
-
cameraEl.removeEventListener("mousemove", mouseMoveHandler);
|
|
330
|
-
cameraEl.removeEventListener("wheel", wheelHandler);
|
|
331
|
-
};
|
|
332
|
-
|
|
333
|
-
//camera.matrix = new Float32Array(viewMatrix);
|
|
334
|
-
|
|
335
|
-
} else {
|
|
336
|
-
throw new Error("Unknown mode found.");
|
|
337
|
-
}
|
|
167
|
+
}, cameraMatrix, event);
|
|
168
|
+
setCameraMatrix(nextCameraMatrix);
|
|
169
|
+
});
|
|
338
170
|
|
|
339
|
-
|
|
340
|
-
|
|
171
|
+
const mouseMoveHandler = useEffectEvent((event) => {
|
|
172
|
+
const onMouseMove = viewMode === "3d" ? onMouseMove3d : onMouseMove2d;
|
|
173
|
+
const nextCameraMatrix = onMouseMove({
|
|
174
|
+
width,
|
|
175
|
+
height,
|
|
176
|
+
aspectRatioMode,
|
|
177
|
+
aspectRatioAlignmentMode,
|
|
178
|
+
margins: {
|
|
179
|
+
marginTop,
|
|
180
|
+
marginBottom,
|
|
181
|
+
marginLeft,
|
|
182
|
+
marginRight,
|
|
183
|
+
},
|
|
184
|
+
}, cameraMatrix, event);
|
|
185
|
+
setCameraMatrix(nextCameraMatrix);
|
|
186
|
+
});
|
|
341
187
|
|
|
342
188
|
// The picking callback.
|
|
343
189
|
const pickFrame = useEffectEvent(async (screenCoordX, screenCoordY) => {
|
|
@@ -355,7 +201,7 @@ export function Pluot(props) {
|
|
|
355
201
|
view_mode: viewMode,
|
|
356
202
|
pickable: false,
|
|
357
203
|
// Should see the latest viewMatrix here, since renderFrame is wrapped in useEffectEvent.
|
|
358
|
-
camera_view:
|
|
204
|
+
camera_view: cameraMatrix,
|
|
359
205
|
plot_id: plotId,
|
|
360
206
|
plot_type: plotType,
|
|
361
207
|
store_name: storeName,
|
|
@@ -379,6 +225,35 @@ export function Pluot(props) {
|
|
|
379
225
|
)));
|
|
380
226
|
});
|
|
381
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
|
+
|
|
382
257
|
|
|
383
258
|
// The renderFrame callback.
|
|
384
259
|
// We use useEffectEvent because we want to "see"
|
|
@@ -401,7 +276,7 @@ export function Pluot(props) {
|
|
|
401
276
|
view_mode: viewMode,
|
|
402
277
|
pickable: false,
|
|
403
278
|
// Should see the latest viewMatrix here, since renderFrame is wrapped in useEffectEvent.
|
|
404
|
-
camera_view:
|
|
279
|
+
camera_view: cameraMatrix,
|
|
405
280
|
plot_id: plotId,
|
|
406
281
|
plot_type: plotType,
|
|
407
282
|
store_name: storeName,
|
|
@@ -460,11 +335,16 @@ export function Pluot(props) {
|
|
|
460
335
|
|
|
461
336
|
const frameBailedEarly = arr.at(-1) === 1;
|
|
462
337
|
if (frameBailedEarly) {
|
|
463
|
-
|
|
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);
|
|
464
343
|
incBacklogIteration(); // Increment this to force a re-render.
|
|
465
344
|
setBailedEarly(true); // Update this to show the loading indicator.
|
|
466
345
|
} else {
|
|
467
346
|
// Successful render.
|
|
347
|
+
currentTimeout.current = minTimeout;
|
|
468
348
|
setBailedEarly(false); // Update this to hide the loading indicator.
|
|
469
349
|
|
|
470
350
|
// Clear the LRU cache for the store (via its store_name) corresponding to the rendered plot.
|
|
@@ -492,6 +372,16 @@ export function Pluot(props) {
|
|
|
492
372
|
return () => throttledRender.cancel();
|
|
493
373
|
}, [throttledRender]);
|
|
494
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
|
+
|
|
495
385
|
// TODO: use react-query?
|
|
496
386
|
useEffect(() => {
|
|
497
387
|
if (!isWasmReady) {
|
|
@@ -509,17 +399,17 @@ export function Pluot(props) {
|
|
|
509
399
|
|
|
510
400
|
// Render on the next animation frame.
|
|
511
401
|
throttledRender();
|
|
512
|
-
}, [isWasmReady, didFirstRender,
|
|
402
|
+
}, [isWasmReady, didFirstRender, cameraMatrix, backlogIteration, plotId, plotType, plotParams, storeName, format,
|
|
513
403
|
width, height, aspectRatioMode, aspectRatioAlignmentMode, marginLeft, marginRight, marginTop, marginBottom]);
|
|
514
404
|
|
|
515
405
|
return (
|
|
516
406
|
<>
|
|
517
|
-
<div style={{ width, height, position: "relative" }}>
|
|
407
|
+
<div style={{ width, height, position: "relative", backgroundColor }}>
|
|
518
408
|
{!supportsWebGpu ? (
|
|
519
409
|
<p>{supportsWebGpuMessage}</p>
|
|
520
410
|
) : null}
|
|
521
411
|
<div
|
|
522
|
-
ref={
|
|
412
|
+
ref={cameraElementRef}
|
|
523
413
|
style={{
|
|
524
414
|
position: "absolute",
|
|
525
415
|
top: marginTop,
|
package/src/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export * from '@pluot/core'; // Re-export everything from the vanilla JS package.
|
|
2
|
-
export { Pluot } from "./Pluot.
|
|
2
|
+
export { Pluot } from "./Pluot.js";
|