func (m Measured) Draw(ctx context.Context, cols []draw.Column,
	canvas *draw.RGB) *draw.RGB {
	w, h := 0, 0
	if canvas != nil {
		w, h = canvas.Size()
	}
	if w < m.Width || h < m.Height {
		canvas = draw.NewRGB(m.Width, m.Height)
	}
	max := float64(0)
	x := 0
	for _, col := range cols {
		if col.ObsSec > max {
			max = col.ObsSec
			x = col.X
		}
	}
	label_text := fmt.Sprintf("obs/sec: %#.3g", float64(max))
	label_height := (m.bounds.Max.Y - m.bounds.Min.Y).Ceil()
	label_width := (m.bounds.Max.X - m.bounds.Min.X).Ceil()
	for _, col := range cols {
		sat := 255 - byte(float64(col.ObsSec)/float64(max)*255)
		c := draw.Color{sat, sat, sat}
		for y := 0; y < m.opts.Height; y++ {
			for x := 0; x < col.W; x++ {
				canvas.Set(x+col.X, y+padding+label_height, c)
			}
		}
	}
	end := x + label_width
	if end > m.opts.Width {
		x = m.opts.Width - label_width
	}
	(&font.Drawer{
		Dst:  canvas.AsImage(),
		Src:  image.Black,
		Face: m.opts.Face,
		Dot: fixed.Point26_6{
			Y: -m.bounds.Min.Y,
			X: fixed.I(x),
		},
	}).DrawString(label_text)
	return canvas
}
Draw performs the drawing of the data on to the canvas. The canvas is
expected to be large enough to handle the drawing. If the canvas is nil,
one is allocated. In either case, the canvas is returned.
package observations
import (
	"context"
	"fmt"
	"image"
	"github.com/vivint/rothko/draw"
	"golang.org/x/image/font"
	"golang.org/x/image/math/fixed"
)
const (
	padding = 2
)
type Measured struct {
	Width int
	Height int
	opts   Options
	bounds fixed.Rectangle26_6
}
type Options struct {
	Face font.Face
	Width int
	Height int
}
func Draw(ctx context.Context, cols []draw.Column, opts Options) *draw.RGB {
	return Measure(ctx, opts).Draw(ctx, cols, nil)
}
func Measure(ctx context.Context, opts Options) Measured {
	bounds, _ := font.BoundString(opts.Face, "obs/sec: 0.00e-00")
	label_height := (bounds.Max.Y - bounds.Min.Y).Ceil()
	return Measured{
		Width:  opts.Width,
		Height: opts.Height + padding + label_height,
		opts:   opts,
		bounds: bounds,
	}
}