From afc220e25c62991fb561d7eea4f951c60be875de Mon Sep 17 00:00:00 2001 From: Will Charczuk Date: Fri, 8 Jul 2016 09:17:28 -0700 Subject: [PATCH] you can now specify tick values and labels manually. --- chart.go | 89 +++++++++++++++++++++++++--------------------- range.go | 7 ++++ testserver/main.go | 8 ----- 3 files changed, 55 insertions(+), 49 deletions(-) diff --git a/chart.go b/chart.go index 7c3b06e..4e867bd 100644 --- a/chart.go +++ b/chart.go @@ -172,7 +172,7 @@ func (c Chart) initRanges(canvasBox Box) (xrange Range, yrange Range) { if xrange.Formatter == nil { xrange.Formatter = s.GetXFormatter() } - if yrange.Format == nil { + if yrange.Formatter == nil { yrange.Formatter = s.GetYFormatter() } } @@ -187,6 +187,9 @@ func (c Chart) initRanges(canvasBox Box) (xrange Range, yrange Range) { if c.XRange.Formatter != nil { xrange.Formatter = c.XRange.Formatter } + if c.XRange.Ticks != nil { + xrange.Ticks = c.XRange.Ticks + } xrange.Domain = canvasBox.Width if c.YRange.IsZero() { @@ -199,6 +202,9 @@ func (c Chart) initRanges(canvasBox Box) (xrange Range, yrange Range) { if c.YRange.Formatter != nil { yrange.Formatter = c.YRange.Formatter } + if c.YRange.Ticks != nil { + yrange.Ticks = c.YRange.Ticks + } yrange.Domain = canvasBox.Height return @@ -244,68 +250,69 @@ func (c Chart) drawAxes(r Renderer, canvasBox Box, xrange, yrange Range) { } } +func (c Chart) generateRangeTicks(r Range, tickCount int, offset float64) []Tick { + var ticks []Tick + rangeTicks := Slices(tickCount, r.Max-r.Min) + for _, rv := range rangeTicks { + ticks = append(ticks, Tick{ + RangeValue: rv + offset, + Label: r.Format(rv + offset), + }) + } + return ticks +} + func (c Chart) drawYAxisLabels(r Renderer, canvasBox Box, yrange Range) { tickFontSize := c.Axes.GetFontSize(DefaultAxisFontSize) + asw := c.getAxisWidth() + tx := canvasBox.Right + DefaultFinalLabelDeltaWidth + asw r.SetFontColor(c.Axes.GetFontColor(DefaultAxisColor)) r.SetFontSize(tickFontSize) - minimumTickHeight := tickFontSize + DefaultMinimumTickVerticalSpacing - tickCount := int(math.Floor(float64(yrange.Domain) / float64(minimumTickHeight))) + ticks := yrange.Ticks + if ticks == nil { + minimumTickHeight := tickFontSize + DefaultMinimumTickVerticalSpacing + tickCount := int(math.Floor(float64(yrange.Domain) / float64(minimumTickHeight))) - if tickCount > DefaultMaxTickCount { - tickCount = DefaultMaxTickCount + if tickCount > DefaultMaxTickCount { + tickCount = DefaultMaxTickCount + } + ticks = c.generateRangeTicks(yrange, tickCount, yrange.Min) } - rangeTicks := Slices(tickCount, yrange.Max-yrange.Min) - domainTicks := Slices(tickCount, float64(yrange.Domain)) - - asw := c.getAxisWidth() - tx := canvasBox.Right + DefaultFinalLabelDeltaWidth + asw - - count := len(rangeTicks) - if len(domainTicks) < count { - count = len(domainTicks) //guard against mismatched array sizes. - } - - for index := 0; index < count; index++ { - v := rangeTicks[index] + yrange.Min - y := domainTicks[index] - ty := canvasBox.Bottom - int(y) - r.Text(yrange.Format(v), tx, ty) + for _, t := range ticks { + v := t.RangeValue + y := yrange.Translate(v) + ty := int(y) + r.Text(t.Label, tx, ty) } } func (c Chart) drawXAxisLabels(r Renderer, canvasBox Box, xrange Range) { tickFontSize := c.Axes.GetFontSize(DefaultAxisFontSize) + ty := canvasBox.Bottom + DefaultXAxisMargin + int(tickFontSize) r.SetFontColor(c.Axes.GetFontColor(DefaultAxisColor)) r.SetFontSize(tickFontSize) - maxLabelWidth := 60 + ticks := xrange.Ticks + if ticks == nil { + maxLabelWidth := 60 + minimumTickWidth := maxLabelWidth + DefaultMinimumTickHorizontalSpacing + tickCount := int(math.Floor(float64(xrange.Domain) / float64(minimumTickWidth))) - minimumTickWidth := maxLabelWidth + DefaultMinimumTickHorizontalSpacing - tickCount := int(math.Floor(float64(xrange.Domain) / float64(minimumTickWidth))) - - if tickCount > DefaultMaxTickCount { - tickCount = DefaultMaxTickCount + if tickCount > DefaultMaxTickCount { + tickCount = DefaultMaxTickCount + } + ticks = c.generateRangeTicks(xrange, tickCount, xrange.Min) } - rangeTicks := Slices(tickCount, xrange.Max-xrange.Min) - domainTicks := Slices(tickCount, float64(xrange.Domain)) - - ty := canvasBox.Bottom + DefaultXAxisMargin + int(tickFontSize) - - count := len(rangeTicks) - if len(domainTicks) < count { - count = len(domainTicks) //guard against mismatched array sizes. - } - - for index := 0; index < count; index++ { - v := rangeTicks[index] + xrange.Min - x := domainTicks[index] + for _, t := range ticks { + v := t.RangeValue + x := xrange.Translate(v) tx := canvasBox.Left + int(x) - r.Text(xrange.Format(v), tx, ty) + r.Text(t.Label, tx, ty) } } diff --git a/range.go b/range.go index ad8a7ce..d3ab4a7 100644 --- a/range.go +++ b/range.go @@ -7,11 +7,18 @@ import ( "github.com/blendlabs/go-util" ) +// Tick represents a label on an axis. +type Tick struct { + RangeValue float64 + Label string +} + // Range represents a continuous range, type Range struct { Min float64 Max float64 Domain int + Ticks []Tick Formatter Formatter } diff --git a/testserver/main.go b/testserver/main.go index f5a54ab..0d26299 100644 --- a/testserver/main.go +++ b/testserver/main.go @@ -1,10 +1,8 @@ package main import ( - "fmt" "log" - "github.com/blendlabs/go-util" "github.com/wcharczuk/go-chart" "github.com/wcharczuk/go-web" ) @@ -29,12 +27,6 @@ func main() { YRange: chart.Range{ Min: 0.0, Max: 7.0, - Formatter: func(v interface{}) string { - if typed, isTyped := v.(float64); isTyped { - return fmt.Sprintf("%.4f", typed) - } - return util.StringEmpty - }, }, FinalValueLabel: chart.Style{ Show: true,