@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/dist-tsc/Pluot.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Pluot.d.ts","sourceRoot":"","sources":["../src/Pluot.jsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Pluot.d.ts","sourceRoot":"","sources":["../src/Pluot.jsx"],"names":[],"mappings":"AAiDA,uCAgZC"}
|
package/dist-tsc/Pluot.js
CHANGED
|
@@ -4,7 +4,7 @@ import { mat4, vec4 } from "gl-matrix";
|
|
|
4
4
|
import lzs from "lz-string";
|
|
5
5
|
import { isEqual, throttle } from "lodash-es";
|
|
6
6
|
import { FetchStore } from 'zarrita';
|
|
7
|
-
import { initialize, getIsWasmReady, render_wasm, pick_wasm, setStore, getStore,
|
|
7
|
+
import { initialize, getIsWasmReady, render_wasm, pick_wasm, setStore, getStore, getBounds, getCameraMatrixFromBounds, checkWebGpuFeatureDetection, onMouseMove2d, onWheel2d, onMouseMove3d, onWheel3d, } from '@pluot/core';
|
|
8
8
|
// Needed due to "SyntaxError: Named export 'decompressFromUint8Array' not found.
|
|
9
9
|
// The requested module 'lz-string' is a CommonJS module,
|
|
10
10
|
// which may not support all module.exports as named exports."
|
|
@@ -37,7 +37,31 @@ 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
|
+
// If cameraMatrix is not provided, then we manage the camera matrix internally.
|
|
42
|
+
const [uncontrolledCameraMatrix, setUncontrolledCameraMatrix] = useState(
|
|
43
|
+
// Note: We use an initializer function here to avoid
|
|
44
|
+
// sharing the same Float32Array among multiple Pluot
|
|
45
|
+
// component instances that may be rendered on the same page.
|
|
46
|
+
() => Float32Array.from(
|
|
47
|
+
// If the cameraMatrix prop was provided, use that for the initial camera matrix;
|
|
48
|
+
// otherwise use the default matrix.
|
|
49
|
+
controlledCameraMatrix === null
|
|
50
|
+
? (viewMode === "2d" ? DEFAULT_VIEW : DEFAULT_3D_VIEW)
|
|
51
|
+
: controlledCameraMatrix));
|
|
52
|
+
// Decide which camera matrix and setter to use.
|
|
53
|
+
// If the user provides the cameraMatrix prop but NOT the setCameraMatrix setter,
|
|
54
|
+
// then interpret the prop as the "initial" camera settings, but still treat as uncontrolled.
|
|
55
|
+
const isControlledCamera = typeof setControlledCameraMatrix === "function";
|
|
56
|
+
// Alternatively, if the user provides the setCameraMatrix setter, but NOT
|
|
57
|
+
// the cameraMatrix, interpret this as they want to use the default camera
|
|
58
|
+
// value initially, but they still want a controlled camera matrix.
|
|
59
|
+
const cameraMatrix = isControlledCamera && controlledCameraMatrix !== null
|
|
60
|
+
? controlledCameraMatrix
|
|
61
|
+
: uncontrolledCameraMatrix;
|
|
62
|
+
const setCameraMatrix = isControlledCamera
|
|
63
|
+
? setControlledCameraMatrix
|
|
64
|
+
: setUncontrolledCameraMatrix;
|
|
41
65
|
const width = Math.floor(widthProp);
|
|
42
66
|
const height = Math.floor(heightProp);
|
|
43
67
|
const isVector = format === "Vector";
|
|
@@ -57,11 +81,11 @@ export function Pluot(props) {
|
|
|
57
81
|
const [supportsWebGpu, supportsWebGpuMessage] = useMemo(checkWebGpuFeatureDetection, []);
|
|
58
82
|
const svgRef = useRef(null);
|
|
59
83
|
const canvasRef = useRef(null);
|
|
60
|
-
const
|
|
84
|
+
const cameraElementRef = useRef(null);
|
|
61
85
|
const tempButtonRef = useRef(null);
|
|
62
86
|
// We may want to update these things without triggering a re-render.
|
|
63
87
|
const isRenderingRef = useRef(false);
|
|
64
|
-
const currentTimeout = useRef(
|
|
88
|
+
const currentTimeout = useRef(minTimeout);
|
|
65
89
|
// TODO: do we want to use the backlog approach or not?
|
|
66
90
|
// (Similar to the one used in the Vitessce heatmap)
|
|
67
91
|
// Reference: https://github.com/vitessce/vitessce/blob/71f17fb605768e0428fb15ed87b3ea34bcbb4803/packages/view-types/heatmap/src/Heatmap.js#L368
|
|
@@ -71,195 +95,41 @@ export function Pluot(props) {
|
|
|
71
95
|
const [didFirstRender, setDidFirstRender] = useState(false);
|
|
72
96
|
const [bailedEarly, setBailedEarly] = useState(true);
|
|
73
97
|
const [pickingResult, setPickingResult] = useState(null);
|
|
74
|
-
// TODO: handle a viewMatrix that is provided and set via props,
|
|
75
|
-
// to enable usage as a controlled component
|
|
76
|
-
// (e.g., for linked views with shared cameras).
|
|
77
|
-
const [viewMatrix, setViewMatrix] = useState(
|
|
78
|
-
// Note: We use an initializer function here to avoid
|
|
79
|
-
// sharing the same Float32Array among multiple Pluot
|
|
80
|
-
// component instances that may be rendered on the same page.
|
|
81
|
-
() => new Float32Array(DEFAULT_VIEW));
|
|
82
98
|
useLayoutEffect(() => {
|
|
83
99
|
initialize().then(() => setIsWasmReady(getIsWasmReady()));
|
|
84
100
|
}, []);
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
distance: 0.0,
|
|
118
|
-
//target: [0.0, 0.0],
|
|
119
|
-
//viewCenter: [0.5, 0.5], // Should this be used when the coordinate system is (0 to 1) rather than (-1 to 1)?
|
|
120
|
-
viewCenter: [0.0, 0.0],
|
|
121
|
-
defaultMouseDownMoveAction: "pan",
|
|
122
|
-
onKeyDown: (event) => {
|
|
123
|
-
onCameraEvent(camera, event);
|
|
124
|
-
},
|
|
125
|
-
onKeyUp: (event) => {
|
|
126
|
-
onCameraEvent(camera, event);
|
|
127
|
-
},
|
|
128
|
-
onMouseDown: (event) => {
|
|
129
|
-
onCameraEvent(camera, event);
|
|
130
|
-
},
|
|
131
|
-
onMouseUp: (event) => {
|
|
132
|
-
onCameraEvent(camera, event);
|
|
133
|
-
},
|
|
134
|
-
onMouseMove: (event) => {
|
|
135
|
-
onCameraEvent(camera, event);
|
|
136
|
-
},
|
|
137
|
-
onWheel: (event) => {
|
|
138
|
-
onCameraEvent(camera, event);
|
|
139
|
-
},
|
|
140
|
-
aspectRatioMode: aspectRatioMode,
|
|
141
|
-
aspectRatioAlignmentMode: aspectRatioAlignmentMode,
|
|
142
|
-
});
|
|
143
|
-
// Set the initial view matrix.
|
|
144
|
-
// We need to ensure we create a new copy of the array.
|
|
145
|
-
camera.setView(new Float32Array(viewMatrix));
|
|
146
|
-
const tempHandler = e => {
|
|
147
|
-
// camera.setScaleBounds([[xScaleMin, xScaleMax], [yScaleMin, yScaleMax]])
|
|
148
|
-
//camera.lookAt([2.0, 2.0], 2.0);
|
|
149
|
-
//onCameraEvent(camera, null);
|
|
150
|
-
// Only zoom/pan the X axis; keep Y unchanged
|
|
151
|
-
const nextCameraMatrix = getCameraMatrixFromBounds({ yMin: 0.0, yMax: 100.0 }, new Float32Array(viewMatrix), {
|
|
152
|
-
width,
|
|
153
|
-
height,
|
|
154
|
-
aspectRatioMode,
|
|
155
|
-
aspectRatioAlignmentMode,
|
|
156
|
-
margins: {
|
|
157
|
-
marginTop,
|
|
158
|
-
marginBottom,
|
|
159
|
-
marginLeft,
|
|
160
|
-
marginRight
|
|
161
|
-
},
|
|
162
|
-
});
|
|
163
|
-
console.log("done", nextCameraMatrix);
|
|
164
|
-
camera.setView(nextCameraMatrix);
|
|
165
|
-
onCameraEvent(camera, null);
|
|
166
|
-
};
|
|
167
|
-
tempButtonRef.current.addEventListener('click', tempHandler);
|
|
168
|
-
// Set up an onClick handler.
|
|
169
|
-
//
|
|
170
|
-
const clickHandler = (event) => {
|
|
171
|
-
pickFrame(event.offsetX, event.offsetY);
|
|
172
|
-
};
|
|
173
|
-
cameraEl.addEventListener("click", clickHandler);
|
|
174
|
-
dispose = () => {
|
|
175
|
-
camera.dispose();
|
|
176
|
-
cameraEl.removeEventListener("click", clickHandler);
|
|
177
|
-
tempButtonRef.current.removeEventListener('click', tempHandler);
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
else if (viewMode === "3d") {
|
|
181
|
-
function onCameraEvent(camera, event) {
|
|
182
|
-
camera.tick();
|
|
183
|
-
// Note: the 3D camera stores the matrix in camera.matrix (not camera.view).
|
|
184
|
-
setViewMatrix(prev => {
|
|
185
|
-
// Since camera events happen even on mousemove events that do not change the matrix,
|
|
186
|
-
// we check for equality here to avoid unnecessary state updates and plot re-renders.
|
|
187
|
-
if (isEqual(prev, camera.matrix)) {
|
|
188
|
-
return prev;
|
|
189
|
-
}
|
|
190
|
-
return mat4.clone(camera.matrix);
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
const camera = create3dCamera(cameraEl, {
|
|
194
|
-
mode: "orbit",
|
|
195
|
-
zoomSpeed: -5,
|
|
196
|
-
});
|
|
197
|
-
// TODO:
|
|
198
|
-
// - fork 3d-view-controls and remove usage of "global" - then clean up vite config.
|
|
199
|
-
// - define a camera.dispsose option.
|
|
200
|
-
// Reference: https://github.com/flekschas/dom-2d-camera/blob/cd59ea035a0ea72c2c0535fa3721f8127946576c/src/index.js#L237C3-L315C71
|
|
201
|
-
const keyUpHandler = (event) => {
|
|
202
|
-
// TODO
|
|
203
|
-
};
|
|
204
|
-
const keyDownHandler = (event) => {
|
|
205
|
-
// TODO
|
|
206
|
-
};
|
|
207
|
-
const mouseUpHandler = (event) => {
|
|
208
|
-
// TODO
|
|
209
|
-
};
|
|
210
|
-
const mouseDownHandler = (event) => {
|
|
211
|
-
// TODO
|
|
212
|
-
};
|
|
213
|
-
// TODO: use react state?
|
|
214
|
-
var lastX = 0;
|
|
215
|
-
var lastY = 0;
|
|
216
|
-
// Reference: https://github.com/mikolalysenko/3d-view/blob/8269e02337bba1923173a750aa7f3f0f76c91ba5/example/minimal.js#L67
|
|
217
|
-
const mouseMoveHandler = (event) => {
|
|
218
|
-
/*
|
|
219
|
-
var dx = (event.clientX - lastX) / width;
|
|
220
|
-
var dy = -(event.clientY - lastY) / height;
|
|
221
|
-
if (event.which === 1) {
|
|
222
|
-
if (event.shiftKey) {
|
|
223
|
-
//zoom
|
|
224
|
-
camera.rotate(now(), 0, 0, dx);
|
|
225
|
-
} else {
|
|
226
|
-
//rotate
|
|
227
|
-
camera.rotate(now(), dx, dy);
|
|
228
|
-
}
|
|
229
|
-
} else if (event.which === 3) {
|
|
230
|
-
//pan
|
|
231
|
-
camera.pan(now(), dx, dy);
|
|
232
|
-
}
|
|
233
|
-
lastX = event.clientX;
|
|
234
|
-
lastY = event.clientY;
|
|
235
|
-
*/
|
|
236
|
-
onCameraEvent(camera, event);
|
|
237
|
-
};
|
|
238
|
-
const wheelHandler = (event) => {
|
|
239
|
-
//camera.pan(now(), 0, 0, event.deltaY);
|
|
240
|
-
onCameraEvent(camera, event);
|
|
241
|
-
};
|
|
242
|
-
cameraEl.addEventListener("keydown", keyDownHandler);
|
|
243
|
-
cameraEl.addEventListener("keyup", keyUpHandler);
|
|
244
|
-
cameraEl.addEventListener("mousedown", mouseDownHandler);
|
|
245
|
-
cameraEl.addEventListener("mouseup", mouseUpHandler);
|
|
246
|
-
cameraEl.addEventListener("mousemove", mouseMoveHandler);
|
|
247
|
-
cameraEl.addEventListener("wheel", wheelHandler);
|
|
248
|
-
dispose = () => {
|
|
249
|
-
cameraEl.removeEventListener("keydown", keyDownHandler);
|
|
250
|
-
cameraEl.removeEventListener("keyup", keyUpHandler);
|
|
251
|
-
cameraEl.removeEventListener("mousedown", mouseDownHandler);
|
|
252
|
-
cameraEl.removeEventListener("mouseup", mouseUpHandler);
|
|
253
|
-
cameraEl.removeEventListener("mousemove", mouseMoveHandler);
|
|
254
|
-
cameraEl.removeEventListener("wheel", wheelHandler);
|
|
255
|
-
};
|
|
256
|
-
//camera.matrix = new Float32Array(viewMatrix);
|
|
257
|
-
}
|
|
258
|
-
else {
|
|
259
|
-
throw new Error("Unknown mode found.");
|
|
260
|
-
}
|
|
261
|
-
return dispose;
|
|
262
|
-
}, [cameraRef, viewMode, aspectRatioMode, aspectRatioAlignmentMode, width, height, marginLeft, marginRight, marginTop, marginBottom]);
|
|
101
|
+
const wheelHandler = useEffectEvent((event) => {
|
|
102
|
+
const onWheel = viewMode === "3d" ? onWheel3d : onWheel2d;
|
|
103
|
+
const nextCameraMatrix = onWheel({
|
|
104
|
+
width,
|
|
105
|
+
height,
|
|
106
|
+
aspectRatioMode,
|
|
107
|
+
aspectRatioAlignmentMode,
|
|
108
|
+
margins: {
|
|
109
|
+
marginTop,
|
|
110
|
+
marginBottom,
|
|
111
|
+
marginLeft,
|
|
112
|
+
marginRight,
|
|
113
|
+
},
|
|
114
|
+
}, cameraMatrix, event);
|
|
115
|
+
setCameraMatrix(nextCameraMatrix);
|
|
116
|
+
});
|
|
117
|
+
const mouseMoveHandler = useEffectEvent((event) => {
|
|
118
|
+
const onMouseMove = viewMode === "3d" ? onMouseMove3d : onMouseMove2d;
|
|
119
|
+
const nextCameraMatrix = onMouseMove({
|
|
120
|
+
width,
|
|
121
|
+
height,
|
|
122
|
+
aspectRatioMode,
|
|
123
|
+
aspectRatioAlignmentMode,
|
|
124
|
+
margins: {
|
|
125
|
+
marginTop,
|
|
126
|
+
marginBottom,
|
|
127
|
+
marginLeft,
|
|
128
|
+
marginRight,
|
|
129
|
+
},
|
|
130
|
+
}, cameraMatrix, event);
|
|
131
|
+
setCameraMatrix(nextCameraMatrix);
|
|
132
|
+
});
|
|
263
133
|
// The picking callback.
|
|
264
134
|
const pickFrame = useEffectEvent(async (screenCoordX, screenCoordY) => {
|
|
265
135
|
const renderParams = {
|
|
@@ -276,7 +146,7 @@ export function Pluot(props) {
|
|
|
276
146
|
view_mode: viewMode,
|
|
277
147
|
pickable: false,
|
|
278
148
|
// Should see the latest viewMatrix here, since renderFrame is wrapped in useEffectEvent.
|
|
279
|
-
camera_view:
|
|
149
|
+
camera_view: cameraMatrix,
|
|
280
150
|
plot_id: plotId,
|
|
281
151
|
plot_type: plotType,
|
|
282
152
|
store_name: storeName,
|
|
@@ -294,6 +164,28 @@ export function Pluot(props) {
|
|
|
294
164
|
// We also need to flip the Y coordinate so that positive is up.
|
|
295
165
|
screenCoordX + marginLeft, marginBottom + (layerHeight - screenCoordY))));
|
|
296
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]);
|
|
297
189
|
// The renderFrame callback.
|
|
298
190
|
// We use useEffectEvent because we want to "see"
|
|
299
191
|
// the latest values of viewMatrix, plotProps, etc.
|
|
@@ -314,7 +206,7 @@ export function Pluot(props) {
|
|
|
314
206
|
view_mode: viewMode,
|
|
315
207
|
pickable: false,
|
|
316
208
|
// Should see the latest viewMatrix here, since renderFrame is wrapped in useEffectEvent.
|
|
317
|
-
camera_view:
|
|
209
|
+
camera_view: cameraMatrix,
|
|
318
210
|
plot_id: plotId,
|
|
319
211
|
plot_type: plotType,
|
|
320
212
|
store_name: storeName,
|
|
@@ -364,12 +256,17 @@ export function Pluot(props) {
|
|
|
364
256
|
ctx.putImageData(imageData, 0, 0);
|
|
365
257
|
const frameBailedEarly = arr.at(-1) === 1;
|
|
366
258
|
if (frameBailedEarly) {
|
|
367
|
-
|
|
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);
|
|
368
264
|
incBacklogIteration(); // Increment this to force a re-render.
|
|
369
265
|
setBailedEarly(true); // Update this to show the loading indicator.
|
|
370
266
|
}
|
|
371
267
|
else {
|
|
372
268
|
// Successful render.
|
|
269
|
+
currentTimeout.current = minTimeout;
|
|
373
270
|
setBailedEarly(false); // Update this to hide the loading indicator.
|
|
374
271
|
// Clear the LRU cache for the store (via its store_name) corresponding to the rendered plot.
|
|
375
272
|
const storeUsed = getStore(renderParams.store_name);
|
|
@@ -389,6 +286,15 @@ export function Pluot(props) {
|
|
|
389
286
|
useEffect(() => {
|
|
390
287
|
return () => throttledRender.cancel();
|
|
391
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
|
+
]);
|
|
392
298
|
// TODO: use react-query?
|
|
393
299
|
useEffect(() => {
|
|
394
300
|
if (!isWasmReady) {
|
|
@@ -404,9 +310,9 @@ export function Pluot(props) {
|
|
|
404
310
|
}
|
|
405
311
|
// Render on the next animation frame.
|
|
406
312
|
throttledRender();
|
|
407
|
-
}, [isWasmReady, didFirstRender,
|
|
313
|
+
}, [isWasmReady, didFirstRender, cameraMatrix, backlogIteration, plotId, plotType, plotParams, storeName, format,
|
|
408
314
|
width, height, aspectRatioMode, aspectRatioAlignmentMode, marginLeft, marginRight, marginTop, marginBottom]);
|
|
409
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", { style: { width, height, position: "relative" }, children: [!supportsWebGpu ? (_jsx("p", { children: supportsWebGpuMessage })) : null, _jsx("div", { ref:
|
|
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: {
|
|
410
316
|
position: "absolute",
|
|
411
317
|
top: marginTop,
|
|
412
318
|
left: marginLeft,
|
package/package.json
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pluot/react",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.1.
|
|
5
|
-
"description": "",
|
|
4
|
+
"version": "0.1.2",
|
|
5
|
+
"description": "React component for visualization with Pluot",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
|
+
"author": "Mark Keller",
|
|
8
|
+
"homepage": "https://pluot.dev",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/keller-mark/pluot.git"
|
|
12
|
+
},
|
|
7
13
|
"type": "module",
|
|
8
14
|
"main": "dist/index.js",
|
|
9
15
|
"files": [
|
|
@@ -30,7 +36,7 @@
|
|
|
30
36
|
"mouse-wheel": "^1.0.2",
|
|
31
37
|
"lz-string": "^1.5.0",
|
|
32
38
|
"lodash-es": "^4.17.23",
|
|
33
|
-
"@pluot/core": "0.1.
|
|
39
|
+
"@pluot/core": "0.1.2"
|
|
34
40
|
},
|
|
35
41
|
"devDependencies": {
|
|
36
42
|
"vitest": "^3.2.4",
|