Files
ZacharyZhang-NY 596c62e026 Add chart components 216-245; step-aware ticks, font-aware labels
Bars, lines, areas, pies, donuts, radar, histogram, scatter, bubbles,
heatmaps, gauge, bullet, funnel, waterfall, treemap, candles, box
plots, steps, rings, legend, axis, crosshair, polar area, pyramid,
spectrum bars and win-loss strips, each drawn dot by dot. Ticks follow
1-2-5 steps and print as precisely as their step needs; a label row
uses micro letters or the pixel font as a whole. Descriptions carry the
data in numbers. Bad data panics.
2026-10-12 00:58:44 -04:00

162 lines
7.0 KiB
Go

package gallery
import (
"fmt"
"math"
"github.com/egoist/mygo/ui"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/chart"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/input"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
)
var plotEntries = []Entry{
{231, "BulletChart", "子弹图", "chart", "Progress against a goal, over bands of poor, fine and great.", demoBulletChart},
{232, "Funnel", "漏斗图", "chart", "From SEEN to BOUGHT at the item shop. Most just window-shop.", demoFunnel},
{233, "Waterfall", "瀑布图", "chart", "Your wallet through a day: green steps up, red steps down.", demoWaterfall},
{234, "Treemap", "矩形树图", "chart", "The bag by weight, tiled into near squares.", demoTreemap},
{235, "Candlestick", "K线图", "chart", "Potion prices. Green days rose, red days fell.", demoCandlestick},
{236, "BoxPlot", "箱线图", "chart", "Rolls by die: the middle half in the box, the ends on whiskers.", demoBoxPlot},
{237, "StepChart", "阶梯图", "chart", "Hero level by week. It only ever steps.", demoStepChart},
{238, "ProgressRing", "进度环", "chart", "Clockwise from twelve, a pixel arc at a time.", demoProgressRing},
{239, "Legend", "图例", "chart", "Click a hero to hide or show their line.", demoLegend},
{240, "Axis", "坐标轴", "chart", "Ticks and micro labels, across or down.", demoAxis},
{241, "Crosshair", "十字线", "chart", "Hover the chart; the crosshair reads every line.", demoCrosshair},
{242, "PolarArea", "极坐标图", "chart", "Equal turns, each reaching out by its value.", demoPolarArea},
{243, "Pyramid", "金字塔图", "chart", "Heroes and monsters back to back, by level.", demoPyramid},
{244, "SpectrumBars", "频谱条", "chart", "Bars dance in segments; peak dots fall behind them.", demoSpectrumBars},
{245, "WinLoss", "胜负图", "chart", "Wins up, losses down, draws a dash. PLAY for one more.", demoWinLoss},
}
func demoBulletChart(c *ui.Context, log func(string)) {
k := pixel.K(c)
ui.Column(c).Gap(k.D(4)).Children(func() {
pixel.Text(c, "Quest XP: 62 of 80", pixel.TextOptions{Slot: pixel.Faint})
chart.BulletChart(c, "Quest XP", 62, 80, []float64{40, 70, 100}, chart.BulletChartOptions{Width: 200})
})
}
func demoFunnel(c *ui.Context, log func(string)) {
chart.Funnel(c, "Item shop", []chart.Slice{{Name: "SEEN", Value: 120}, {Name: "CLICKED", Value: 64}, {Name: "IN CART", Value: 30},
{Name: "BOUGHT", Value: 12}}, chart.FunnelOptions{Width: 200})
}
func demoWaterfall(c *ui.Context, log func(string)) {
chart.Waterfall(c, "Wallet", []string{"QUEST", "INN", "SHOP", "LOOT", "TAX"}, []float64{40, -8, -25, 18, -5}, chart.WaterfallOptions{Width: 200, Height: 90})
}
func demoTreemap(c *ui.Context, log func(string)) {
chart.Treemap(c, "Bag weight", []chart.Slice{{Name: "ARMOR", Value: 10}, {Name: "SWORD", Value: 8}, {Name: "SHIELD", Value: 6},
{Name: "HERBS", Value: 2}, {Name: "KEYS", Value: 1}, {Name: "MAP", Value: 1}}, chart.TreemapOptions{Width: 200, Height: 90})
}
func demoCandlestick(c *ui.Context, log func(string)) {
chart.Candlestick(c, "Potion price", []chart.Candle{
{Label: "MON", Open: 10, High: 14, Low: 9, Close: 13}, {Label: "TUE", Open: 13, High: 15, Low: 8, Close: 9},
{Label: "WED", Open: 9, High: 12, Low: 7, Close: 11}, {Label: "THU", Open: 11, High: 18, Low: 11, Close: 17},
{Label: "FRI", Open: 17, High: 17, Low: 12, Close: 13},
}, chart.CandlestickOptions{Width: 200, Height: 90})
}
func demoBoxPlot(c *ui.Context, log func(string)) {
chart.BoxPlot(c, "Rolls", []chart.Series{
{Name: "D6", Values: []float64{1, 2, 3, 3, 4, 5, 6}},
{Name: "D8", Values: []float64{1, 3, 4, 5, 5, 7, 8}},
{Name: "D12", Values: []float64{2, 4, 6, 7, 9, 11, 12}},
}, chart.BoxPlotOptions{Width: 200, Height: 90})
}
func demoStepChart(c *ui.Context, log func(string)) {
chart.StepChart(c, "Level", []string{"W1", "W2", "W3", "W4", "W5", "W6"}, []float64{1, 3, 3, 4, 7, 8}, chart.StepChartOptions{Width: 200, Height: 90, Slot: pixel.Magic})
}
func demoProgressRing(c *ui.Context, log func(string)) {
k := pixel.K(c)
s := chartState(c)
// The pretend download takes four seconds.
done := k.Since(c, s.since, 10, 40)
ui.Row(c).Gap(k.D(8)).AlignItems(ui.Center).Children(func() {
chart.ProgressRing(c, "Download", float64(done)/40, chart.ProgressRingOptions{Size: 48})
if input.Button(c, "DOWNLOAD", input.ButtonOptions{Disabled: done < 40}).Clicked {
s.since = k.Now()
log("downloading more RAM")
}
})
}
func demoLegend(c *ui.Context, log func(string)) {
k := pixel.K(c)
s := chartState(c)
var shown []chart.Series
for i, h := range heroes {
if !s.hidden[i] {
shown = append(shown, h)
}
}
ui.Column(c).Gap(k.D(4)).Children(func() {
if chart.Legend(c, "Heroes", heroes, s.hidden, chart.LegendOptions{}).Changed {
log(fmt.Sprintf("hidden %v", s.hidden))
}
chart.LineChart(c, "Hero HP", week, shown, chart.LineChartOptions{Width: 200, Height: 90})
})
}
func demoAxis(c *ui.Context, log func(string)) {
k := pixel.K(c)
ui.Row(c).Gap(k.D(12)).AlignItems(ui.Center).Children(func() {
chart.Axis(c, chart.AxisOptions{Labels: []string{"LOW", "MID", "HIGH"}, Vertical: true, Length: 60})
chart.Axis(c, chart.AxisOptions{Min: 0, Max: 250, Length: 160})
})
}
func demoCrosshair(c *ui.Context, log func(string)) {
chart.Crosshair(c, "Hero HP", week, heroes, chart.CrosshairOptions{Width: 200, Height: 90})
}
func demoPolarArea(c *ui.Context, log func(string)) {
chart.PolarArea(c, "Rain", []chart.Slice{{Name: "SPRING", Value: 6, Slot: pixel.Good}, {Name: "SUMMER", Value: 3, Slot: pixel.Warn},
{Name: "AUTUMN", Value: 8, Slot: pixel.Bad}, {Name: "WINTER", Value: 4, Slot: pixel.Info}}, chart.PolarAreaOptions{Size: 80})
}
func demoPyramid(c *ui.Context, log func(string)) {
chart.Pyramid(c, "By level", []string{"LV 1-9", "LV 10-19", "LV 20-29", "LV 30+"},
chart.Series{Name: "HEROES", Values: []float64{40, 26, 12, 4}, Slot: pixel.Info},
chart.Series{Name: "MONSTERS", Values: []float64{60, 30, 18, 9}, Slot: pixel.Bad}, chart.PyramidOptions{Width: 220})
}
func demoSpectrumBars(c *ui.Context, log func(string)) {
k := pixel.K(c)
s := chartState(c)
n := 0
if s.play && !k.Reduce() {
n = k.Tick(c, 8)
}
levels := make([]float64, 12)
for i := range levels {
// A made-up song: a slow swell times a quick wobble.
t, f := float64(n), float64(i)
levels[i] = 0.5 + 0.45*math.Sin(t*0.7+f*1.3)*math.Sin(t*0.13+f*0.4)
}
ui.Row(c).Gap(k.D(8)).AlignItems(ui.Center).Children(func() {
chart.SpectrumBars(c, "Music", levels, chart.SpectrumBarsOptions{Segments: 12})
if input.Toggle(c, "PLAY", &s.play, input.ToggleOptions{Size: input.Small}).Changed {
log(fmt.Sprintf("play %v", s.play))
}
})
}
func demoWinLoss(c *ui.Context, log func(string)) {
k := pixel.K(c)
s := chartState(c)
ui.Row(c).Gap(k.D(8)).AlignItems(ui.Center).Children(func() {
chart.WinLoss(c, "Arena", s.matches, chart.WinLossOptions{})
if input.Button(c, "PLAY", input.ButtonOptions{Size: input.Small}).Clicked {
r := s.dice.IntN(3) - 1
// The strip keeps the last twenty matches.
s.matches = append(s.matches[max(0, len(s.matches)-19):], r)
log([]string{"lost", "draw", "won"}[r+1])
}
})
}