Desenhar
Um núcleo Rust transforma uma vista no que um quadro desenha, em todo lugar onde o Nonius roda. No navegador, ele é compilado para WebAssembly e desenha com WebGPU, com WebGL2 onde falta WebGPU, e na CPU num canvas 2D quando o navegador não dá nenhum dos dois. No .NET e no C, é uma biblioteca nativa. Como imagem, desenha na CPU num PNG. Todos desenham o mesmo quadro: os mesmos eixos, rótulos, mira e legenda, desenhados pelo próprio gráfico e nunca sobrepostos a ele como HTML.
Então a escolha abaixo é só onde o seu programa vive.
No navegador
O pacote npm nonius desenha num <canvas>. Duas regras valem em toda
página: a caixa dá o tamanho ao canvas, nunca os pixels do próprio
canvas; e a página é servida por HTTP, já que um módulo e o seu
WebAssembly não carregam de file://.
<!-- Names the package for the browser, as a bundler would. -->
<script type="importmap">{ "imports": { "nonius": "./node_modules/nonius/index.js" } }</script>
<style>
/* The box gives the canvas its size; the chart draws at that size in
device pixels and follows it. */
.plot { position: relative; height: 400px; }
.plot canvas { position: absolute; inset: 0; width: 100%; height: 100%; touch-action: none; }
</style>
<div class="plot"><canvas></canvas></div>
<script type="module">
import { fromPoints } from "./points.js";
await fromPoints(document.querySelector("canvas"));
</script>
Essa é uma página inteira. O import map diz ao navegador onde está o
"nonius"; com um bundler (Vite, webpack), o bundler faz isso. O módulo
que ela carrega cria o gráfico, entrega os pontos por qualquer
caminho — o módulo dos tiles ali está inteiro — e
deixa a pessoa explorar com o interact: arrastar para mover, roda do
mouse ou dois dedos para o zoom, passar o cursor para a mira. Um
gráfico alimentado por tiles entrega os tiles ao interact,
interact(chart, canvas, { tiles }), para que mover busque o que a
nova vista precisa.
As opções do gráfico vão para o createChart, e cada uma tem um método
para mudá-la depois:
createChart(canvas, { theme: "light", timeZone: "America/Sao_Paulo", title: "Estufa 2", unit: "°C" }),
e depois setTheme, setTimeZone(chart, name), setTitle e
setUnit. Toda opção, tipada e documentada, está no index.d.ts do
pacote.
React
O gráfico é criado de forma assíncrona (ele espera o seu WebAssembly) e tem memória fora da do JavaScript, então um componente o cria num efeito e o libera na limpeza do efeito — também quando a limpeza vem antes, como o modo de desenvolvimento do React faz de propósito:
createChart(element).then((made) => {
if (disposed) {
made.free();
return;
}
chart = made;
chart.setScale(decimals);
chart.push(points.t, points.v, points.w);
return () => {
disposed = true;
interaction?.stop();
chart?.free();
};
Vue
O mesmo, em onMounted e onBeforeUnmount, com uma regra própria do
Vue: guarde o gráfico numa variável simples, não num ref.
// Plain variables, not refs: Vue would wrap the chart in a proxy, and the
// chart's methods must be called on the chart itself.
let chart: Chart | undefined;
let interaction: ReturnType<typeof interact> | undefined;
Os dois componentes estão inteiros nos exemplos de React e Vue do pacote, rodando com o Vite e só o plug-in do próprio framework: o WebAssembly não precisa de plug-in nenhum.
No .NET
O pacote NuGet Nonius traz o núcleo nativo para Windows x64 e Linux
x64. Sobre ele, dois controles.
Avalonia, para uma janela de desktop (Nonius.Avalonia):
var series = Series.FromNsb(File.ReadAllBytes(nsb));
var chart = new NoniusChart { Series = series };
chart.PointPicked += (_, point) => Console.WriteLine(point);
window.Content = chart;
Blazor WebAssembly, para uma página (Nonius.Blazor), que usa o
pacote do navegador por baixo:
<NoniusChart @ref="chart" Nsb="day-4096.nsb" Start="First" End="Last" Theme="theme" Gap="gap"
TimeZone="zone" Title="@title" Unit="@unit"
ViewChanged="Changed" style="display: block; width: 800px; height: 400px" />
Tempos e valores passam entre o .NET e a página como strings decimais, nunca como números JavaScript, que arredondariam um tempo em nanossegundos de hoje para 256 ns.
A partir do C
O nonius-capi é o núcleo atrás de uma interface C: um cabeçalho, o
nonius.h, e uma biblioteca para ligar. Os pacotes .NET são feitos
sobre ele, e qualquer outra linguagem que chama C — o ctypes do
Python, o cgo do Go, o FFM do Java — chega ao Nonius do mesmo jeito. Um
arquivo de série desenhado como PNG:
NoniusSeries *series = NULL;
/* Summaries from one second up, nine levels. */
int32_t status = nonius_series_from_nsb(bytes, len, 1000000000, 9, &series);
if (status != NONIUS_OK) {
fprintf(stderr, "%s\n", nonius_status_message(status));
return 1;
}
Toda chamada devolve um status; nonius_status_message o diz em
palavras. O programa inteiro, e como compilá-lo com MSVC ou gcc, estão
na página do nonius-capi.
Como imagem
Uma still é um quadro desenhado na CPU, sem janela e sem GPU, codificado como PNG: para um relatório, um e-mail, a resposta de um servidor. Quatro caminhos para a mesma imagem:
| De | Como |
|---|---|
| Um servidor | GET /v1/series/{id}/png?… (Servir) |
| .NET | series.StillPng(new Still(…)) (Escolha o caminho) |
| C | nonius_still_png(series, &still, &png) |
| Rust | nonius_render::still_png |
No Rust, uma still diz tudo o que um quadro precisa:
#![allow(unused)]
fn main() {
let png = still_png(&series, &Still {
start: 0,
end: 1_000_000_000,
width: 640,
height: 320,
theme: Theme::dark(),
ratio: 1.0,
scale: Scale::new(2)?,
gap: None,
zone: Zone::UTC,
title: None,
unit: None,
})?;
}
width e height são os pixels da própria imagem; ratio é a razão
de pixels da tela a que ela se destina, que escala os rótulos e as
linhas, não a imagem.
O mesmo gráfico em todo lugar
Cada opção tem o mesmo sentido em todo pacote, e o mesmo padrão: escuro, UTC, todo ponto ligado, sem cabeçalho.
| Navegador | .NET | Imagem do servidor | |
|---|---|---|---|
| Tema | theme: "light" | Theme.Light | theme=light |
| Fuso | timeZone: "Europe/Lisbon" | um TimeZoneInfo | tz=Europe/Lisbon |
| Lacuna | setSeriesGap(0, ns), ns um BigInt | Gap | gap= |
| Título, unidade | title, unit do createChart | Title, Unit | title=, unit= |