Skip to content

Scientific geometry

Linked 2D and 3D in one view ​

scenes/linked-2d-and-3d-in-one-view
const { positions, scalars } = shell(500_000);
plot.showPoints(plot.uploadPoints({ positions, scalars }));
plot.frameScene();
const bins = histogram(scalars, 24);
plot.setCharts({
  charts: [
    {
      kind: "bar",
      series: [{ x: plot.uploadBuffer(bins.x), y: plot.uploadBuffer(bins.y), label: "points" }],
      at: "topLeft",
      // Small enough to stay clear of the cloud on a wide canvas.
      size: [0.28, 0.3],
      title: "Ripple at each point",
      y: { range: [0, Math.max(...bins.y)] },
      legend: false,
    },
  ],
});

Calls

5 in the code above, in the order it makes them

plot.showPoints(handle: PointsHandle): void

Draw the point cloud handle names.

plot.uploadPoints(points: PointSet): PointsHandle

Upload a point cloud and return a handle naming it.

plot.frameScene(): void

Point 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.setCharts(document: ChartDocument): readonly ChartHandle[]

Draw a document of charts, and get a handle for each one.

The handles come back in document order and are what names a chart afterwards: chartView, setChartView, chartPicked, and the chart on a hover, a hit or any other event. A page linking two charts holds two handles rather than allocating ids.

Replaces the charts and the series each one reads.

Throws rather than drawing a partial document, and a throw leaves the previous document in place. What it refuses: a misspelled key, a handle that names something other than a buffer, a range whose ends are the wrong way round, and a column on a chart kind that cannot read it. All four would otherwise be a chart that draws something subtly wrong, or nothing, with nothing said.

Setting a document again is the way to change one: there is no per-chart edit, and the compile is cheap because the data does not move. The buffers are named by handle, so a live chart writes to them with writeBuffer and never sets a document at all.

plot.uploadBuffer(values: Float32Array | Float64Array): BufferHandle

Upload a block of scalars and return a handle naming it.

A buffer keeps the precision it arrived in. Use Float64Array for time and for anything where f32's twenty-four bits of mantissa would lose something real: a millisecond timestamp narrowed to f32 rounds every sample into a five-minute bucket, and it does that silently.