@pluot/react 0.1.0 → 0.1.1
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/index.js +1227 -4924
- package/dist-tsc/Pluot.d.ts.map +1 -1
- package/dist-tsc/Pluot.js +72 -187
- package/package.json +9 -3
- package/src/Pluot.jsx +80 -217
- 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.
|
|
@@ -67,8 +68,38 @@ export function Pluot(props) {
|
|
|
67
68
|
maxTimeout = 32,
|
|
68
69
|
allowSimultaneousRenders = true,
|
|
69
70
|
debugMargins = false,
|
|
71
|
+
cameraMatrix: controlledCameraMatrix = null,
|
|
72
|
+
setCameraMatrix: setControlledCameraMatrix = null,
|
|
70
73
|
} = props;
|
|
71
74
|
|
|
75
|
+
// If cameraMatrix is not provided, then we manage the camera matrix internally.
|
|
76
|
+
const [uncontrolledCameraMatrix, setUncontrolledCameraMatrix] = useState(
|
|
77
|
+
// Note: We use an initializer function here to avoid
|
|
78
|
+
// sharing the same Float32Array among multiple Pluot
|
|
79
|
+
// component instances that may be rendered on the same page.
|
|
80
|
+
() => Float32Array.from(
|
|
81
|
+
// If the cameraMatrix prop was provided, use that for the initial camera matrix;
|
|
82
|
+
// otherwise use the default matrix.
|
|
83
|
+
controlledCameraMatrix === null
|
|
84
|
+
? (viewMode === "2d" ? DEFAULT_VIEW : DEFAULT_3D_VIEW)
|
|
85
|
+
: controlledCameraMatrix
|
|
86
|
+
)
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
// Decide which camera matrix and setter to use.
|
|
90
|
+
// If the user provides the cameraMatrix prop but NOT the setCameraMatrix setter,
|
|
91
|
+
// then interpret the prop as the "initial" camera settings, but still treat as uncontrolled.
|
|
92
|
+
const isControlledCamera = typeof setControlledCameraMatrix === "function";
|
|
93
|
+
// Alternatively, if the user provides the setCameraMatrix setter, but NOT
|
|
94
|
+
// the cameraMatrix, interpret this as they want to use the default camera
|
|
95
|
+
// value initially, but they still want a controlled camera matrix.
|
|
96
|
+
const cameraMatrix = isControlledCamera && controlledCameraMatrix !== null
|
|
97
|
+
? controlledCameraMatrix
|
|
98
|
+
: uncontrolledCameraMatrix;
|
|
99
|
+
const setCameraMatrix = isControlledCamera
|
|
100
|
+
? setControlledCameraMatrix
|
|
101
|
+
: setUncontrolledCameraMatrix;
|
|
102
|
+
|
|
72
103
|
const width = Math.floor(widthProp);
|
|
73
104
|
const height = Math.floor(heightProp);
|
|
74
105
|
|
|
@@ -92,7 +123,7 @@ export function Pluot(props) {
|
|
|
92
123
|
|
|
93
124
|
const svgRef = useRef(null);
|
|
94
125
|
const canvasRef = useRef(null);
|
|
95
|
-
const
|
|
126
|
+
const cameraElementRef = useRef(null);
|
|
96
127
|
|
|
97
128
|
const tempButtonRef = useRef(null);
|
|
98
129
|
|
|
@@ -112,232 +143,64 @@ export function Pluot(props) {
|
|
|
112
143
|
|
|
113
144
|
const [pickingResult, setPickingResult] = useState(null);
|
|
114
145
|
|
|
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
146
|
|
|
125
147
|
useLayoutEffect(() => {
|
|
126
148
|
initialize().then(() => setIsWasmReady(getIsWasmReady()));
|
|
127
149
|
}, []);
|
|
128
150
|
|
|
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
151
|
|
|
152
|
+
const wheelHandler = useEffectEvent((event) => {
|
|
153
|
+
const onWheel = viewMode === "3d" ? onWheel3d : onWheel2d;
|
|
154
|
+
const nextCameraMatrix = onWheel({
|
|
155
|
+
width,
|
|
156
|
+
height,
|
|
157
|
+
aspectRatioMode,
|
|
158
|
+
aspectRatioAlignmentMode,
|
|
159
|
+
margins: {
|
|
160
|
+
marginTop,
|
|
161
|
+
marginBottom,
|
|
162
|
+
marginLeft,
|
|
163
|
+
marginRight,
|
|
164
|
+
},
|
|
165
|
+
}, cameraMatrix, event);
|
|
166
|
+
setCameraMatrix(nextCameraMatrix);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const mouseMoveHandler = useEffectEvent((event) => {
|
|
170
|
+
const onMouseMove = viewMode === "3d" ? onMouseMove3d : onMouseMove2d;
|
|
171
|
+
const nextCameraMatrix = onMouseMove({
|
|
172
|
+
width,
|
|
173
|
+
height,
|
|
174
|
+
aspectRatioMode,
|
|
175
|
+
aspectRatioAlignmentMode,
|
|
176
|
+
margins: {
|
|
177
|
+
marginTop,
|
|
178
|
+
marginBottom,
|
|
179
|
+
marginLeft,
|
|
180
|
+
marginRight,
|
|
181
|
+
},
|
|
182
|
+
}, cameraMatrix, event);
|
|
183
|
+
setCameraMatrix(nextCameraMatrix);
|
|
184
|
+
});
|
|
137
185
|
|
|
138
|
-
// Set up
|
|
186
|
+
// Set up FUNCTIONAL camera.
|
|
139
187
|
useEffect(() => {
|
|
140
188
|
// Set up the camera.
|
|
141
|
-
const cameraEl =
|
|
189
|
+
const cameraEl = cameraElementRef.current;
|
|
190
|
+
|
|
142
191
|
if (!cameraEl) {
|
|
143
192
|
return () => {};
|
|
144
193
|
}
|
|
145
194
|
|
|
146
|
-
let dispose = () => {};
|
|
147
|
-
|
|
148
195
|
// Create a 2D camera for handling zoom and pan.
|
|
149
|
-
|
|
150
|
-
|
|
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",
|
|
196
|
+
cameraEl.addEventListener("mousemove", mouseMoveHandler);
|
|
197
|
+
cameraEl.addEventListener("wheel", wheelHandler);
|
|
173
198
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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);
|
|
191
|
-
},
|
|
192
|
-
aspectRatioMode: aspectRatioMode,
|
|
193
|
-
aspectRatioAlignmentMode: aspectRatioAlignmentMode,
|
|
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
|
-
}
|
|
338
|
-
|
|
339
|
-
return dispose;
|
|
340
|
-
}, [cameraRef, viewMode, aspectRatioMode, aspectRatioAlignmentMode, width, height, marginLeft, marginRight, marginTop, marginBottom]);
|
|
199
|
+
return () => {
|
|
200
|
+
cameraEl.removeEventListener("mousemove", mouseMoveHandler);
|
|
201
|
+
cameraEl.removeEventListener("wheel", wheelHandler);
|
|
202
|
+
};
|
|
203
|
+
}, [viewMode]);
|
|
341
204
|
|
|
342
205
|
// The picking callback.
|
|
343
206
|
const pickFrame = useEffectEvent(async (screenCoordX, screenCoordY) => {
|
|
@@ -355,7 +218,7 @@ export function Pluot(props) {
|
|
|
355
218
|
view_mode: viewMode,
|
|
356
219
|
pickable: false,
|
|
357
220
|
// Should see the latest viewMatrix here, since renderFrame is wrapped in useEffectEvent.
|
|
358
|
-
camera_view:
|
|
221
|
+
camera_view: cameraMatrix,
|
|
359
222
|
plot_id: plotId,
|
|
360
223
|
plot_type: plotType,
|
|
361
224
|
store_name: storeName,
|
|
@@ -401,7 +264,7 @@ export function Pluot(props) {
|
|
|
401
264
|
view_mode: viewMode,
|
|
402
265
|
pickable: false,
|
|
403
266
|
// Should see the latest viewMatrix here, since renderFrame is wrapped in useEffectEvent.
|
|
404
|
-
camera_view:
|
|
267
|
+
camera_view: cameraMatrix,
|
|
405
268
|
plot_id: plotId,
|
|
406
269
|
plot_type: plotType,
|
|
407
270
|
store_name: storeName,
|
|
@@ -509,7 +372,7 @@ export function Pluot(props) {
|
|
|
509
372
|
|
|
510
373
|
// Render on the next animation frame.
|
|
511
374
|
throttledRender();
|
|
512
|
-
}, [isWasmReady, didFirstRender,
|
|
375
|
+
}, [isWasmReady, didFirstRender, cameraMatrix, backlogIteration, plotId, plotType, plotParams, storeName, format,
|
|
513
376
|
width, height, aspectRatioMode, aspectRatioAlignmentMode, marginLeft, marginRight, marginTop, marginBottom]);
|
|
514
377
|
|
|
515
378
|
return (
|
|
@@ -519,7 +382,7 @@ export function Pluot(props) {
|
|
|
519
382
|
<p>{supportsWebGpuMessage}</p>
|
|
520
383
|
) : null}
|
|
521
384
|
<div
|
|
522
|
-
ref={
|
|
385
|
+
ref={cameraElementRef}
|
|
523
386
|
style={{
|
|
524
387
|
position: "absolute",
|
|
525
388
|
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";
|