diff options
Diffstat (limited to 'lib/card.lua')
| -rw-r--r-- | lib/card.lua | 54 |
1 files changed, 54 insertions, 0 deletions
diff --git a/lib/card.lua b/lib/card.lua index f25c393..337347e 100644 --- a/lib/card.lua +++ b/lib/card.lua @@ -270,4 +270,58 @@ function M.truncate(s, limit) return s:sub(1, cut - 1) .. '\u{2026}' end +-- A line chart: several series sharing one baseline and one vertical scale. +-- +-- `series` is a list of { values = <array>, colour = <rgb> }. `max` is the +-- shared ceiling; passing one rather than computing per series is the whole +-- point, because two series scaled independently lie about their relative +-- size: a 200kB/s upload would draw the same height as a 40MB/s download. +-- +-- Values are drawn oldest-left, one per array entry, so a caller sizing its +-- history to the pixel width gets one sample per column and no interpolation. +-- A series shorter than its buffer draws only what it has, which is what a +-- freshly started dashboard shows while the window fills. +function M.plot(cr, x, y, w, h, series, max, colors) + if not (w > 0 and h > 0) then return end + max = tonumber(max) or 0 + if max <= 0 then max = 1 end -- an idle link is a flat line, not a division by zero + + local prev_width = cairo_get_line_width(cr) + local prev_cap = cairo_get_line_cap(cr) + + -- The baseline, so an empty chart still reads as a chart rather than a gap. + M.rgba(cr, colors.rule, 0.5) + cairo_new_path(cr) + cairo_set_line_width(cr, 1) + cairo_move_to(cr, x, y + h) + cairo_line_to(cr, x + w, y + h) + cairo_stroke(cr) + + cairo_set_line_width(cr, math.max(1.5, h * 0.02)) + cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND) + + for _, s in ipairs(series) do + local v = s.values + local n = #v + if n >= 2 then + -- One sample per column when the caller sized its buffer to the width. + local step = w / math.max(n - 1, 1) + M.rgba(cr, s.colour, 1) + cairo_new_path(cr) + for i = 1, n do + local frac = v[i] / max + if frac < 0 then frac = 0 elseif frac > 1 then frac = 1 end + local px = x + (i - 1) * step + local py = y + h - frac * h + if i == 1 then cairo_move_to(cr, px, py) else cairo_line_to(cr, px, py) end + end + cairo_stroke(cr) + end + end + + -- Leave the line state as it was found, for the same reason card.ring does. + cairo_set_line_width(cr, prev_width) + cairo_set_line_cap(cr, prev_cap) +end + return M |
