inching up coverage.
This commit is contained in:
parent
18a6e3eed1
commit
56da554741
10 changed files with 315 additions and 92 deletions
|
@ -54,7 +54,7 @@ graph := chart.Chart{
|
|||
},
|
||||
Annotations: []chart.Annotation{
|
||||
chart.Annotation{
|
||||
X: float64(xvalues[len(xvalues)-1].Unix()), //todo: helpers for this.
|
||||
X: chart.TimeToFloat64(xvalues[len(xvalues)-1]),
|
||||
Y: yvalues[len(yvalues)-1],
|
||||
Label: chart.FloatValueFormatter(yvalues[len(yvalues)-1]),
|
||||
},
|
||||
|
|
|
@ -1,45 +1,6 @@
|
|||
package chart
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CreateContinuousSeriesLastValueLabel returns a (1) value annotation series.
|
||||
func CreateContinuousSeriesLastValueLabel(name string, xvalues, yvalues []float64, valueFormatter ValueFormatter) AnnotationSeries {
|
||||
return AnnotationSeries{
|
||||
Name: name,
|
||||
Style: Style{
|
||||
Show: true,
|
||||
StrokeColor: GetDefaultSeriesStrokeColor(0),
|
||||
},
|
||||
Annotations: []Annotation{
|
||||
Annotation{
|
||||
X: xvalues[len(xvalues)-1],
|
||||
Y: yvalues[len(yvalues)-1],
|
||||
Label: valueFormatter(yvalues[len(yvalues)-1]),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// CreateTimeSeriesLastValueLabel returns a (1) value annotation series.
|
||||
func CreateTimeSeriesLastValueLabel(name string, xvalues []time.Time, yvalues []float64, valueFormatter ValueFormatter) AnnotationSeries {
|
||||
return AnnotationSeries{
|
||||
Name: name,
|
||||
Style: Style{
|
||||
Show: true,
|
||||
StrokeColor: GetDefaultSeriesStrokeColor(0),
|
||||
},
|
||||
Annotations: []Annotation{
|
||||
Annotation{
|
||||
X: float64(xvalues[len(xvalues)-1].Unix()),
|
||||
Y: yvalues[len(yvalues)-1],
|
||||
Label: valueFormatter(yvalues[len(yvalues)-1]),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
import "math"
|
||||
|
||||
// Annotation is a label on the chart.
|
||||
type Annotation struct {
|
||||
|
|
8
style.go
8
style.go
|
@ -15,9 +15,7 @@ type Style struct {
|
|||
|
||||
StrokeWidth float64
|
||||
StrokeColor drawing.Color
|
||||
|
||||
FillColor drawing.Color
|
||||
|
||||
FontSize float64
|
||||
FontColor drawing.Color
|
||||
Font *truetype.Font
|
||||
|
@ -25,7 +23,7 @@ type Style struct {
|
|||
|
||||
// IsZero returns if the object is set or not.
|
||||
func (s Style) IsZero() bool {
|
||||
return s.StrokeColor.IsZero() && s.FillColor.IsZero() && s.StrokeWidth == 0 && s.FontSize == 0 && s.Font == nil
|
||||
return s.StrokeColor.IsZero() && s.FillColor.IsZero() && s.StrokeWidth == 0 && s.FontColor.IsZero() && s.FontSize == 0 && s.Font == nil
|
||||
}
|
||||
|
||||
// GetStrokeColor returns the stroke color.
|
||||
|
@ -107,12 +105,12 @@ func (s Style) GetPadding(defaults ...Box) Box {
|
|||
|
||||
// WithDefaultsFrom coalesces two styles into a new style.
|
||||
func (s Style) WithDefaultsFrom(defaults Style) (final Style) {
|
||||
final.StrokeColor = s.GetStrokeColor(defaults.StrokeColor)
|
||||
final.StrokeWidth = s.GetStrokeWidth(defaults.StrokeWidth)
|
||||
final.FillColor = s.GetFillColor(defaults.FillColor)
|
||||
final.FontColor = s.GetFontColor(defaults.FontColor)
|
||||
final.Font = s.GetFont(defaults.Font)
|
||||
final.Padding = s.GetPadding(defaults.Padding)
|
||||
final.StrokeColor = s.GetStrokeColor(defaults.StrokeColor)
|
||||
final.StrokeWidth = s.GetStrokeWidth(defaults.StrokeWidth)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
237
style_test.go
Normal file
237
style_test.go
Normal file
|
@ -0,0 +1,237 @@
|
|||
package chart
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/blendlabs/go-assert"
|
||||
"github.com/golang/freetype/truetype"
|
||||
"github.com/wcharczuk/go-chart/drawing"
|
||||
)
|
||||
|
||||
func TestStyleIsZero(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
zero := Style{}
|
||||
assert.True(zero.IsZero())
|
||||
|
||||
strokeColor := Style{StrokeColor: drawing.ColorWhite}
|
||||
assert.False(strokeColor.IsZero())
|
||||
|
||||
fillColor := Style{FillColor: drawing.ColorWhite}
|
||||
assert.False(fillColor.IsZero())
|
||||
|
||||
strokeWidth := Style{StrokeWidth: 5.0}
|
||||
assert.False(strokeWidth.IsZero())
|
||||
|
||||
fontSize := Style{FontSize: 12.0}
|
||||
assert.False(fontSize.IsZero())
|
||||
|
||||
fontColor := Style{FontColor: drawing.ColorWhite}
|
||||
assert.False(fontColor.IsZero())
|
||||
|
||||
font := Style{Font: &truetype.Font{}}
|
||||
assert.False(font.IsZero())
|
||||
}
|
||||
|
||||
func TestStyleGetStrokeColor(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
unset := Style{}
|
||||
assert.Equal(drawing.ColorTransparent, unset.GetStrokeColor())
|
||||
assert.Equal(drawing.ColorWhite, unset.GetStrokeColor(drawing.ColorWhite))
|
||||
|
||||
set := Style{StrokeColor: drawing.ColorWhite}
|
||||
assert.Equal(drawing.ColorWhite, set.GetStrokeColor())
|
||||
assert.Equal(drawing.ColorWhite, set.GetStrokeColor(drawing.ColorBlack))
|
||||
}
|
||||
|
||||
func TestStyleGetFillColor(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
unset := Style{}
|
||||
assert.Equal(drawing.ColorTransparent, unset.GetFillColor())
|
||||
assert.Equal(drawing.ColorWhite, unset.GetFillColor(drawing.ColorWhite))
|
||||
|
||||
set := Style{FillColor: drawing.ColorWhite}
|
||||
assert.Equal(drawing.ColorWhite, set.GetFillColor())
|
||||
assert.Equal(drawing.ColorWhite, set.GetFillColor(drawing.ColorBlack))
|
||||
}
|
||||
|
||||
func TestStyleGetStrokeWidth(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
unset := Style{}
|
||||
assert.Equal(DefaultStrokeWidth, unset.GetStrokeWidth())
|
||||
assert.Equal(DefaultStrokeWidth+1, unset.GetStrokeWidth(DefaultStrokeWidth+1))
|
||||
|
||||
set := Style{StrokeWidth: DefaultStrokeWidth + 2}
|
||||
assert.Equal(DefaultStrokeWidth+2, set.GetStrokeWidth())
|
||||
assert.Equal(DefaultStrokeWidth+2, set.GetStrokeWidth(DefaultStrokeWidth+1))
|
||||
}
|
||||
|
||||
func TestStyleGetFontSize(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
unset := Style{}
|
||||
assert.Equal(DefaultFontSize, unset.GetFontSize())
|
||||
assert.Equal(DefaultFontSize+1, unset.GetFontSize(DefaultFontSize+1))
|
||||
|
||||
set := Style{FontSize: DefaultFontSize + 2}
|
||||
assert.Equal(DefaultFontSize+2, set.GetFontSize())
|
||||
assert.Equal(DefaultFontSize+2, set.GetFontSize(DefaultFontSize+1))
|
||||
}
|
||||
|
||||
func TestStyleGetFontColor(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
unset := Style{}
|
||||
assert.Equal(drawing.ColorTransparent, unset.GetFontColor())
|
||||
assert.Equal(drawing.ColorWhite, unset.GetFontColor(drawing.ColorWhite))
|
||||
|
||||
set := Style{FontColor: drawing.ColorWhite}
|
||||
assert.Equal(drawing.ColorWhite, set.GetFontColor())
|
||||
assert.Equal(drawing.ColorWhite, set.GetFontColor(drawing.ColorBlack))
|
||||
}
|
||||
|
||||
func TestStyleGetFont(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
f, err := GetDefaultFont()
|
||||
assert.Nil(err)
|
||||
|
||||
unset := Style{}
|
||||
assert.Nil(unset.GetFont())
|
||||
assert.Equal(f, unset.GetFont(f))
|
||||
|
||||
set := Style{Font: f}
|
||||
assert.NotNil(set.GetFont())
|
||||
}
|
||||
|
||||
func TestStyleGetPadding(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
unset := Style{}
|
||||
assert.True(unset.GetPadding().IsZero())
|
||||
assert.False(unset.GetPadding(DefaultBackgroundPadding).IsZero())
|
||||
assert.Equal(DefaultBackgroundPadding, unset.GetPadding(DefaultBackgroundPadding))
|
||||
|
||||
set := Style{Padding: DefaultBackgroundPadding}
|
||||
assert.False(set.GetPadding().IsZero())
|
||||
assert.Equal(DefaultBackgroundPadding, set.GetPadding())
|
||||
assert.Equal(DefaultBackgroundPadding, set.GetPadding(Box{
|
||||
Top: DefaultBackgroundPadding.Top + 1,
|
||||
Left: DefaultBackgroundPadding.Left + 1,
|
||||
Right: DefaultBackgroundPadding.Right + 1,
|
||||
Bottom: DefaultBackgroundPadding.Bottom + 1,
|
||||
}))
|
||||
}
|
||||
|
||||
func TestStyleWithDefaultsFrom(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
f, err := GetDefaultFont()
|
||||
assert.Nil(err)
|
||||
|
||||
unset := Style{}
|
||||
set := Style{
|
||||
StrokeColor: drawing.ColorWhite,
|
||||
StrokeWidth: 5.0,
|
||||
FillColor: drawing.ColorWhite,
|
||||
FontColor: drawing.ColorWhite,
|
||||
Font: f,
|
||||
Padding: DefaultBackgroundPadding,
|
||||
}
|
||||
|
||||
coalesced := unset.WithDefaultsFrom(set)
|
||||
assert.Equal(set, coalesced)
|
||||
}
|
||||
|
||||
func TestStyleSVG(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
f, err := GetDefaultFont()
|
||||
assert.Nil(err)
|
||||
|
||||
set := Style{
|
||||
StrokeColor: drawing.ColorWhite,
|
||||
StrokeWidth: 5.0,
|
||||
FillColor: drawing.ColorWhite,
|
||||
FontColor: drawing.ColorWhite,
|
||||
Font: f,
|
||||
Padding: DefaultBackgroundPadding,
|
||||
}
|
||||
|
||||
svgString := set.SVG(DefaultDPI)
|
||||
assert.NotEmpty(svgString)
|
||||
assert.True(strings.Contains(svgString, "stroke:rgba(255,255,255,1.0)"))
|
||||
assert.True(strings.Contains(svgString, "stroke-width:5"))
|
||||
assert.True(strings.Contains(svgString, "fill:rgba(255,255,255,1.0)"))
|
||||
}
|
||||
|
||||
func TestStyleSVGStroke(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
set := Style{
|
||||
StrokeColor: drawing.ColorWhite,
|
||||
StrokeWidth: 5.0,
|
||||
FillColor: drawing.ColorWhite,
|
||||
FontColor: drawing.ColorWhite,
|
||||
Padding: DefaultBackgroundPadding,
|
||||
}
|
||||
svgStroke := set.SVGStroke()
|
||||
assert.False(svgStroke.StrokeColor.IsZero())
|
||||
assert.NotZero(svgStroke.StrokeWidth)
|
||||
assert.True(svgStroke.FillColor.IsZero())
|
||||
assert.True(svgStroke.FontColor.IsZero())
|
||||
}
|
||||
|
||||
func TestStyleSVGFill(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
set := Style{
|
||||
StrokeColor: drawing.ColorWhite,
|
||||
StrokeWidth: 5.0,
|
||||
FillColor: drawing.ColorWhite,
|
||||
FontColor: drawing.ColorWhite,
|
||||
Padding: DefaultBackgroundPadding,
|
||||
}
|
||||
svgFill := set.SVGFill()
|
||||
assert.False(svgFill.FillColor.IsZero())
|
||||
assert.Zero(svgFill.StrokeWidth)
|
||||
assert.True(svgFill.StrokeColor.IsZero())
|
||||
assert.True(svgFill.FontColor.IsZero())
|
||||
}
|
||||
|
||||
func TestStyleSVGFillAndStroke(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
set := Style{
|
||||
StrokeColor: drawing.ColorWhite,
|
||||
StrokeWidth: 5.0,
|
||||
FillColor: drawing.ColorWhite,
|
||||
FontColor: drawing.ColorWhite,
|
||||
Padding: DefaultBackgroundPadding,
|
||||
}
|
||||
svgFillAndStroke := set.SVGFillAndStroke()
|
||||
assert.False(svgFillAndStroke.FillColor.IsZero())
|
||||
assert.NotZero(svgFillAndStroke.StrokeWidth)
|
||||
assert.False(svgFillAndStroke.StrokeColor.IsZero())
|
||||
assert.True(svgFillAndStroke.FontColor.IsZero())
|
||||
}
|
||||
|
||||
func TestStyleSVGText(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
set := Style{
|
||||
StrokeColor: drawing.ColorWhite,
|
||||
StrokeWidth: 5.0,
|
||||
FillColor: drawing.ColorWhite,
|
||||
FontColor: drawing.ColorWhite,
|
||||
Padding: DefaultBackgroundPadding,
|
||||
}
|
||||
svgStroke := set.SVGText()
|
||||
assert.True(svgStroke.StrokeColor.IsZero())
|
||||
assert.Zero(svgStroke.StrokeWidth)
|
||||
assert.True(svgStroke.FillColor.IsZero())
|
||||
assert.False(svgStroke.FontColor.IsZero())
|
||||
}
|
12
tick.go
12
tick.go
|
@ -1,5 +1,17 @@
|
|||
package chart
|
||||
|
||||
// GenerateTicksWithStep generates a set of ticks.
|
||||
func GenerateTicksWithStep(ra Range, step float64, vf ValueFormatter) []Tick {
|
||||
var ticks []Tick
|
||||
for cursor := ra.Min; cursor < ra.Max; cursor += step {
|
||||
ticks = append(ticks, Tick{
|
||||
Value: cursor,
|
||||
Label: vf(cursor),
|
||||
})
|
||||
}
|
||||
return ticks
|
||||
}
|
||||
|
||||
// Tick represents a label on an axis.
|
||||
type Tick struct {
|
||||
Value float64
|
||||
|
|
1
tick_test.go
Normal file
1
tick_test.go
Normal file
|
@ -0,0 +1 @@
|
|||
package chart
|
5
util.go
5
util.go
|
@ -5,6 +5,11 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// TimeToFloat64 returns a float64 representation of a time.
|
||||
func TimeToFloat64(t time.Time) float64 {
|
||||
return float64(t.Unix())
|
||||
}
|
||||
|
||||
// MinAndMax returns both the min and max in one pass.
|
||||
func MinAndMax(values ...float64) (min float64, max float64) {
|
||||
if len(values) == 0 {
|
||||
|
|
38
value_formatter_test.go
Normal file
38
value_formatter_test.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
package chart
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/blendlabs/go-assert"
|
||||
)
|
||||
|
||||
func TestTimeValueFormatterWithFormat(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
d := time.Now()
|
||||
di := d.Unix()
|
||||
df := float64(di)
|
||||
|
||||
s := TimeValueFormatterWithFormat(d, DefaultDateFormat)
|
||||
si := TimeValueFormatterWithFormat(di, DefaultDateFormat)
|
||||
sf := TimeValueFormatterWithFormat(df, DefaultDateFormat)
|
||||
assert.Equal(s, si)
|
||||
assert.Equal(s, sf)
|
||||
|
||||
sd := TimeValueFormatter(d)
|
||||
sdi := TimeValueFormatter(di)
|
||||
sdf := TimeValueFormatter(df)
|
||||
assert.Equal(s, sd)
|
||||
assert.Equal(s, sdi)
|
||||
assert.Equal(s, sdf)
|
||||
}
|
||||
|
||||
func TestFloatValueFormatterWithFormat(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
v := 123.456
|
||||
sv := FloatValueFormatterWithFormat(v, "%.3f")
|
||||
assert.Equal("123.456", sv)
|
||||
assert.Equal("", FloatValueFormatterWithFormat(123, "%.3f"))
|
||||
}
|
29
xaxis.go
29
xaxis.go
|
@ -37,7 +37,13 @@ func (xa XAxis) GetTicks(r Renderer, ra Range, vf ValueFormatter) []Tick {
|
|||
|
||||
func (xa XAxis) generateTicks(r Renderer, ra Range, vf ValueFormatter) []Tick {
|
||||
step := xa.getTickStep(r, ra, vf)
|
||||
return xa.generateTicksWithStep(ra, step, vf)
|
||||
return GenerateTicksWithStep(ra, step, vf)
|
||||
}
|
||||
|
||||
func (xa XAxis) getTickStep(r Renderer, ra Range, vf ValueFormatter) float64 {
|
||||
tickCount := xa.getTickCount(r, ra, vf)
|
||||
step := ra.Delta() / float64(tickCount)
|
||||
return step
|
||||
}
|
||||
|
||||
func (xa XAxis) getTickCount(r Renderer, ra Range, vf ValueFormatter) int {
|
||||
|
@ -58,27 +64,6 @@ func (xa XAxis) getTickCount(r Renderer, ra Range, vf ValueFormatter) int {
|
|||
return count
|
||||
}
|
||||
|
||||
func (xa XAxis) getTickStep(r Renderer, ra Range, vf ValueFormatter) float64 {
|
||||
tickCount := xa.getTickCount(r, ra, vf)
|
||||
step := ra.Delta() / float64(tickCount)
|
||||
return step
|
||||
}
|
||||
|
||||
func (xa XAxis) generateTicksWithStep(ra Range, step float64, vf ValueFormatter) []Tick {
|
||||
var ticks []Tick
|
||||
for cursor := ra.Min; cursor < ra.Max; cursor += step {
|
||||
ticks = append(ticks, Tick{
|
||||
Value: cursor,
|
||||
Label: vf(cursor),
|
||||
})
|
||||
|
||||
if len(ticks) == 20 {
|
||||
return ticks
|
||||
}
|
||||
}
|
||||
return ticks
|
||||
}
|
||||
|
||||
// Render renders the axis
|
||||
func (xa XAxis) Render(r Renderer, canvasBox Box, ra Range, ticks []Tick) {
|
||||
tickFontSize := xa.Style.GetFontSize(DefaultFontSize)
|
||||
|
|
26
yaxis.go
26
yaxis.go
|
@ -36,14 +36,7 @@ func (ya YAxis) GetTicks(r Renderer, ra Range, vf ValueFormatter) []Tick {
|
|||
|
||||
func (ya YAxis) generateTicks(r Renderer, ra Range, vf ValueFormatter) []Tick {
|
||||
step := ya.getTickStep(r, ra, vf)
|
||||
return ya.generateTicksWithStep(ra, step, vf)
|
||||
}
|
||||
|
||||
func (ya YAxis) getTickCount(r Renderer, ra Range, vf ValueFormatter) int {
|
||||
textHeight := int(ya.Style.GetFontSize(DefaultFontSize))
|
||||
height := textHeight + DefaultMinimumTickVerticalSpacing
|
||||
count := int(math.Ceil(float64(ra.Domain) / float64(height)))
|
||||
return count
|
||||
return GenerateTicksWithStep(ra, step, vf)
|
||||
}
|
||||
|
||||
func (ya YAxis) getTickStep(r Renderer, ra Range, vf ValueFormatter) float64 {
|
||||
|
@ -51,18 +44,11 @@ func (ya YAxis) getTickStep(r Renderer, ra Range, vf ValueFormatter) float64 {
|
|||
return ra.Delta() / float64(tickCount)
|
||||
}
|
||||
|
||||
func (ya YAxis) generateTicksWithStep(ra Range, step float64, vf ValueFormatter) []Tick {
|
||||
var ticks []Tick
|
||||
for cursor := ra.Min; cursor < ra.Max; cursor += step {
|
||||
ticks = append(ticks, Tick{
|
||||
Value: cursor,
|
||||
Label: vf(cursor),
|
||||
})
|
||||
if len(ticks) == 20 {
|
||||
return ticks
|
||||
}
|
||||
}
|
||||
return ticks
|
||||
func (ya YAxis) getTickCount(r Renderer, ra Range, vf ValueFormatter) int {
|
||||
textHeight := int(ya.Style.GetFontSize(DefaultFontSize))
|
||||
height := textHeight + DefaultMinimumTickVerticalSpacing
|
||||
count := int(math.Ceil(float64(ra.Domain) / float64(height)))
|
||||
return count
|
||||
}
|
||||
|
||||
// Render renders the axis.
|
||||
|
|
Loading…
Reference in a new issue