aboutsummaryrefslogtreecommitdiffstats
path: root/widgets/sky.lua
blob: 7dbc3993b22c7245576cc1c1db8a11b46cfef7aa (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
-- Sky: sunrise, sunset, day length and the moon, over the daylight arc.
--
-- Adapted from the Conky-Mirage theme's sky tile by alexsson-xexpanderx
-- (see lib/weather.lua). The arc is the one the old weather card drew, minus
-- its time labels: the rows carry the times now.

local card = require 'lib.card'
local data = require 'lib.data'
local weather = require 'lib.weather'

local M = {}

-- The dot is placed by evaluating the curve at t rather than by computing a
-- point on a circle: the curve IS the path, so evaluating it keeps the dot on
-- the arc if the control points are ever adjusted.
local function draw_arc(cr, x, y, w, h, colors, rise, set, now)
  local pad = math.max(4, w * 0.012)
  local x0, x1 = x + pad, x + w - pad
  local base = y + h - pad
  local top = y + h * 0.08

  -- Cubic Bezier control points, chosen so the curve peaks near the middle.
  local p0 = { x0, base }
  local c1 = { x0 + (x1 - x0) * 0.22, top - h * 0.14 }
  local c2 = { x1 - (x1 - x0) * 0.22, top - h * 0.14 }
  local p3 = { x1, base }

  -- The horizon the sun rises from and sets into.
  card.rgba(cr, colors.rule, 0.6)
  cairo_set_line_width(cr, 1)
  cairo_move_to(cr, x0, base)
  cairo_line_to(cr, x1, base)
  cairo_stroke(cr)

  card.rgba(cr, colors.rule, 0.9)
  cairo_set_line_width(cr, 1.5)
  cairo_move_to(cr, p0[1], p0[2])
  cairo_curve_to(cr, c1[1], c1[2], c2[1], c2[2], p3[1], p3[2])
  cairo_stroke(cr)

  local t = weather.sun_t(now, rise, set)
  local function bez(a, b, c, d)
    local u = 1 - t
    return u * u * u * a + 3 * u * u * t * b + 3 * u * t * t * c + t * t * t * d
  end
  -- At night the dot is parked at an end, so it takes the dim colour: a bright
  -- dot on the horizon would read as a sun that is still up.
  card.rgba(cr, weather.is_day(now, rise, set) and colors.body or colors.label)
  cairo_new_path(cr)
  cairo_arc(cr, bez(p0[1], c1[1], c2[1], p3[1]), bez(p0[2], c1[2], c2[2], p3[2]),
    math.max(3, math.min(9, h * 0.045)), 0, math.pi * 2)
  cairo_fill(cr)
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, 'SKY', 'no weather data', 'bin/weather-fetch.sh')
    return
  end

  local now = os.time()
  local rise, set = w.sun.rise, w.sun.set
  local phase, lit = weather.moon(now)
  local rows = {
    { 'SUNRISE', weather.clock(rise) },
    { 'SUNSET', weather.clock(set) },
    { 'DAYLIGHT', weather.duration(set - rise) },
    { 'MOON', weather.num(lit * 100, '%') },
  }
  local y = card.standard(cr, inner, colors, {
    icon = card.ICON.sky, title = 'SKY',
    big = weather.moon_icon(phase), big_font = card.FONT_MONO,
    big_color = colors.heading, rows = rows, share = 0.65,
  })

  -- An arch taller than about 0.6 of its width reads as a door, not a day.
  local arc_h = math.min(inner.y + inner.h - y, inner.w * 0.6)
  if arc_h >= 50 then
    draw_arc(cr, inner.x, inner.y + inner.h - arc_h, inner.w, arc_h, colors, rise, set, now)
  end
end

return M