Appearance
Tooltips and selection
Picking on volumes, clouds, splats
const { positions, scalars } = clusters(3000);
plot.showPoints(plot.uploadPoints({ positions, scalars }));
plot.frameScene();
return {
async pointer(x, y) {
const hit = await plot.pickScene(x, y, true);
if (hit === null) return "nothing within six pixels";
const at = [0, 1, 2].map((c) => fixed(positions[hit.index * 3 + c])).join(", ");
return `point ${hit.index} at (${at}), value ${fixed(scalars[hit.index])}`;
},
};Calls
4 in the code above, in the order it makes them
plot.showPoints(handle: PointsHandle): voidDraw the point cloud
handlenames.plot.uploadPoints(points: PointSet): PointsHandleUpload a point cloud and return a handle naming it.
plot.frameScene(): voidPoint the camera at the middle of everything uploaded, far enough back to see it.
Call after uploading. A scene measured in grid cells or in millimetres is nowhere near where a camera starts, and without this the first thing a caller sees is an empty canvas.
plot.pickScene(x: number, y: number, detail?: boolean, radius?: number): Promise<ScenePick | null>What scene item is under a pixel: a volume, a point cloud, a splat set, a vector field or a surface.
nullwhen the ray meets nothing.The scientific geometry's answer to
hitTest, which resolves a chart's datum. Withdetail, the pick also names the sub-object it struck -- the point, the splat, the arrow -- as an index into the array that was uploaded.Resolves a frame or two later, because the answer comes from reading a pixel back off the GPU and a browser cannot wait on that. The page has to keep rendering for it to land, which a plot being looked at does anyway.
radiusis how far, in logical pixels, the pointer may be from what it picks. A point in a cloud is a disc a few pixels across and a user aims at it rather than hitting it, so the default is a tolerance, not a pixel: within it the most specific feature wins (a point, a splat or an arrow before a surface), ties broken by distance. Pass0for the pixel under the pointer and nothing else. A near pick carries novalue: the scalar at a point the pointer was merely near is not read back.A pick started while another is in flight replaces it, so the earlier promise resolves
nullrather than hanging: a pointer crossing the canvas costs one pick, not a queue of them.On WebGL2 there is no pixel to read back. The pick is a ray cast on the CPU against the next frame, which turns
setScenePickingon;radiusis ignored there, and isosurfaces are not reached.