1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
-- Hourly: the next 24 hours as a temperature curve over rain-chance bars.
--
-- Adapted from the Conky-Mirage theme's hourly tile by alexsson-xexpanderx
-- (see lib/weather.lua), redrawn in this repo's palette, glyphs and fonts.
local card = require 'lib.card'
local data = require 'lib.data'
local weather = require 'lib.weather'
local M = {}
-- The band, top to bottom: glyph line, temperature line, curve, hour line.
-- Every third hour is labelled; all 24 shape the curve.
local function draw_curve(cr, x, y, w, h, colors, hours, S)
local n = math.min(#hours, 24)
local label = math.max(9, math.min(h * 0.135, w / 8 * 0.22, S * 0.32))
local glyph = label * 1.5
local slot = w / n
local function px(i) return x + (i - 0.5) * slot end
local top = y + glyph + label * 1.6
local base = y + h - label * 1.6
if base - top < label * 2 then return end -- no room: no curve beats a broken one
local lo, hi = math.huge, -math.huge
for i = 1, n do
local hr = hours[i]
if hr.temp then lo, hi = math.min(lo, hr.temp), math.max(hi, hr.temp) end
end
if lo > hi then return end
-- A flat day still needs a span to divide by; 1 degree keeps it mid-band.
local span = math.max(hi - lo, 1)
local function py(t) return base - (base - top) * (0.15 + 0.7 * (t - lo) / span) end
-- Rain chance as bars rising from the baseline, under the curve. Typical
-- chances are a few percent, which scaled honestly is a 1px bar no alpha can
-- rescue, so any nonzero chance gets a floor; the half alpha keeps the bars
-- a backdrop to the full-strength curve.
card.rgba(cr, colors.value, 0.5)
for i = 1, n do
local p = math.min(hours[i].precip or 0, 100)
if p > 0 then
local bh = math.max((base - top) * 0.5 * p / 100, label * 0.4)
cairo_rectangle(cr, px(i) - slot * 0.3, base - bh, slot * 0.6, bh)
end
end
cairo_fill(cr)
card.rgba(cr, colors.rule, 0.6)
cairo_set_line_width(cr, 1)
cairo_move_to(cr, x, base)
cairo_line_to(cr, x + w, base)
cairo_stroke(cr)
-- The curve, broken where an hour has no temperature rather than bridged.
card.rgba(cr, colors.highlight)
cairo_set_line_width(cr, math.max(1.5, label * 0.15))
local pen = false
for i = 1, n do
local hr = hours[i]
if hr.temp then
if pen then cairo_line_to(cr, px(i), py(hr.temp))
else cairo_move_to(cr, px(i), py(hr.temp)) end
pen = true
else
pen = false
end
end
cairo_stroke(cr)
for i = 1, n, 3 do
local hr = hours[i]
card.font(cr, card.FONT_MONO, glyph, false)
card.rgba(cr, colors.highlight)
card.text_centre(cr, px(i), y + glyph, weather.icon(hr.code, hr.is_day))
card.font(cr, card.FONT_MONO, label, false)
card.rgba(cr, colors.value)
card.text_centre(cr, px(i), y + glyph + label * 1.3, weather.deg(hr.temp))
card.rgba(cr, colors.label)
card.text_centre(cr, px(i), y + h - label * 0.2,
weather.clock(hr.t, '%H'))
end
end
function M.draw(cr, rect, colors)
local inner = card.card(cr, rect, colors)
local w = weather.parse(data.slurp(weather.CACHE))
if not w then
card.notice(cr, inner, colors, 'NEXT 24H', 'no weather data', 'bin/weather-fetch.sh')
return
end
-- hours[1] is the current hour, so the big value is the one after it.
local y, S = card.standard(cr, inner, colors, {
icon = card.ICON.hourly, title = 'NEXT 24H',
big = weather.deg((w.hours[2] or {}).temp), share = 0.56,
})
draw_curve(cr, inner.x, y, inner.w, inner.y + inner.h - y, colors, w.hours, S)
end
return M
|