@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/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,uCAqXC"}
|
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 = 32, allowSimultaneousRenders = true, debugMargins = false, } = props;
|
|
40
|
+
minTimeout = 32, maxTimeout = 32, allowSimultaneousRenders = true, debugMargins = false, cameraMatrix: controlledCameraMatrix = null, setCameraMatrix: setControlledCameraMatrix = null, } = 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,7 +81,7 @@ 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);
|
|
@@ -71,195 +95,56 @@ 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
|
-
|
|
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
|
+
});
|
|
133
|
+
// Set up FUNCTIONAL camera.
|
|
93
134
|
useEffect(() => {
|
|
94
135
|
// Set up the camera.
|
|
95
|
-
const cameraEl =
|
|
136
|
+
const cameraEl = cameraElementRef.current;
|
|
96
137
|
if (!cameraEl) {
|
|
97
138
|
return () => { };
|
|
98
139
|
}
|
|
99
|
-
let dispose = () => { };
|
|
100
140
|
// Create a 2D camera for handling zoom and pan.
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (isEqual(prev, camera.view)) {
|
|
109
|
-
return prev;
|
|
110
|
-
}
|
|
111
|
-
return mat4.clone(camera.view);
|
|
112
|
-
});
|
|
113
|
-
currentTimeout.current = minTimeout;
|
|
114
|
-
}
|
|
115
|
-
const camera = create2dCamera(cameraEl, {
|
|
116
|
-
isFixed: false,
|
|
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]);
|
|
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]);
|
|
263
148
|
// The picking callback.
|
|
264
149
|
const pickFrame = useEffectEvent(async (screenCoordX, screenCoordY) => {
|
|
265
150
|
const renderParams = {
|
|
@@ -276,7 +161,7 @@ export function Pluot(props) {
|
|
|
276
161
|
view_mode: viewMode,
|
|
277
162
|
pickable: false,
|
|
278
163
|
// Should see the latest viewMatrix here, since renderFrame is wrapped in useEffectEvent.
|
|
279
|
-
camera_view:
|
|
164
|
+
camera_view: cameraMatrix,
|
|
280
165
|
plot_id: plotId,
|
|
281
166
|
plot_type: plotType,
|
|
282
167
|
store_name: storeName,
|
|
@@ -314,7 +199,7 @@ export function Pluot(props) {
|
|
|
314
199
|
view_mode: viewMode,
|
|
315
200
|
pickable: false,
|
|
316
201
|
// Should see the latest viewMatrix here, since renderFrame is wrapped in useEffectEvent.
|
|
317
|
-
camera_view:
|
|
202
|
+
camera_view: cameraMatrix,
|
|
318
203
|
plot_id: plotId,
|
|
319
204
|
plot_type: plotType,
|
|
320
205
|
store_name: storeName,
|
|
@@ -404,9 +289,9 @@ export function Pluot(props) {
|
|
|
404
289
|
}
|
|
405
290
|
// Render on the next animation frame.
|
|
406
291
|
throttledRender();
|
|
407
|
-
}, [isWasmReady, didFirstRender,
|
|
292
|
+
}, [isWasmReady, didFirstRender, cameraMatrix, backlogIteration, plotId, plotType, plotParams, storeName, format,
|
|
408
293
|
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:
|
|
294
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { style: { width, height, position: "relative" }, children: [!supportsWebGpu ? (_jsx("p", { children: supportsWebGpuMessage })) : null, _jsx("div", { ref: cameraElementRef, style: {
|
|
410
295
|
position: "absolute",
|
|
411
296
|
top: marginTop,
|
|
412
297
|
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.1",
|
|
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.1"
|
|
34
40
|
},
|
|
35
41
|
"devDependencies": {
|
|
36
42
|
"vitest": "^3.2.4",
|