aboutsummaryrefslogtreecommitdiffstats
path: root/widgets/hourly.lua
blob: 82f758549bcb029e3fe3cd63f39bf9f749d99994 (plain)
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
-- 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.11, w / 8 * 0.22))
  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.
  card.rgba(cr, colors.value, 0.3)
  for i = 1, n do
    local p = math.min(hours[i].precip or 0, 100)
    if p > 0 then
      local bh = (base - top) * 0.5 * p / 100
      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.4,
  })
  draw_curve(cr, inner.x, y, inner.w, inner.y + inner.h - y, colors, w.hours, S)
end

return M