Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Português

Choose a path

Every Nonius chart draws the same thing — a series of points in time order — but the points can reach it four ways. Which one fits depends on two questions: where are the points now, and do they stop? (A word new to you — a tile, a summary — is in the glossary.)

Your pointsPathWhat the chart holds
Already in the page, made or fetched by your own codePush themAll of them
A finished history, small enough to send wholeA fileAll of them, fetched once
A history too long to send, or one many people readTiles from a serverOnly what the view on screen needs
Arriving now, as they happenA live feedThe newest, as they come

And when there is no browser at all — a report sent by e-mail, a desktop program, a server answering with a picture — the chart is drawn as an image.

“Small enough to send whole” is a matter of arithmetic. A point is 24 bytes (a time, a value and a weight, 8 bytes each), so ten million points are a 240 MB file, downloaded before the first frame and held in the browser’s memory. A day of readings once a second is 86,400 points, 2 MB: a file is the simple choice. Years of readings once a millisecond are hundreds of gigabytes: only tiles reach them.

Push them

Your page already has the points — computed, read from your own API, decoded from your own format. Hand them to the chart in time order, as many at a time as you like. The browser package’s example:

const chart = await createChart(canvas);
chart.setScale(2);
chart.push(t, v, w);

t, v and w are columns of integers; Your data says how a reading becomes them, and why setScale(2).

A file

A series file holds a finished history in Nonius’s own format, NSB1. The chart fetches it whole and reads its bytes where they land, with no decoding step, so a file of millions of points is drawn as soon as it has arrived.

const chart = await createChart(canvas);
await loadNsb(chart, await fetch(url));

The file is written once, from wherever the data is: a CSV, a Rust or .NET program, Node. Your data shows each.

Tiles from a server

When the history is too long to send, a server keeps it and sends tiles: small pieces cut for the view on screen, each about its pixels’ worth of bytes whatever the length of the history. Zoomed out on ten years, the chart reads summaries; zoomed into one second, the points of that second. The chart fetches tiles as a person pans and zooms.

import { attachTiles, createChart, fetchSeries, interact } from "nonius";

// A series on a Nonius server, drawn from its tiles: the chart holds only
// what its views need and fetches the rest as a person moves, however long
// the history. `url` is the series' address, `…/v1/series/{id}`.
export async function fromServer(canvas, url) {
  // Asked for first, so the answer comes while the WebAssembly downloads.
  const meta = fetchSeries(url);
  const chart = await createChart(canvas);
  // Opens the chart on the whole series.
  const tiles = await attachTiles(chart, url, { meta });
  const interaction = interact(chart, canvas, { tiles });
  return { chart, tiles, interaction };
}

That is a whole module. fetchSeries is not awaited on purpose: the chart takes the promise, and the answer arrives while the WebAssembly downloads. interact is given the tiles, so that each view a person moves to fetches the tiles it needs. url is the series’ address on the server, http://host:port/v1/series/{id}. The server can be nonius-server, from a series file, or your own, speaking the Nonius protocol. Serving shows how to run one.

A live feed

Points that are happening now come over a WebSocket, as numbered frames sent by the Rust crate nonius-live. The chart follows the newest points; a frame the network loses is noticed by its number and asked for again.

const chart = await createChart(canvas);
// A feed's values are integers and carry no scale: say how many decimal
// places they have (2 here: 10_050 is 100.50).
chart.setScale(2);
const stream = streamInto(chart, url);

An image

The same chart, drawn as a PNG on the CPU: no browser, no GPU. A server answers GET /v1/series/{id}/png?from=…&to=…&width=…&height=…; in .NET, a series draws itself:

// Light, for a box of 1200 × 400 CSS pixels at a pixel ratio of 2:
// the size is in device pixels, the ratio in thousandths.
byte[] image = series.StillPng(new Still(first, last + 1, 2400, 800, Theme.Light, RatioPermille: 2000));
File.WriteAllBytes(png, image);

and in Rust, nonius-render does. The image is the frame a browser draws, pixel for pixel on the 2D canvas, so a report and a page agree. Drawing shows each.

Not sure?

Start with a file. It is one fetch and one call, and the file it needs is what a server serves later: moving to tiles changes how the chart is fed, not the data.