diff --git a/internal/gopow/annotater.go b/internal/gopow/annotater.go new file mode 100644 index 0000000..f4cc4bf --- /dev/null +++ b/internal/gopow/annotater.go @@ -0,0 +1,170 @@ +package gopow + +import ( + "image" + "math" + "time" + + "code.google.com/p/freetype-go/freetype" + log "github.com/Sirupsen/logrus" + "github.com/dustin/go-humanize" + + "github.com/dhogborg/rtl-gopow/internal/resources" +) + +// font configuration +const ( + dpi float64 = 72 + fontfile string = "resources/fonts/luxisr.ttf" + hinting string = "none" + size float64 = 18 +) + +type Annotator struct { + image *image.RGBA + table *TableComplex + + context *freetype.Context +} + +func NewAnnotator(img *image.RGBA, table *TableComplex) (*Annotator, error) { + + a := &Annotator{ + image: img, + table: table, + } + + err := a.init() + if err != nil { + return nil, err + } + + return a, nil + +} + +func (a *Annotator) init() error { + + // load the font + fontBytes, err := resources.Asset(fontfile) + if err != nil { + return err + } + + font, err := freetype.ParseFont(fontBytes) + if err != nil { + return err + } + + // Initialize the context. + fg := image.White + + a.context = freetype.NewContext() + a.context.SetDPI(dpi) + a.context.SetFont(font) + a.context.SetFontSize(size) + + a.context.SetClip(a.image.Bounds()) + a.context.SetDst(a.image) + a.context.SetSrc(fg) + + switch hinting { + default: + a.context.SetHinting(freetype.NoHinting) + case "full": + a.context.SetHinting(freetype.FullHinting) + } + + return nil +} + +func (a *Annotator) DrawXScale() error { + + log.WithFields(log.Fields{ + "hzLow": a.table.HzLow, + "hzHigh": a.table.HzHigh, + }).Debug("annotate X scale") + + // how many samples? + count := int(math.Floor(float64(a.table.Bins) / float64(500))) + + log.WithFields(log.Fields{ + "labels": count, + }).Debug("annotate X scale") + + hzPerLabel := float64(a.table.HzHigh-a.table.HzLow) / float64(count) + pxPerLabel := int(math.Floor(float64(a.table.Bins) / float64(count))) + + for si := 0; si < count; si++ { + + hz := a.table.HzLow + (float64(si) * hzPerLabel) + px := si * pxPerLabel + + str := humanize.SI(hz, "Hz") + + // draw a guideline on the exact frequency + for i := 0; i < 50; i++ { + a.image.Set(px, i, image.White) + } + + // draw the text + pt := freetype.Pt(px+10, 30) + _, _ = a.context.DrawString(str, pt) + + } + + return nil +} + +func (a *Annotator) DrawYScale() error { + + log.WithFields(log.Fields{ + "timestart": a.table.TimeStart.String(), + "timeend": a.table.TimeEnd.String(), + }).Debug("annotate Y scale") + + start, end := a.table.TimeStart, a.table.TimeEnd + + // how many samples? + count := int(math.Floor(float64(a.table.Integrations) / float64(100))) + + uStart := start.Unix() + uEnd := end.Unix() + + secsPerLabel := int(math.Floor(float64(uEnd-uStart) / float64(count))) + pxPerLabel := int(math.Floor(float64(a.table.Integrations) / float64(count))) + + log.WithFields(log.Fields{ + "labels": count, + "secsPerLabel": secsPerLabel, + "pxPerLabel": pxPerLabel, + }).Debug("annotate Y scale") + + for si := 0; si < count; si++ { + + secs := time.Duration(secsPerLabel * si * int(time.Second)) + px := si * pxPerLabel + + var str string = "" + + if si == 0 { + str = start.String() + } else { + point := start.Add(secs) + str = point.Format("15:04:05") + } + + // draw a guideline on the exact time + for i := 0; i < 75; i++ { + a.image.Set(i, px, image.White) + } + + // draw the text, 3 px margin to the line + pt := freetype.Pt(3, px-3) + _, _ = a.context.DrawString(str, pt) + + } + + return nil + +} diff --git a/internal/gopow/gopow.go b/internal/gopow/gopow.go index 3e869f1..1262549 100644 --- a/internal/gopow/gopow.go +++ b/internal/gopow/gopow.go @@ -5,9 +5,11 @@ import ( "image" "image/png" "os" + "time" log "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" + "github.com/dustin/go-humanize" ) type RunConfig struct { @@ -17,8 +19,9 @@ type RunConfig struct { } type GoPow struct { - config *RunConfig - image *image.RGBA + config *RunConfig + image *image.RGBA + timestamp time.Time } func NewGoPow(c *cli.Context) (*GoPow, error) { @@ -61,6 +64,7 @@ func NewGoPow(c *cli.Context) (*GoPow, error) { func (g *GoPow) Render() error { log.Debug("staring render") + g.timestamp = time.Now() table, err := NewTable(g.config.InputFile) if err != nil { @@ -75,16 +79,14 @@ func (g *GoPow) Render() error { } } - // add some frequency and date annotation - err = table.AnnotateXScale(g.image) + annotator, err := NewAnnotator(g.image, table) if err != nil { return err } - err = table.AnnotateYScale(g.image) - if err != nil { - return err - } + // add some frequency and date annotation + annotator.DrawXScale() + annotator.DrawYScale() return nil } @@ -105,5 +107,8 @@ func (g *GoPow) Write() error { return err } + duration := humanize.RelTime(g.timestamp, time.Now(), "", "") + log.Info("GoPow finished in " + duration) + return nil } diff --git a/internal/gopow/table.go b/internal/gopow/table.go index c799b5a..46fc3c0 100644 --- a/internal/gopow/table.go +++ b/internal/gopow/table.go @@ -9,20 +9,9 @@ import ( "strings" "time" - "code.google.com/p/freetype-go/freetype" log "github.com/Sirupsen/logrus" "github.com/dustin/go-humanize" "github.com/lucasb-eyer/go-colorful" - - "github.com/dhogborg/rtl-gopow/internal/resources" -) - -// font configuration -var ( - dpi float64 = 72 - fontfile string = "resources/fonts/luxisr.ttf" - hinting string = "none" - size float64 = 34 ) type TableComplex struct { @@ -209,89 +198,3 @@ func (t *TableComplex) ColorAt(x, y int) color.Color { return colorful.Hsv(hue, 1, 0.90) } - -func (t *TableComplex) AnnotateXScale(img *image.RGBA) error { - - log.WithFields(log.Fields{ - "hzLow": t.HzLow, - "hzHigh": t.HzHigh, - }).Debug("annotate X scale") - - // load the font - fontBytes, err := resources.Asset(fontfile) - if err != nil { - return err - } - - font, err := freetype.ParseFont(fontBytes) - if err != nil { - return err - } - - // Initialize the context. - fg := image.White - ruler := image.White - - c := freetype.NewContext() - c.SetDPI(dpi) - c.SetFont(font) - c.SetFontSize(size) - - c.SetClip(img.Bounds()) - c.SetDst(img) - c.SetSrc(fg) - - switch hinting { - default: - c.SetHinting(freetype.NoHinting) - case "full": - c.SetHinting(freetype.FullHinting) - } - - // how many samples? - count := int(math.Floor(float64(t.Bins) / float64(500))) - - log.WithFields(log.Fields{ - "labels": count, - }).Debug("annotate X scale") - - hzPerLable := float64(t.HzHigh-t.HzLow) / float64(count) - pxPerLable := int(math.Floor(float64(t.Bins) / float64(count))) - - for si := 0; si < count; si++ { - - hz := t.HzLow + (float64(si) * hzPerLable) - px := si * pxPerLable - - str := humanize.SI(hz, "Hz") - - // draw a guideline on the exact frequency - for i := 0; i < 50; i++ { - img.Set(px, i, ruler) - } - - // draw the text - pt := freetype.Pt(px+10, 30) - _, _ = c.DrawString(str, pt) - - } - - return nil -} - -func (t *TableComplex) AnnotateYScale(img *image.RGBA) error { - - log.WithFields(log.Fields{ - "timestart": t.TimeStart.String(), - "timeend": t.TimeEnd.String(), - }).Debug("annotate Y scale") - - // how many samples? - const count = 10 - - log.WithFields(log.Fields{ - "labels": count, - }).Debug("annotate Y scale") - - return nil -}