introduces the Range
interface (instead of a concrete type).
This commit is contained in:
parent
8af50213c3
commit
b0934ee2e3
27 changed files with 331 additions and 172 deletions
|
@ -40,7 +40,7 @@ func (as AnnotationSeries) Measure(r Renderer, canvasBox Box, xrange, yrange Ran
|
||||||
Bottom: 0,
|
Bottom: 0,
|
||||||
}
|
}
|
||||||
if as.Style.IsZero() || as.Style.Show {
|
if as.Style.IsZero() || as.Style.Show {
|
||||||
style := as.Style.WithDefaultsFrom(Style{
|
style := as.Style.InheritFrom(Style{
|
||||||
Font: defaults.Font,
|
Font: defaults.Font,
|
||||||
FillColor: DefaultAnnotationFillColor,
|
FillColor: DefaultAnnotationFillColor,
|
||||||
FontSize: DefaultAnnotationFontSize,
|
FontSize: DefaultAnnotationFontSize,
|
||||||
|
@ -64,7 +64,7 @@ func (as AnnotationSeries) Measure(r Renderer, canvasBox Box, xrange, yrange Ran
|
||||||
// Render draws the series.
|
// Render draws the series.
|
||||||
func (as AnnotationSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
func (as AnnotationSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
||||||
if as.Style.IsZero() || as.Style.Show {
|
if as.Style.IsZero() || as.Style.Show {
|
||||||
style := as.Style.WithDefaultsFrom(Style{
|
style := as.Style.InheritFrom(Style{
|
||||||
Font: defaults.Font,
|
Font: defaults.Font,
|
||||||
FontColor: DefaultTextColor,
|
FontColor: DefaultTextColor,
|
||||||
FillColor: DefaultAnnotationFillColor,
|
FillColor: DefaultAnnotationFillColor,
|
||||||
|
|
|
@ -29,12 +29,12 @@ func TestAnnotationSeriesMeasure(t *testing.T) {
|
||||||
f, err := GetDefaultFont()
|
f, err := GetDefaultFont()
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
|
|
||||||
xrange := Range{
|
xrange := &ContinuousRange{
|
||||||
Min: 1.0,
|
Min: 1.0,
|
||||||
Max: 4.0,
|
Max: 4.0,
|
||||||
Domain: 100,
|
Domain: 100,
|
||||||
}
|
}
|
||||||
yrange := Range{
|
yrange := &ContinuousRange{
|
||||||
Min: 1.0,
|
Min: 1.0,
|
||||||
Max: 4.0,
|
Max: 4.0,
|
||||||
Domain: 100,
|
Domain: 100,
|
||||||
|
@ -82,12 +82,12 @@ func TestAnnotationSeriesRender(t *testing.T) {
|
||||||
f, err := GetDefaultFont()
|
f, err := GetDefaultFont()
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
|
|
||||||
xrange := Range{
|
xrange := &ContinuousRange{
|
||||||
Min: 1.0,
|
Min: 1.0,
|
||||||
Max: 4.0,
|
Max: 4.0,
|
||||||
Domain: 100,
|
Domain: 100,
|
||||||
}
|
}
|
||||||
yrange := Range{
|
yrange := &ContinuousRange{
|
||||||
Min: 1.0,
|
Min: 1.0,
|
||||||
Max: 4.0,
|
Max: 4.0,
|
||||||
Domain: 100,
|
Domain: 100,
|
||||||
|
|
14
axis.go
14
axis.go
|
@ -14,7 +14,17 @@ const (
|
||||||
type Axis interface {
|
type Axis interface {
|
||||||
GetName() string
|
GetName() string
|
||||||
GetStyle() Style
|
GetStyle() Style
|
||||||
GetTicks(r Renderer, ra Range, vf ValueFormatter) []Tick
|
|
||||||
|
GetTicks() []Tick
|
||||||
|
GenerateTicks(r Renderer, ra Range, vf ValueFormatter) []Tick
|
||||||
|
|
||||||
|
// GetGridLines returns the gridlines for the axis.
|
||||||
GetGridLines(ticks []Tick) []GridLine
|
GetGridLines(ticks []Tick) []GridLine
|
||||||
Render(c *Chart, r Renderer, canvasBox Box, ra Range, ticks []Tick)
|
|
||||||
|
// Measure should return an absolute box for the axis.
|
||||||
|
// This is used when auto-fitting the canvas to the background.
|
||||||
|
Measure(r Renderer, canvasBox Box, ra Range, style Style, ticks []Tick) Box
|
||||||
|
|
||||||
|
// Render renders the axis.
|
||||||
|
Render(r Renderer, canvasBox Box, ra Range, style Style, ticks []Tick)
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,7 +108,7 @@ func (bbs *BollingerBandsSeries) GetBoundedLastValue() (x, y1, y2 float64) {
|
||||||
|
|
||||||
// Render renders the series.
|
// Render renders the series.
|
||||||
func (bbs *BollingerBandsSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
func (bbs *BollingerBandsSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
||||||
s := bbs.Style.WithDefaultsFrom(defaults.WithDefaultsFrom(Style{
|
s := bbs.Style.InheritFrom(defaults.InheritFrom(Style{
|
||||||
StrokeWidth: 1.0,
|
StrokeWidth: 1.0,
|
||||||
StrokeColor: DefaultAxisColor.WithAlpha(64),
|
StrokeColor: DefaultAxisColor.WithAlpha(64),
|
||||||
FillColor: DefaultAxisColor.WithAlpha(32),
|
FillColor: DefaultAxisColor.WithAlpha(32),
|
||||||
|
|
101
chart.go
101
chart.go
|
@ -137,7 +137,7 @@ func (c Chart) getRanges() (xrange, yrange, yrangeAlt Range) {
|
||||||
var miny, maxy float64 = math.MaxFloat64, 0
|
var miny, maxy float64 = math.MaxFloat64, 0
|
||||||
var minya, maxya float64 = math.MaxFloat64, 0
|
var minya, maxya float64 = math.MaxFloat64, 0
|
||||||
|
|
||||||
hasSecondaryAxis := false
|
seriesMappedToSecondaryAxis := false
|
||||||
|
|
||||||
// note: a possible future optimization is to not scan the series values if
|
// note: a possible future optimization is to not scan the series values if
|
||||||
// all axis are represented by either custom ticks or custom ranges.
|
// all axis are represented by either custom ticks or custom ranges.
|
||||||
|
@ -162,7 +162,7 @@ func (c Chart) getRanges() (xrange, yrange, yrangeAlt Range) {
|
||||||
minya = math.Min(minya, vy2)
|
minya = math.Min(minya, vy2)
|
||||||
maxya = math.Max(maxya, vy1)
|
maxya = math.Max(maxya, vy1)
|
||||||
maxya = math.Max(maxya, vy2)
|
maxya = math.Max(maxya, vy2)
|
||||||
hasSecondaryAxis = true
|
seriesMappedToSecondaryAxis = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if vp, isValueProvider := s.(ValueProvider); isValueProvider {
|
} else if vp, isValueProvider := s.(ValueProvider); isValueProvider {
|
||||||
|
@ -179,27 +179,40 @@ func (c Chart) getRanges() (xrange, yrange, yrangeAlt Range) {
|
||||||
} else if seriesAxis == YAxisSecondary {
|
} else if seriesAxis == YAxisSecondary {
|
||||||
minya = math.Min(minya, vy)
|
minya = math.Min(minya, vy)
|
||||||
maxya = math.Max(maxya, vy)
|
maxya = math.Max(maxya, vy)
|
||||||
hasSecondaryAxis = true
|
seriesMappedToSecondaryAxis = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if xrange == nil {
|
||||||
|
xrange = &ContinuousRange{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if yrange == nil {
|
||||||
|
yrange = &ContinuousRange{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if yrangeAlt == nil {
|
||||||
|
yrangeAlt = &ContinuousRange{}
|
||||||
|
}
|
||||||
|
|
||||||
if len(c.XAxis.Ticks) > 0 {
|
if len(c.XAxis.Ticks) > 0 {
|
||||||
tickMin, tickMax := math.MaxFloat64, 0.0
|
tickMin, tickMax := math.MaxFloat64, 0.0
|
||||||
for _, t := range c.XAxis.Ticks {
|
for _, t := range c.XAxis.Ticks {
|
||||||
tickMin = math.Min(tickMin, t.Value)
|
tickMin = math.Min(tickMin, t.Value)
|
||||||
tickMax = math.Max(tickMax, t.Value)
|
tickMax = math.Max(tickMax, t.Value)
|
||||||
}
|
}
|
||||||
xrange.Min = tickMin
|
|
||||||
xrange.Max = tickMax
|
xrange.SetMin(tickMin)
|
||||||
} else if !c.XAxis.Range.IsZero() {
|
xrange.SetMax(tickMax)
|
||||||
xrange.Min = c.XAxis.Range.Min
|
} else if c.XAxis.Range != nil && !c.XAxis.Range.IsZero() {
|
||||||
xrange.Max = c.XAxis.Range.Max
|
xrange.SetMin(c.XAxis.Range.GetMin())
|
||||||
|
xrange.SetMax(c.XAxis.Range.GetMax())
|
||||||
} else {
|
} else {
|
||||||
xrange.Min = minx
|
xrange.SetMin(minx)
|
||||||
xrange.Max = maxx
|
xrange.SetMax(maxx)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(c.YAxis.Ticks) > 0 {
|
if len(c.YAxis.Ticks) > 0 {
|
||||||
|
@ -208,15 +221,20 @@ func (c Chart) getRanges() (xrange, yrange, yrangeAlt Range) {
|
||||||
tickMin = math.Min(tickMin, t.Value)
|
tickMin = math.Min(tickMin, t.Value)
|
||||||
tickMax = math.Max(tickMax, t.Value)
|
tickMax = math.Max(tickMax, t.Value)
|
||||||
}
|
}
|
||||||
yrange.Min = tickMin
|
yrange.SetMin(tickMin)
|
||||||
yrange.Max = tickMax
|
yrange.SetMax(tickMax)
|
||||||
} else if !c.YAxis.Range.IsZero() {
|
} else if c.YAxis.Range != nil && !c.YAxis.Range.IsZero() {
|
||||||
yrange.Min = c.YAxis.Range.Min
|
yrange.SetMin(c.YAxis.Range.GetMin())
|
||||||
yrange.Max = c.YAxis.Range.Max
|
yrange.SetMax(c.YAxis.Range.GetMax())
|
||||||
} else {
|
} else {
|
||||||
yrange.Min = miny
|
yrange.SetMin(miny)
|
||||||
yrange.Max = maxy
|
yrange.SetMax(maxy)
|
||||||
yrange.Min, yrange.Max = yrange.GetRoundedRangeBounds()
|
|
||||||
|
delta := yrange.GetDelta()
|
||||||
|
roundTo := GetRoundToForDelta(delta)
|
||||||
|
rmin, rmax := RoundDown(yrange.GetMin(), roundTo), RoundUp(yrange.GetMax(), roundTo)
|
||||||
|
yrange.SetMin(rmin)
|
||||||
|
yrange.SetMax(rmax)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(c.YAxisSecondary.Ticks) > 0 {
|
if len(c.YAxisSecondary.Ticks) > 0 {
|
||||||
|
@ -225,30 +243,34 @@ func (c Chart) getRanges() (xrange, yrange, yrangeAlt Range) {
|
||||||
tickMin = math.Min(tickMin, t.Value)
|
tickMin = math.Min(tickMin, t.Value)
|
||||||
tickMax = math.Max(tickMax, t.Value)
|
tickMax = math.Max(tickMax, t.Value)
|
||||||
}
|
}
|
||||||
yrangeAlt.Min = tickMin
|
yrangeAlt.SetMin(tickMin)
|
||||||
yrangeAlt.Max = tickMax
|
yrangeAlt.SetMax(tickMax)
|
||||||
} else if !c.YAxisSecondary.Range.IsZero() {
|
} else if c.YAxisSecondary.Range != nil && !c.YAxisSecondary.Range.IsZero() {
|
||||||
yrangeAlt.Min = c.YAxisSecondary.Range.Min
|
yrangeAlt.SetMin(c.YAxisSecondary.Range.GetMin())
|
||||||
yrangeAlt.Max = c.YAxisSecondary.Range.Max
|
yrangeAlt.SetMax(c.YAxisSecondary.Range.GetMax())
|
||||||
} else if hasSecondaryAxis {
|
} else if seriesMappedToSecondaryAxis {
|
||||||
yrangeAlt.Min = minya
|
yrangeAlt.SetMin(minya)
|
||||||
yrangeAlt.Max = maxya
|
yrangeAlt.SetMax(maxya)
|
||||||
yrangeAlt.Min, yrangeAlt.Max = yrangeAlt.GetRoundedRangeBounds()
|
|
||||||
|
delta := yrangeAlt.GetDelta()
|
||||||
|
roundTo := GetRoundToForDelta(delta)
|
||||||
|
rmin, rmax := RoundDown(yrangeAlt.GetMin(), roundTo), RoundUp(yrangeAlt.GetMax(), roundTo)
|
||||||
|
yrangeAlt.SetMin(rmin)
|
||||||
|
yrangeAlt.SetMax(rmax)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Chart) checkRanges(xr, yr, yra Range) error {
|
func (c Chart) checkRanges(xr, yr, yra Range) error {
|
||||||
|
if math.IsInf(xr.GetDelta(), 0) || math.IsNaN(xr.GetDelta()) || xr.GetDelta() == 0 {
|
||||||
if math.IsInf(xr.Delta(), 0) || math.IsNaN(xr.Delta()) {
|
|
||||||
return errors.New("Invalid (infinite or NaN) x-range delta")
|
return errors.New("Invalid (infinite or NaN) x-range delta")
|
||||||
}
|
}
|
||||||
if math.IsInf(yr.Delta(), 0) || math.IsNaN(yr.Delta()) {
|
if math.IsInf(yr.GetDelta(), 0) || math.IsNaN(yr.GetDelta()) || yr.GetDelta() == 0 {
|
||||||
return errors.New("Invalid (infinite or NaN) y-range delta")
|
return errors.New("Invalid (infinite or NaN) y-range delta")
|
||||||
}
|
}
|
||||||
if c.hasSecondarySeries() {
|
if c.hasSecondarySeries() {
|
||||||
if math.IsInf(yra.Delta(), 0) || math.IsNaN(yra.Delta()) {
|
if math.IsInf(yra.GetDelta(), 0) || math.IsNaN(yra.GetDelta()) || yra.GetDelta() == 0 {
|
||||||
return errors.New("Invalid (infinite or NaN) y-secondary-range delta")
|
return errors.New("Invalid (infinite or NaN) y-secondary-range delta")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -320,14 +342,11 @@ func (c Chart) getAxisAdjustedCanvasBox(r Renderer, canvasBox Box, xr, yr, yra R
|
||||||
return canvasBox.OuterConstrain(c.Box(), axesOuterBox)
|
return canvasBox.OuterConstrain(c.Box(), axesOuterBox)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Chart) setRangeDomains(canvasBox Box, xr, yr, yra Range) (xr2, yr2, yra2 Range) {
|
func (c Chart) setRangeDomains(canvasBox Box, xr, yr, yra Range) (Range, Range, Range) {
|
||||||
xr2.Min, xr2.Max = xr.Min, xr.Max
|
xr.SetDomain(canvasBox.Width())
|
||||||
xr2.Domain = canvasBox.Width()
|
yr.SetDomain(canvasBox.Height())
|
||||||
yr2.Min, yr2.Max = yr.Min, yr.Max
|
yra.SetDomain(canvasBox.Height())
|
||||||
yr2.Domain = canvasBox.Height()
|
return xr, yr, yra
|
||||||
yra2.Min, yra2.Max = yra.Min, yra.Max
|
|
||||||
yra2.Domain = canvasBox.Height()
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Chart) hasAnnotationSeries() bool {
|
func (c Chart) hasAnnotationSeries() bool {
|
||||||
|
@ -372,7 +391,7 @@ func (c Chart) getAnnotationAdjustedCanvasBox(r Renderer, canvasBox Box, xr, yr,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Chart) getBackgroundStyle() Style {
|
func (c Chart) getBackgroundStyle() Style {
|
||||||
return c.Background.WithDefaultsFrom(c.styleDefaultsBackground())
|
return c.Background.InheritFrom(c.styleDefaultsBackground())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Chart) drawBackground(r Renderer) {
|
func (c Chart) drawBackground(r Renderer) {
|
||||||
|
@ -383,7 +402,7 @@ func (c Chart) drawBackground(r Renderer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Chart) getCanvasStyle() Style {
|
func (c Chart) getCanvasStyle() Style {
|
||||||
return c.Canvas.WithDefaultsFrom(c.styleDefaultsCanvas())
|
return c.Canvas.InheritFrom(c.styleDefaultsCanvas())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Chart) drawCanvas(r Renderer, canvasBox Box) {
|
func (c Chart) drawCanvas(r Renderer, canvasBox Box) {
|
||||||
|
|
|
@ -77,24 +77,24 @@ func TestChartGetRanges(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
xrange, yrange, yrangeAlt := c.getRanges()
|
xrange, yrange, yrangeAlt := c.getRanges()
|
||||||
assert.Equal(-2.0, xrange.Min)
|
assert.Equal(-2.0, xrange.GetMin())
|
||||||
assert.Equal(5.0, xrange.Max)
|
assert.Equal(5.0, xrange.GetMax())
|
||||||
|
|
||||||
assert.Equal(-2.1, yrange.Min)
|
assert.Equal(-2.1, yrange.GetMin())
|
||||||
assert.Equal(4.5, yrange.Max)
|
assert.Equal(4.5, yrange.GetMax())
|
||||||
|
|
||||||
assert.Equal(10.0, yrangeAlt.Min)
|
assert.Equal(10.0, yrangeAlt.GetMin())
|
||||||
assert.Equal(14.0, yrangeAlt.Max)
|
assert.Equal(14.0, yrangeAlt.GetMax())
|
||||||
|
|
||||||
cSet := Chart{
|
cSet := Chart{
|
||||||
XAxis: XAxis{
|
XAxis: XAxis{
|
||||||
Range: Range{Min: 9.8, Max: 19.8},
|
Range: &ContinuousRange{Min: 9.8, Max: 19.8},
|
||||||
},
|
},
|
||||||
YAxis: YAxis{
|
YAxis: YAxis{
|
||||||
Range: Range{Min: 9.9, Max: 19.9},
|
Range: &ContinuousRange{Min: 9.9, Max: 19.9},
|
||||||
},
|
},
|
||||||
YAxisSecondary: YAxis{
|
YAxisSecondary: YAxis{
|
||||||
Range: Range{Min: 9.7, Max: 19.7},
|
Range: &ContinuousRange{Min: 9.7, Max: 19.7},
|
||||||
},
|
},
|
||||||
Series: []Series{
|
Series: []Series{
|
||||||
ContinuousSeries{
|
ContinuousSeries{
|
||||||
|
@ -114,14 +114,14 @@ func TestChartGetRanges(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
xr2, yr2, yra2 := cSet.getRanges()
|
xr2, yr2, yra2 := cSet.getRanges()
|
||||||
assert.Equal(9.8, xr2.Min)
|
assert.Equal(9.8, xr2.GetMin())
|
||||||
assert.Equal(19.8, xr2.Max)
|
assert.Equal(19.8, xr2.GetMax())
|
||||||
|
|
||||||
assert.Equal(9.9, yr2.Min)
|
assert.Equal(9.9, yr2.GetMin())
|
||||||
assert.Equal(19.9, yr2.Max)
|
assert.Equal(19.9, yr2.GetMax())
|
||||||
|
|
||||||
assert.Equal(9.7, yra2.Min)
|
assert.Equal(9.7, yra2.GetMin())
|
||||||
assert.Equal(19.7, yra2.Max)
|
assert.Equal(19.7, yra2.GetMax())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestChartGetRangesUseTicks(t *testing.T) {
|
func TestChartGetRangesUseTicks(t *testing.T) {
|
||||||
|
@ -139,7 +139,7 @@ func TestChartGetRangesUseTicks(t *testing.T) {
|
||||||
{4.0, "4.0"},
|
{4.0, "4.0"},
|
||||||
{5.0, "Five"},
|
{5.0, "Five"},
|
||||||
},
|
},
|
||||||
Range: Range{
|
Range: &ContinuousRange{
|
||||||
Min: -5.0,
|
Min: -5.0,
|
||||||
Max: 5.0,
|
Max: 5.0,
|
||||||
},
|
},
|
||||||
|
@ -153,10 +153,10 @@ func TestChartGetRangesUseTicks(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
xr, yr, yar := c.getRanges()
|
xr, yr, yar := c.getRanges()
|
||||||
assert.Equal(-2.0, xr.Min)
|
assert.Equal(-2.0, xr.GetMin())
|
||||||
assert.Equal(2.0, xr.Max)
|
assert.Equal(2.0, xr.GetMax())
|
||||||
assert.Equal(0.0, yr.Min)
|
assert.Equal(0.0, yr.GetMin())
|
||||||
assert.Equal(5.0, yr.Max)
|
assert.Equal(5.0, yr.GetMax())
|
||||||
assert.True(yar.IsZero(), yar.String())
|
assert.True(yar.IsZero(), yar.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,7 +165,7 @@ func TestChartGetRangesUseUserRanges(t *testing.T) {
|
||||||
|
|
||||||
c := Chart{
|
c := Chart{
|
||||||
YAxis: YAxis{
|
YAxis: YAxis{
|
||||||
Range: Range{
|
Range: &ContinuousRange{
|
||||||
Min: -5.0,
|
Min: -5.0,
|
||||||
Max: 5.0,
|
Max: 5.0,
|
||||||
},
|
},
|
||||||
|
@ -179,10 +179,10 @@ func TestChartGetRangesUseUserRanges(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
xr, yr, yar := c.getRanges()
|
xr, yr, yar := c.getRanges()
|
||||||
assert.Equal(-2.0, xr.Min)
|
assert.Equal(-2.0, xr.GetMin())
|
||||||
assert.Equal(2.0, xr.Max)
|
assert.Equal(2.0, xr.GetMax())
|
||||||
assert.Equal(-5.0, yr.Min)
|
assert.Equal(-5.0, yr.GetMin())
|
||||||
assert.Equal(5.0, yr.Max)
|
assert.Equal(5.0, yr.GetMax())
|
||||||
assert.True(yar.IsZero(), yar.String())
|
assert.True(yar.IsZero(), yar.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,15 +310,15 @@ func TestChartGetAxesTicks(t *testing.T) {
|
||||||
c := Chart{
|
c := Chart{
|
||||||
XAxis: XAxis{
|
XAxis: XAxis{
|
||||||
Style: Style{Show: true},
|
Style: Style{Show: true},
|
||||||
Range: Range{Min: 9.8, Max: 19.8},
|
Range: &ContinuousRange{Min: 9.8, Max: 19.8},
|
||||||
},
|
},
|
||||||
YAxis: YAxis{
|
YAxis: YAxis{
|
||||||
Style: Style{Show: true},
|
Style: Style{Show: true},
|
||||||
Range: Range{Min: 9.9, Max: 19.9},
|
Range: &ContinuousRange{Min: 9.9, Max: 19.9},
|
||||||
},
|
},
|
||||||
YAxisSecondary: YAxis{
|
YAxisSecondary: YAxis{
|
||||||
Style: Style{Show: true},
|
Style: Style{Show: true},
|
||||||
Range: Range{Min: 9.7, Max: 19.7},
|
Range: &ContinuousRange{Min: 9.7, Max: 19.7},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
xr, yr, yar := c.getRanges()
|
xr, yr, yar := c.getRanges()
|
||||||
|
@ -337,7 +337,7 @@ func TestChartSingleSeries(t *testing.T) {
|
||||||
Width: 1024,
|
Width: 1024,
|
||||||
Height: 400,
|
Height: 400,
|
||||||
YAxis: YAxis{
|
YAxis: YAxis{
|
||||||
Range: Range{
|
Range: &ContinuousRange{
|
||||||
Min: 0.0,
|
Min: 0.0,
|
||||||
Max: 4.0,
|
Max: 4.0,
|
||||||
},
|
},
|
||||||
|
@ -377,7 +377,7 @@ func TestChartRegressionBadRangesByUser(t *testing.T) {
|
||||||
|
|
||||||
c := Chart{
|
c := Chart{
|
||||||
YAxis: YAxis{
|
YAxis: YAxis{
|
||||||
Range: Range{
|
Range: &ContinuousRange{
|
||||||
Min: math.Inf(-1),
|
Min: math.Inf(-1),
|
||||||
Max: math.Inf(1), // this could really happen? eh.
|
Max: math.Inf(1), // this could really happen? eh.
|
||||||
},
|
},
|
||||||
|
|
32
concat_series.go
Normal file
32
concat_series.go
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package chart
|
||||||
|
|
||||||
|
// ConcatSeries is a special type of series that concatenates its `InnerSeries`.
|
||||||
|
type ConcatSeries []Series
|
||||||
|
|
||||||
|
// Len returns the length of the concatenated set of series.
|
||||||
|
func (cs ConcatSeries) Len() int {
|
||||||
|
total := 0
|
||||||
|
for _, s := range cs {
|
||||||
|
if typed, isValueProvider := s.(ValueProvider); isValueProvider {
|
||||||
|
total += typed.Len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetValue returns the value at the (meta) index (i.e 0 => totalLen-1)
|
||||||
|
func (cs ConcatSeries) GetValue(index int) (x, y float64) {
|
||||||
|
cursor := 0
|
||||||
|
for _, s := range cs {
|
||||||
|
if typed, isValueProvider := s.(ValueProvider); isValueProvider {
|
||||||
|
len := typed.Len()
|
||||||
|
if index < cursor+len {
|
||||||
|
x, y = typed.GetValue(index - cursor) //FENCEPOSTS.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cursor += typed.Len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
41
concat_series_test.go
Normal file
41
concat_series_test.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package chart
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
assert "github.com/blendlabs/go-assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConcatSeries(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
s1 := ContinuousSeries{
|
||||||
|
XValues: Seq(1.0, 10.0),
|
||||||
|
YValues: Seq(1.0, 10.0),
|
||||||
|
}
|
||||||
|
|
||||||
|
s2 := ContinuousSeries{
|
||||||
|
XValues: Seq(11, 20.0),
|
||||||
|
YValues: Seq(10.0, 1.0),
|
||||||
|
}
|
||||||
|
|
||||||
|
s3 := ContinuousSeries{
|
||||||
|
XValues: Seq(21, 30.0),
|
||||||
|
YValues: Seq(1.0, 10.0),
|
||||||
|
}
|
||||||
|
|
||||||
|
cs := ConcatSeries([]Series{s1, s2, s3})
|
||||||
|
assert.Equal(30, cs.Len())
|
||||||
|
|
||||||
|
x0, y0 := cs.GetValue(0)
|
||||||
|
assert.Equal(1.0, x0)
|
||||||
|
assert.Equal(1.0, y0)
|
||||||
|
|
||||||
|
xm, ym := cs.GetValue(19)
|
||||||
|
assert.Equal(20.0, xm)
|
||||||
|
assert.Equal(1.0, ym)
|
||||||
|
|
||||||
|
xn, yn := cs.GetValue(29)
|
||||||
|
assert.Equal(30.0, xn)
|
||||||
|
assert.Equal(10.0, yn)
|
||||||
|
}
|
67
continuous_range.go
Normal file
67
continuous_range.go
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
package chart
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ContinuousRange represents a boundary for a set of numbers.
|
||||||
|
type ContinuousRange struct {
|
||||||
|
Min float64
|
||||||
|
Max float64
|
||||||
|
Domain int
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsZero returns if the ContinuousRange has been set or not.
|
||||||
|
func (r ContinuousRange) IsZero() bool {
|
||||||
|
return (r.Min == 0 || math.IsNaN(r.Min)) &&
|
||||||
|
(r.Max == 0 || math.IsNaN(r.Max)) &&
|
||||||
|
r.Domain == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMin gets the min value for the continuous range.
|
||||||
|
func (r ContinuousRange) GetMin() float64 {
|
||||||
|
return r.Min
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMin sets the min value for the continuous range.
|
||||||
|
func (r *ContinuousRange) SetMin(min float64) {
|
||||||
|
r.Min = min
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMax returns the max value for the continuous range.
|
||||||
|
func (r ContinuousRange) GetMax() float64 {
|
||||||
|
return r.Max
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMax sets the max value for the continuous range.
|
||||||
|
func (r *ContinuousRange) SetMax(max float64) {
|
||||||
|
r.Max = max
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDelta returns the difference between the min and max value.
|
||||||
|
func (r ContinuousRange) GetDelta() float64 {
|
||||||
|
return r.Max - r.Min
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDomain returns the range domain.
|
||||||
|
func (r ContinuousRange) GetDomain() int {
|
||||||
|
return r.Domain
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDomain sets the range domain.
|
||||||
|
func (r *ContinuousRange) SetDomain(domain int) {
|
||||||
|
r.Domain = domain
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a simple string for the ContinuousRange.
|
||||||
|
func (r ContinuousRange) String() string {
|
||||||
|
return fmt.Sprintf("ContinuousRange [%.2f,%.2f] => %d", r.Min, r.Max, r.Domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translate maps a given value into the ContinuousRange space.
|
||||||
|
func (r ContinuousRange) Translate(value float64) int {
|
||||||
|
normalized := value - r.Min
|
||||||
|
ratio := normalized / r.GetDelta()
|
||||||
|
return int(math.Ceil(ratio * float64(r.Domain)))
|
||||||
|
}
|
|
@ -9,7 +9,7 @@ import (
|
||||||
func TestRangeTranslate(t *testing.T) {
|
func TestRangeTranslate(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
values := []float64{1.0, 2.0, 2.5, 2.7, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0}
|
values := []float64{1.0, 2.0, 2.5, 2.7, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0}
|
||||||
r := Range{Domain: 1000}
|
r := ContinuousRange{Domain: 1000}
|
||||||
r.Min, r.Max = MinAndMax(values...)
|
r.Min, r.Max = MinAndMax(values...)
|
||||||
|
|
||||||
// delta = ~7.0
|
// delta = ~7.0
|
|
@ -50,6 +50,6 @@ func (cs ContinuousSeries) GetYAxis() YAxisType {
|
||||||
|
|
||||||
// Render renders the series.
|
// Render renders the series.
|
||||||
func (cs ContinuousSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
func (cs ContinuousSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
||||||
style := cs.Style.WithDefaultsFrom(defaults)
|
style := cs.Style.InheritFrom(defaults)
|
||||||
DrawLineSeries(r, canvasBox, xrange, yrange, style, cs)
|
DrawLineSeries(r, canvasBox, xrange, yrange, style, cs)
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,7 +114,7 @@ func DrawHistogramSeries(r Renderer, canvasBox Box, xrange, yrange Range, s Styl
|
||||||
|
|
||||||
//calculate bar width?
|
//calculate bar width?
|
||||||
seriesLength := vs.Len()
|
seriesLength := vs.Len()
|
||||||
barWidth := int(math.Floor(float64(xrange.Domain) / float64(seriesLength)))
|
barWidth := int(math.Floor(float64(xrange.GetDomain()) / float64(seriesLength)))
|
||||||
if len(barWidths) > 0 {
|
if len(barWidths) > 0 {
|
||||||
barWidth = barWidths[0]
|
barWidth = barWidths[0]
|
||||||
}
|
}
|
||||||
|
@ -271,9 +271,9 @@ func CreateLegend(c *Chart, userDefaults ...Style) Renderable {
|
||||||
|
|
||||||
var legendStyle Style
|
var legendStyle Style
|
||||||
if len(userDefaults) > 0 {
|
if len(userDefaults) > 0 {
|
||||||
legendStyle = userDefaults[0].WithDefaultsFrom(chartDefaults.WithDefaultsFrom(legendDefaults))
|
legendStyle = userDefaults[0].InheritFrom(chartDefaults.InheritFrom(legendDefaults))
|
||||||
} else {
|
} else {
|
||||||
legendStyle = chartDefaults.WithDefaultsFrom(legendDefaults)
|
legendStyle = chartDefaults.InheritFrom(legendDefaults)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DEFAULTS
|
// DEFAULTS
|
||||||
|
@ -292,7 +292,7 @@ func CreateLegend(c *Chart, userDefaults ...Style) Renderable {
|
||||||
if s.GetStyle().IsZero() || s.GetStyle().Show {
|
if s.GetStyle().IsZero() || s.GetStyle().Show {
|
||||||
if _, isAnnotationSeries := s.(AnnotationSeries); !isAnnotationSeries {
|
if _, isAnnotationSeries := s.(AnnotationSeries); !isAnnotationSeries {
|
||||||
labels = append(labels, s.GetName())
|
labels = append(labels, s.GetName())
|
||||||
lines = append(lines, s.GetStyle().WithDefaultsFrom(c.styleDefaultsSeries(index)))
|
lines = append(lines, s.GetStyle().InheritFrom(c.styleDefaultsSeries(index)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,6 +96,6 @@ func (ema *EMASeries) ensureCachedValues() {
|
||||||
|
|
||||||
// Render renders the series.
|
// Render renders the series.
|
||||||
func (ema *EMASeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
func (ema *EMASeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
||||||
style := ema.Style.WithDefaultsFrom(defaults)
|
style := ema.Style.InheritFrom(defaults)
|
||||||
DrawLineSeries(r, canvasBox, xrange, yrange, style, ema)
|
DrawLineSeries(r, canvasBox, xrange, yrange, style, ema)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ func drawChart(res http.ResponseWriter, req *http.Request) {
|
||||||
Style: chart.Style{
|
Style: chart.Style{
|
||||||
Show: true,
|
Show: true,
|
||||||
},
|
},
|
||||||
Range: chart.Range{
|
Range: chart.ContinuousRange{
|
||||||
Min: 0.0,
|
Min: 0.0,
|
||||||
Max: 10.0,
|
Max: 10.0,
|
||||||
},
|
},
|
||||||
|
|
|
@ -52,6 +52,6 @@ func (hs HistogramSeries) GetBoundedValue(index int) (x, y1, y2 float64) {
|
||||||
|
|
||||||
// Render implements Series.Render.
|
// Render implements Series.Render.
|
||||||
func (hs HistogramSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
func (hs HistogramSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
||||||
style := hs.Style.WithDefaultsFrom(defaults)
|
style := hs.Style.InheritFrom(defaults)
|
||||||
DrawHistogramSeries(r, canvasBox, xrange, yrange, style, hs)
|
DrawHistogramSeries(r, canvasBox, xrange, yrange, style, hs)
|
||||||
}
|
}
|
||||||
|
|
|
@ -192,7 +192,7 @@ func (macds *MACDSignalSeries) ensureSignal() {
|
||||||
|
|
||||||
// Render renders the series.
|
// Render renders the series.
|
||||||
func (macds *MACDSignalSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
func (macds *MACDSignalSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
||||||
style := macds.Style.WithDefaultsFrom(defaults)
|
style := macds.Style.InheritFrom(defaults)
|
||||||
DrawLineSeries(r, canvasBox, xrange, yrange, style, macds)
|
DrawLineSeries(r, canvasBox, xrange, yrange, style, macds)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,6 +284,6 @@ func (macdl *MACDLineSeries) ensureEMASeries() {
|
||||||
|
|
||||||
// Render renders the series.
|
// Render renders the series.
|
||||||
func (macdl *MACDLineSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
func (macdl *MACDLineSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
||||||
style := macdl.Style.WithDefaultsFrom(defaults)
|
style := macdl.Style.InheritFrom(defaults)
|
||||||
DrawLineSeries(r, canvasBox, xrange, yrange, style, macdl)
|
DrawLineSeries(r, canvasBox, xrange, yrange, style, macdl)
|
||||||
}
|
}
|
||||||
|
|
61
range.go
61
range.go
|
@ -1,44 +1,41 @@
|
||||||
package chart
|
package chart
|
||||||
|
|
||||||
import (
|
// NameProvider is a type that returns a name.
|
||||||
"fmt"
|
type NameProvider interface {
|
||||||
"math"
|
GetName() string
|
||||||
)
|
|
||||||
|
|
||||||
// Range represents a boundary for a set of numbers.
|
|
||||||
type Range struct {
|
|
||||||
Min float64
|
|
||||||
Max float64
|
|
||||||
Domain int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsZero returns if the range has been set or not.
|
// StyleProvider is a type that returns a style.
|
||||||
func (r Range) IsZero() bool {
|
type StyleProvider interface {
|
||||||
return (r.Min == 0 || math.IsNaN(r.Min)) &&
|
GetStyle() Style
|
||||||
(r.Max == 0 || math.IsNaN(r.Max)) &&
|
|
||||||
r.Domain == 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delta returns the difference between the min and max value.
|
// IsZeroable is a type that returns if it's been set or not.
|
||||||
func (r Range) Delta() float64 {
|
type IsZeroable interface {
|
||||||
return r.Max - r.Min
|
IsZero() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns a simple string for the range.
|
// Stringable is a type that has a string representation.
|
||||||
func (r Range) String() string {
|
type Stringable interface {
|
||||||
return fmt.Sprintf("Range [%.2f,%.2f] => %d", r.Min, r.Max, r.Domain)
|
String() string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Translate maps a given value into the range space.
|
// Range is a
|
||||||
func (r Range) Translate(value float64) int {
|
type Range interface {
|
||||||
normalized := value - r.Min
|
Stringable
|
||||||
ratio := normalized / r.Delta()
|
IsZeroable
|
||||||
return int(math.Ceil(ratio * float64(r.Domain)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRoundedRangeBounds returns some `prettified` range bounds.
|
GetMin() float64
|
||||||
func (r Range) GetRoundedRangeBounds() (min, max float64) {
|
SetMin(min float64)
|
||||||
delta := r.Max - r.Min
|
|
||||||
roundTo := GetRoundToForDelta(delta)
|
GetMax() float64
|
||||||
return RoundDown(r.Min, roundTo), RoundUp(r.Max, roundTo)
|
SetMax(max float64)
|
||||||
|
|
||||||
|
GetDelta() float64
|
||||||
|
|
||||||
|
GetDomain() int
|
||||||
|
SetDomain(domain int)
|
||||||
|
|
||||||
|
// Translate the range to the domain.
|
||||||
|
Translate(value float64) int
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,6 +85,6 @@ func (sma SMASeries) getAverage(index int) float64 {
|
||||||
|
|
||||||
// Render renders the series.
|
// Render renders the series.
|
||||||
func (sma SMASeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
func (sma SMASeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
||||||
style := sma.Style.WithDefaultsFrom(defaults)
|
style := sma.Style.InheritFrom(defaults)
|
||||||
DrawLineSeries(r, canvasBox, xrange, yrange, style, sma)
|
DrawLineSeries(r, canvasBox, xrange, yrange, style, sma)
|
||||||
}
|
}
|
||||||
|
|
15
style.go
15
style.go
|
@ -28,6 +28,7 @@ func (s Style) IsZero() bool {
|
||||||
return s.StrokeColor.IsZero() && s.FillColor.IsZero() && s.StrokeWidth == 0 && s.FontColor.IsZero() && 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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// String returns a text representation of the style.
|
||||||
func (s Style) String() string {
|
func (s Style) String() string {
|
||||||
if s.IsZero() {
|
if s.IsZero() {
|
||||||
return "{}"
|
return "{}"
|
||||||
|
@ -184,8 +185,18 @@ func (s Style) GetPadding(defaults ...Box) Box {
|
||||||
return s.Padding
|
return s.Padding
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithDefaultsFrom coalesces two styles into a new style.
|
// PersistToRenderer passes the style onto a renderer.
|
||||||
func (s Style) WithDefaultsFrom(defaults Style) (final Style) {
|
func (s Style) PersistToRenderer(r Renderer) {
|
||||||
|
r.SetStrokeColor(s.GetStrokeColor())
|
||||||
|
r.SetStrokeWidth(s.GetStrokeWidth())
|
||||||
|
r.SetStrokeDashArray(s.GetStrokeDashArray())
|
||||||
|
r.SetFont(s.GetFont())
|
||||||
|
r.SetFontColor(s.GetFontColor())
|
||||||
|
r.SetFontSize(s.GetFontSize())
|
||||||
|
}
|
||||||
|
|
||||||
|
// InheritFrom coalesces two styles into a new style.
|
||||||
|
func (s Style) InheritFrom(defaults Style) (final Style) {
|
||||||
final.StrokeColor = s.GetStrokeColor(defaults.StrokeColor)
|
final.StrokeColor = s.GetStrokeColor(defaults.StrokeColor)
|
||||||
final.StrokeWidth = s.GetStrokeWidth(defaults.StrokeWidth)
|
final.StrokeWidth = s.GetStrokeWidth(defaults.StrokeWidth)
|
||||||
final.StrokeDashArray = s.GetStrokeDashArray(defaults.StrokeDashArray)
|
final.StrokeDashArray = s.GetStrokeDashArray(defaults.StrokeDashArray)
|
||||||
|
|
|
@ -142,7 +142,7 @@ func TestStyleWithDefaultsFrom(t *testing.T) {
|
||||||
Padding: DefaultBackgroundPadding,
|
Padding: DefaultBackgroundPadding,
|
||||||
}
|
}
|
||||||
|
|
||||||
coalesced := unset.WithDefaultsFrom(set)
|
coalesced := unset.InheritFrom(set)
|
||||||
assert.Equal(set, coalesced)
|
assert.Equal(set, coalesced)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
2
tick.go
2
tick.go
|
@ -3,7 +3,7 @@ package chart
|
||||||
// GenerateTicksWithStep generates a set of ticks.
|
// GenerateTicksWithStep generates a set of ticks.
|
||||||
func GenerateTicksWithStep(ra Range, step float64, vf ValueFormatter) []Tick {
|
func GenerateTicksWithStep(ra Range, step float64, vf ValueFormatter) []Tick {
|
||||||
var ticks []Tick
|
var ticks []Tick
|
||||||
min, max := ra.Min, ra.Max
|
min, max := ra.GetMin(), ra.GetMax()
|
||||||
for cursor := min; cursor <= max; cursor += step {
|
for cursor := min; cursor <= max; cursor += step {
|
||||||
ticks = append(ticks, Tick{
|
ticks = append(ticks, Tick{
|
||||||
Value: cursor,
|
Value: cursor,
|
||||||
|
|
|
@ -9,6 +9,6 @@ import (
|
||||||
func TestGenerateTicksWithStep(t *testing.T) {
|
func TestGenerateTicksWithStep(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
ticks := GenerateTicksWithStep(Range{Min: 1.0, Max: 10.0, Domain: 100}, 1.0, FloatValueFormatter)
|
ticks := GenerateTicksWithStep(&ContinuousRange{Min: 1.0, Max: 10.0, Domain: 100}, 1.0, FloatValueFormatter)
|
||||||
assert.Len(ticks, 10)
|
assert.Len(ticks, 10)
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,6 @@ func (ts TimeSeries) GetYAxis() YAxisType {
|
||||||
|
|
||||||
// Render renders the series.
|
// Render renders the series.
|
||||||
func (ts TimeSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
func (ts TimeSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
|
||||||
style := ts.Style.WithDefaultsFrom(defaults)
|
style := ts.Style.InheritFrom(defaults)
|
||||||
DrawLineSeries(r, canvasBox, xrange, yrange, style, ts)
|
DrawLineSeries(r, canvasBox, xrange, yrange, style, ts)
|
||||||
}
|
}
|
||||||
|
|
23
xaxis.go
23
xaxis.go
|
@ -44,7 +44,7 @@ func (xa XAxis) generateTicks(r Renderer, ra Range, defaults Style, vf ValueForm
|
||||||
|
|
||||||
func (xa XAxis) getTickStep(r Renderer, ra Range, defaults Style, vf ValueFormatter) float64 {
|
func (xa XAxis) getTickStep(r Renderer, ra Range, defaults Style, vf ValueFormatter) float64 {
|
||||||
tickCount := xa.getTickCount(r, ra, defaults, vf)
|
tickCount := xa.getTickCount(r, ra, defaults, vf)
|
||||||
step := ra.Delta() / float64(tickCount)
|
step := ra.GetDelta() / float64(tickCount)
|
||||||
return step
|
return step
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,8 +53,8 @@ func (xa XAxis) getTickCount(r Renderer, ra Range, defaults Style, vf ValueForma
|
||||||
r.SetFontSize(xa.Style.GetFontSize(defaults.GetFontSize(DefaultFontSize)))
|
r.SetFontSize(xa.Style.GetFontSize(defaults.GetFontSize(DefaultFontSize)))
|
||||||
|
|
||||||
// take a cut at determining the 'widest' value.
|
// take a cut at determining the 'widest' value.
|
||||||
l0 := vf(ra.Min)
|
l0 := vf(ra.GetMin())
|
||||||
ln := vf(ra.Max)
|
ln := vf(ra.GetMax())
|
||||||
ll := l0
|
ll := l0
|
||||||
if len(ln) > len(l0) {
|
if len(ln) > len(l0) {
|
||||||
ll = ln
|
ll = ln
|
||||||
|
@ -62,7 +62,7 @@ func (xa XAxis) getTickCount(r Renderer, ra Range, defaults Style, vf ValueForma
|
||||||
llb := r.MeasureText(ll)
|
llb := r.MeasureText(ll)
|
||||||
textWidth := llb.Width()
|
textWidth := llb.Width()
|
||||||
width := textWidth + DefaultMinimumTickHorizontalSpacing
|
width := textWidth + DefaultMinimumTickHorizontalSpacing
|
||||||
count := int(math.Ceil(float64(ra.Domain) / float64(width)))
|
count := int(math.Ceil(float64(ra.GetDomain()) / float64(width)))
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,13 +76,7 @@ func (xa XAxis) GetGridLines(ticks []Tick) []GridLine {
|
||||||
|
|
||||||
// Measure returns the bounds of the axis.
|
// Measure returns the bounds of the axis.
|
||||||
func (xa XAxis) Measure(r Renderer, canvasBox Box, ra Range, defaults Style, ticks []Tick) Box {
|
func (xa XAxis) Measure(r Renderer, canvasBox Box, ra Range, defaults Style, ticks []Tick) Box {
|
||||||
r.SetStrokeColor(xa.Style.GetStrokeColor(defaults.StrokeColor))
|
xa.Style.InheritFrom(defaults).PersistToRenderer(r)
|
||||||
r.SetStrokeWidth(xa.Style.GetStrokeWidth(defaults.StrokeWidth))
|
|
||||||
r.SetStrokeDashArray(xa.Style.GetStrokeDashArray())
|
|
||||||
r.SetFont(xa.Style.GetFont(defaults.GetFont()))
|
|
||||||
r.SetFontColor(xa.Style.GetFontColor(DefaultAxisColor))
|
|
||||||
r.SetFontSize(xa.Style.GetFontSize(defaults.GetFontSize()))
|
|
||||||
|
|
||||||
sort.Sort(Ticks(ticks))
|
sort.Sort(Ticks(ticks))
|
||||||
|
|
||||||
var left, right, top, bottom = math.MaxInt32, 0, math.MaxInt32, 0
|
var left, right, top, bottom = math.MaxInt32, 0, math.MaxInt32, 0
|
||||||
|
@ -110,12 +104,7 @@ func (xa XAxis) Measure(r Renderer, canvasBox Box, ra Range, defaults Style, tic
|
||||||
|
|
||||||
// Render renders the axis
|
// Render renders the axis
|
||||||
func (xa XAxis) Render(r Renderer, canvasBox Box, ra Range, defaults Style, ticks []Tick) {
|
func (xa XAxis) Render(r Renderer, canvasBox Box, ra Range, defaults Style, ticks []Tick) {
|
||||||
r.SetStrokeColor(xa.Style.GetStrokeColor(defaults.StrokeColor))
|
xa.Style.InheritFrom(defaults).PersistToRenderer(r)
|
||||||
r.SetStrokeWidth(xa.Style.GetStrokeWidth(defaults.StrokeWidth))
|
|
||||||
r.SetStrokeDashArray(xa.Style.GetStrokeDashArray())
|
|
||||||
r.SetFont(xa.Style.GetFont(defaults.GetFont()))
|
|
||||||
r.SetFontColor(xa.Style.GetFontColor(DefaultAxisColor))
|
|
||||||
r.SetFontSize(xa.Style.GetFontSize(defaults.GetFontSize()))
|
|
||||||
|
|
||||||
r.MoveTo(canvasBox.Left, canvasBox.Bottom)
|
r.MoveTo(canvasBox.Left, canvasBox.Bottom)
|
||||||
r.LineTo(canvasBox.Right, canvasBox.Bottom)
|
r.LineTo(canvasBox.Right, canvasBox.Bottom)
|
||||||
|
|
|
@ -16,7 +16,7 @@ func TestXAxisGetTickCount(t *testing.T) {
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
|
|
||||||
xa := XAxis{}
|
xa := XAxis{}
|
||||||
xr := Range{Min: 10, Max: 100, Domain: 1024}
|
xr := &ContinuousRange{Min: 10, Max: 100, Domain: 1024}
|
||||||
styleDefaults := Style{
|
styleDefaults := Style{
|
||||||
Font: f,
|
Font: f,
|
||||||
FontSize: 10.0,
|
FontSize: 10.0,
|
||||||
|
@ -36,14 +36,14 @@ func TestXAxisGetTickStep(t *testing.T) {
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
|
|
||||||
xa := XAxis{}
|
xa := XAxis{}
|
||||||
xr := Range{Min: 10, Max: 100, Domain: 1024}
|
xr := &ContinuousRange{Min: 10, Max: 100, Domain: 1024}
|
||||||
styleDefaults := Style{
|
styleDefaults := Style{
|
||||||
Font: f,
|
Font: f,
|
||||||
FontSize: 10.0,
|
FontSize: 10.0,
|
||||||
}
|
}
|
||||||
vf := FloatValueFormatter
|
vf := FloatValueFormatter
|
||||||
step := xa.getTickStep(r, xr, styleDefaults, vf)
|
step := xa.getTickStep(r, xr, styleDefaults, vf)
|
||||||
assert.Equal(xr.Delta()/16.0, step)
|
assert.Equal(xr.GetDelta()/16.0, step)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestXAxisGetTicks(t *testing.T) {
|
func TestXAxisGetTicks(t *testing.T) {
|
||||||
|
@ -56,7 +56,7 @@ func TestXAxisGetTicks(t *testing.T) {
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
|
|
||||||
xa := XAxis{}
|
xa := XAxis{}
|
||||||
xr := Range{Min: 10, Max: 100, Domain: 1024}
|
xr := &ContinuousRange{Min: 10, Max: 100, Domain: 1024}
|
||||||
styleDefaults := Style{
|
styleDefaults := Style{
|
||||||
Font: f,
|
Font: f,
|
||||||
FontSize: 10.0,
|
FontSize: 10.0,
|
||||||
|
@ -78,7 +78,7 @@ func TestXAxisGetTicksWithUserDefaults(t *testing.T) {
|
||||||
xa := XAxis{
|
xa := XAxis{
|
||||||
Ticks: []Tick{{Value: 1.0, Label: "1.0"}},
|
Ticks: []Tick{{Value: 1.0, Label: "1.0"}},
|
||||||
}
|
}
|
||||||
xr := Range{Min: 10, Max: 100, Domain: 1024}
|
xr := &ContinuousRange{Min: 10, Max: 100, Domain: 1024}
|
||||||
styleDefaults := Style{
|
styleDefaults := Style{
|
||||||
Font: f,
|
Font: f,
|
||||||
FontSize: 10.0,
|
FontSize: 10.0,
|
||||||
|
|
21
yaxis.go
21
yaxis.go
|
@ -17,9 +17,10 @@ type YAxis struct {
|
||||||
|
|
||||||
ValueFormatter ValueFormatter
|
ValueFormatter ValueFormatter
|
||||||
Range Range
|
Range Range
|
||||||
Ticks []Tick
|
|
||||||
|
|
||||||
|
Ticks []Tick
|
||||||
GridLines []GridLine
|
GridLines []GridLine
|
||||||
|
|
||||||
GridMajorStyle Style
|
GridMajorStyle Style
|
||||||
GridMinorStyle Style
|
GridMinorStyle Style
|
||||||
}
|
}
|
||||||
|
@ -51,7 +52,7 @@ func (ya YAxis) generateTicks(r Renderer, ra Range, defaults Style, vf ValueForm
|
||||||
|
|
||||||
func (ya YAxis) getTickStep(r Renderer, ra Range, defaults Style, vf ValueFormatter) float64 {
|
func (ya YAxis) getTickStep(r Renderer, ra Range, defaults Style, vf ValueFormatter) float64 {
|
||||||
tickCount := ya.getTickCount(r, ra, defaults, vf)
|
tickCount := ya.getTickCount(r, ra, defaults, vf)
|
||||||
step := ra.Delta() / float64(tickCount)
|
step := ra.GetDelta() / float64(tickCount)
|
||||||
return step
|
return step
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,9 +60,9 @@ func (ya YAxis) getTickCount(r Renderer, ra Range, defaults Style, vf ValueForma
|
||||||
r.SetFont(ya.Style.GetFont(defaults.GetFont()))
|
r.SetFont(ya.Style.GetFont(defaults.GetFont()))
|
||||||
r.SetFontSize(ya.Style.GetFontSize(defaults.GetFontSize(DefaultFontSize)))
|
r.SetFontSize(ya.Style.GetFontSize(defaults.GetFontSize(DefaultFontSize)))
|
||||||
//given the domain, figure out how many ticks we can draw ...
|
//given the domain, figure out how many ticks we can draw ...
|
||||||
label := vf(ra.Min)
|
label := vf(ra.GetMin())
|
||||||
tb := r.MeasureText(label)
|
tb := r.MeasureText(label)
|
||||||
count := int(math.Ceil(float64(ra.Domain) / float64(tb.Height()+DefaultMinimumTickVerticalSpacing)))
|
count := int(math.Ceil(float64(ra.GetDomain()) / float64(tb.Height()+DefaultMinimumTickVerticalSpacing)))
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,11 +76,7 @@ func (ya YAxis) GetGridLines(ticks []Tick) []GridLine {
|
||||||
|
|
||||||
// Measure returns the bounds of the axis.
|
// Measure returns the bounds of the axis.
|
||||||
func (ya YAxis) Measure(r Renderer, canvasBox Box, ra Range, defaults Style, ticks []Tick) Box {
|
func (ya YAxis) Measure(r Renderer, canvasBox Box, ra Range, defaults Style, ticks []Tick) Box {
|
||||||
r.SetStrokeColor(ya.Style.GetStrokeColor(defaults.StrokeColor))
|
ya.Style.InheritFrom(defaults).PersistToRenderer(r)
|
||||||
r.SetStrokeWidth(ya.Style.GetStrokeWidth(defaults.StrokeWidth))
|
|
||||||
r.SetFont(ya.Style.GetFont(defaults.GetFont()))
|
|
||||||
r.SetFontColor(ya.Style.GetFontColor(DefaultAxisColor))
|
|
||||||
r.SetFontSize(ya.Style.GetFontSize(defaults.GetFontSize()))
|
|
||||||
|
|
||||||
sort.Sort(Ticks(ticks))
|
sort.Sort(Ticks(ticks))
|
||||||
|
|
||||||
|
@ -122,11 +119,7 @@ func (ya YAxis) Measure(r Renderer, canvasBox Box, ra Range, defaults Style, tic
|
||||||
|
|
||||||
// Render renders the axis.
|
// Render renders the axis.
|
||||||
func (ya YAxis) Render(r Renderer, canvasBox Box, ra Range, defaults Style, ticks []Tick) {
|
func (ya YAxis) Render(r Renderer, canvasBox Box, ra Range, defaults Style, ticks []Tick) {
|
||||||
r.SetStrokeColor(ya.Style.GetStrokeColor(defaults.StrokeColor))
|
ya.Style.InheritFrom(defaults).PersistToRenderer(r)
|
||||||
r.SetStrokeWidth(ya.Style.GetStrokeWidth(defaults.StrokeWidth))
|
|
||||||
r.SetFont(ya.Style.GetFont(defaults.GetFont()))
|
|
||||||
r.SetFontColor(ya.Style.GetFontColor(DefaultAxisColor))
|
|
||||||
r.SetFontSize(ya.Style.GetFontSize(defaults.GetFontSize(DefaultFontSize)))
|
|
||||||
|
|
||||||
sort.Sort(Ticks(ticks))
|
sort.Sort(Ticks(ticks))
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ func TestYAxisGetTickCount(t *testing.T) {
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
|
|
||||||
ya := YAxis{}
|
ya := YAxis{}
|
||||||
yr := Range{Min: 10, Max: 100, Domain: 1024}
|
yr := &ContinuousRange{Min: 10, Max: 100, Domain: 1024}
|
||||||
styleDefaults := Style{
|
styleDefaults := Style{
|
||||||
Font: f,
|
Font: f,
|
||||||
FontSize: 10.0,
|
FontSize: 10.0,
|
||||||
|
@ -36,14 +36,14 @@ func TestYAxisGetTickStep(t *testing.T) {
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
|
|
||||||
ya := YAxis{}
|
ya := YAxis{}
|
||||||
yr := Range{Min: 10, Max: 100, Domain: 1024}
|
yr := &ContinuousRange{Min: 10, Max: 100, Domain: 1024}
|
||||||
styleDefaults := Style{
|
styleDefaults := Style{
|
||||||
Font: f,
|
Font: f,
|
||||||
FontSize: 10.0,
|
FontSize: 10.0,
|
||||||
}
|
}
|
||||||
vf := FloatValueFormatter
|
vf := FloatValueFormatter
|
||||||
step := ya.getTickStep(r, yr, styleDefaults, vf)
|
step := ya.getTickStep(r, yr, styleDefaults, vf)
|
||||||
assert.Equal(yr.Delta()/34.0, step)
|
assert.Equal(yr.GetDelta()/34.0, step)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestYAxisGetTicks(t *testing.T) {
|
func TestYAxisGetTicks(t *testing.T) {
|
||||||
|
@ -56,7 +56,7 @@ func TestYAxisGetTicks(t *testing.T) {
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
|
|
||||||
ya := YAxis{}
|
ya := YAxis{}
|
||||||
yr := Range{Min: 10, Max: 100, Domain: 1024}
|
yr := &ContinuousRange{Min: 10, Max: 100, Domain: 1024}
|
||||||
styleDefaults := Style{
|
styleDefaults := Style{
|
||||||
Font: f,
|
Font: f,
|
||||||
FontSize: 10.0,
|
FontSize: 10.0,
|
||||||
|
@ -78,7 +78,7 @@ func TestYAxisGetTicksWithUserDefaults(t *testing.T) {
|
||||||
ya := YAxis{
|
ya := YAxis{
|
||||||
Ticks: []Tick{{Value: 1.0, Label: "1.0"}},
|
Ticks: []Tick{{Value: 1.0, Label: "1.0"}},
|
||||||
}
|
}
|
||||||
yr := Range{Min: 10, Max: 100, Domain: 1024}
|
yr := &ContinuousRange{Min: 10, Max: 100, Domain: 1024}
|
||||||
styleDefaults := Style{
|
styleDefaults := Style{
|
||||||
Font: f,
|
Font: f,
|
||||||
FontSize: 10.0,
|
FontSize: 10.0,
|
||||||
|
|
Loading…
Reference in a new issue