sparklinedemo.gno
3.81 Kb · 97 lines
1// Package sparklinedemo is a small gnoweb demo of the block-character series
2// renderer provided by the [p/moul/x/daily/sparkline](/p/moul/x/daily/sparkline/v0)
3// library: it sparks a few fixed series and shows the three behaviours worth
4// knowing before using it in a realm — flat series, clamping, and floor
5// rounding.
6//
7// It contains no rendering logic of its own. Stateless, so Render is
8// deterministic.
9package sparklinedemo
10
11import (
12 "strconv"
13 "strings"
14
15 "gno.land/p/moul/x/daily/sparkline/v0"
16)
17
18// series are fixed rather than derived from chain state: a demo whose output
19// moved with the block height could not be pinned by an example test.
20var (
21 traffic = []int{3, 5, 9, 14, 12, 18, 25, 21, 16, 11, 7, 4}
22 flat = []int{1000, 1000, 1000, 1000}
23 swing = []int{-8, -3, 0, 4, 9, 2, -5}
24 pct = []int{-20, 0, 25, 50, 75, 100, 140}
25)
26
27// Render renders the demo for gnoweb.
28//
29// Render("") / Render("/") -> the full demo
30func Render(path string) string {
31 var b strings.Builder
32
33 b.WriteString("# Sparklines\n\n")
34 b.WriteString("A numeric series as one line of block characters, demoing the ")
35 b.WriteString("[`p/moul/x/daily/sparkline`](/p/moul/x/daily/sparkline/v0) library.\n\n")
36
37 b.WriteString("## A series\n\n")
38 lo, hi, _ := sparkline.Bounds(traffic)
39 b.WriteString("`" + sparkline.Ints(traffic) + "`\n\n")
40 b.WriteString(ints(traffic) + "\n\n")
41 b.WriteString("Scaled between its own bounds, `" + strconv.Itoa(lo) + "` and `" +
42 strconv.Itoa(hi) + "`. The shape is the point; the axis is not.\n\n")
43
44 b.WriteString("## The ramp\n\n")
45 b.WriteString("`" + sparkline.Levels + "` — eight levels, low to high.\n\n")
46 b.WriteString("| value | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |\n")
47 b.WriteString("|---|---|---|---|---|---|---|---|---|\n")
48 b.WriteString("| level |")
49 for v := 0; v <= 7; v++ {
50 b.WriteString(" `" + sparkline.Scaled([]int{v}, 0, 7) + "` |")
51 }
52 b.WriteString("\n\n")
53
54 b.WriteString("## A flat series\n\n")
55 b.WriteString("`" + sparkline.Ints(flat) + "` — " + ints(flat) + "\n\n")
56 b.WriteString("Mid-ramp, not along the floor. A series that never moves has no ")
57 b.WriteString("shape, but drawing it at the bottom would claim it was *zero*, ")
58 b.WriteString("which is a different thing and a wrong one.\n\n")
59
60 b.WriteString("## Negatives\n\n")
61 b.WriteString("`" + sparkline.Ints(swing) + "` — " + ints(swing) + "\n\n")
62 b.WriteString("Nothing special: the window is the minimum and maximum, wherever ")
63 b.WriteString("zero happens to fall.\n\n")
64
65 b.WriteString("## A fixed window clamps\n\n")
66 b.WriteString(ints(pct) + " against `0..100`:\n\n")
67 b.WriteString("`" + sparkline.Scaled(pct, 0, 100) + "`\n\n")
68 b.WriteString("`-20` pins to the floor and `140` to the ceiling. Choosing the ")
69 b.WriteString("window keeps a percentage readable, and one outlier cannot flatten ")
70 b.WriteString("the rest of the picture — or break the realm that drew it.\n\n")
71
72 b.WriteString("## Rounding is floor\n\n")
73 b.WriteString("| value in `0..100` | level |\n")
74 b.WriteString("|---|---|\n")
75 for _, v := range []int{0, 14, 50, 85, 99, 100} {
76 b.WriteString("| " + strconv.Itoa(v) + " | `" +
77 sparkline.Scaled([]int{v}, 0, 100) + "` |\n")
78 }
79 b.WriteString("\nOnly the maximum itself reaches the top of the ramp, so the peak ")
80 b.WriteString("of a series is always visually distinct.\n\n")
81
82 b.WriteString("## Why integers\n\n")
83 b.WriteString("Scaling is integer division throughout. Render output has to be ")
84 b.WriteString("byte-identical on every validating node, and floating point is the ")
85 b.WriteString("usual way that quietly stops being true.\n")
86
87 return b.String()
88}
89
90// ints formats a series as `a, b, c` for the prose around each sparkline.
91func ints(values []int) string {
92 parts := make([]string, len(values))
93 for i, v := range values {
94 parts[i] = strconv.Itoa(v)
95 }
96 return "`" + strings.Join(parts, ", ") + "`"
97}