descending

This commit is contained in:
Will Charczuk 2017-01-10 13:50:17 -08:00
parent 78cbfa62bc
commit 98d3996b47
6 changed files with 127 additions and 18 deletions

View file

@ -0,0 +1,55 @@
package main
import (
"net/http"
"github.com/wcharczuk/go-chart"
)
func drawChart(res http.ResponseWriter, req *http.Request) {
/*
The below will draw the same chart as the `basic` example, except with both the x and y axes turned on.
In this case, both the x and y axis ticks are generated automatically, the x and y ranges are established automatically, the canvas "box" is adjusted to fit the space the axes occupy so as not to clip.
*/
graph := chart.Chart{
Height: 500,
Width: 500,
XAxis: chart.XAxis{
Style: chart.Style{
Show: true,
},
/*Range: &chart.ContinuousRange{
Descending: true,
},*/
},
YAxis: chart.YAxis{
Style: chart.Style{
Show: true,
},
Range: &chart.ContinuousRange{
Descending: true,
},
},
Series: []chart.Series{
chart.ContinuousSeries{
Style: chart.Style{
Show: true,
StrokeColor: chart.GetDefaultColor(0).WithAlpha(64),
FillColor: chart.GetDefaultColor(0).WithAlpha(64),
},
XValues: []float64{1.0, 2.0, 3.0, 4.0, 5.0},
YValues: []float64{1.0, 2.0, 3.0, 4.0, 5.0},
},
},
}
res.Header().Set("Content-Type", "image/png")
graph.Render(chart.PNG, res)
}
func main() {
http.HandleFunc("/", drawChart)
http.ListenAndServe(":8080", nil)
}

View file

@ -10,6 +10,12 @@ type ContinuousRange struct {
Min float64 Min float64
Max float64 Max float64
Domain int Domain int
Descending bool
}
// IsDescending returns if the range is descending.
func (r ContinuousRange) IsDescending() bool {
return r.Descending
} }
// IsZero returns if the ContinuousRange has been set or not. // IsZero returns if the ContinuousRange has been set or not.
@ -56,12 +62,17 @@ func (r *ContinuousRange) SetDomain(domain int) {
// String returns a simple string for the ContinuousRange. // String returns a simple string for the ContinuousRange.
func (r ContinuousRange) String() string { func (r ContinuousRange) String() string {
return fmt.Sprintf("ContinuousRange [%.2f,%.2f] => %f", r.Min, r.Max, r.Domain) return fmt.Sprintf("ContinuousRange [%.2f,%.2f] => %d", r.Min, r.Max, r.Domain)
} }
// Translate maps a given value into the ContinuousRange space. // Translate maps a given value into the ContinuousRange space.
func (r ContinuousRange) Translate(value float64) int { func (r ContinuousRange) Translate(value float64) int {
normalized := value - r.Min normalized := value - r.Min
ratio := normalized / r.GetDelta() ratio := normalized / r.GetDelta()
if r.IsDescending() {
return r.Domain - int(math.Ceil(ratio*float64(r.Domain)))
}
return int(math.Ceil(ratio * float64(r.Domain))) return int(math.Ceil(ratio * float64(r.Domain)))
} }

View file

@ -18,9 +18,15 @@ type MarketHoursRange struct {
ValueFormatter ValueFormatter ValueFormatter ValueFormatter
Descending bool
Domain int Domain int
} }
// IsDescending returns if the range is descending.
func (mhr MarketHoursRange) IsDescending() bool {
return mhr.Descending
}
// GetTimezone returns the timezone for the market hours range. // GetTimezone returns the timezone for the market hours range.
func (mhr MarketHoursRange) GetTimezone() *time.Location { func (mhr MarketHoursRange) GetTimezone() *time.Location {
return mhr.GetMarketOpen().Location() return mhr.GetMarketOpen().Location()

View file

@ -36,6 +36,8 @@ type Range interface {
GetDomain() int GetDomain() int
SetDomain(domain int) SetDomain(domain int)
IsDescending() bool
// Translate the range to the domain. // Translate the range to the domain.
Translate(value float64) int Translate(value float64) int
} }

36
tick.go
View file

@ -1,6 +1,10 @@
package chart package chart
import "math" import (
"fmt"
"math"
"strings"
)
// TicksProvider is a type that provides ticks. // TicksProvider is a type that provides ticks.
type TicksProvider interface { type TicksProvider interface {
@ -31,6 +35,15 @@ func (t Ticks) Less(i, j int) bool {
return t[i].Value < t[j].Value return t[i].Value < t[j].Value
} }
// String returns a string representation of the set of ticks.
func (t Ticks) String() string {
var values []string
for i, tick := range t {
values = append(values, fmt.Sprintf("[%d: %s]", i, tick.Label))
}
return strings.Join(values, ", ")
}
// GenerateContinuousTicks generates a set of ticks. // GenerateContinuousTicks generates a set of ticks.
func GenerateContinuousTicks(r Renderer, ra Range, isVertical bool, style Style, vf ValueFormatter) []Tick { func GenerateContinuousTicks(r Renderer, ra Range, isVertical bool, style Style, vf ValueFormatter) []Tick {
if vf == nil { if vf == nil {
@ -40,10 +53,17 @@ func GenerateContinuousTicks(r Renderer, ra Range, isVertical bool, style Style,
var ticks []Tick var ticks []Tick
min, max := ra.GetMin(), ra.GetMax() min, max := ra.GetMin(), ra.GetMax()
if ra.IsDescending() {
ticks = append(ticks, Tick{
Value: max,
Label: vf(max),
})
} else {
ticks = append(ticks, Tick{ ticks = append(ticks, Tick{
Value: min, Value: min,
Label: vf(min), Label: vf(min),
}) })
}
minLabel := vf(min) minLabel := vf(min)
style.GetTextOptions().WriteToRenderer(r) style.GetTextOptions().WriteToRenderer(r)
@ -67,17 +87,29 @@ func GenerateContinuousTicks(r Renderer, ra Range, isVertical bool, style Style,
intermediateTickCount = Math.MinInt(intermediateTickCount, 1<<10) intermediateTickCount = Math.MinInt(intermediateTickCount, 1<<10)
for x := 1; x < intermediateTickCount; x++ { for x := 1; x < intermediateTickCount; x++ {
tickValue := min + Math.RoundUp(tickStep*float64(x), roundTo) var tickValue float64
if ra.IsDescending() {
tickValue = max - Math.RoundUp(tickStep*float64(x), roundTo)
} else {
tickValue = min + Math.RoundUp(tickStep*float64(x), roundTo)
}
ticks = append(ticks, Tick{ ticks = append(ticks, Tick{
Value: tickValue, Value: tickValue,
Label: vf(tickValue), Label: vf(tickValue),
}) })
} }
if ra.IsDescending() {
ticks = append(ticks, Tick{
Value: min,
Label: vf(min),
})
} else {
ticks = append(ticks, Tick{ ticks = append(ticks, Tick{
Value: max, Value: max,
Label: vf(max), Label: vf(max),
}) })
}
return ticks return ticks
} }

View file

@ -42,9 +42,10 @@ func TestGenerateContinuousTicksDescending(t *testing.T) {
r.SetFont(f) r.SetFont(f)
ra := &ContinuousRange{ ra := &ContinuousRange{
Min: 10.0, Min: 0.0,
Max: 0.0, Max: 10.0,
Domain: 256, Domain: 256,
Descending: true,
} }
vf := FloatValueFormatter vf := FloatValueFormatter
@ -53,5 +54,7 @@ func TestGenerateContinuousTicksDescending(t *testing.T) {
assert.NotEmpty(ticks) assert.NotEmpty(ticks)
assert.Len(ticks, 11) assert.Len(ticks, 11)
assert.Equal(10.0, ticks[0].Value) assert.Equal(10.0, ticks[0].Value)
assert.Equal(9.0, ticks[1].Value)
assert.Equal(1.0, ticks[len(ticks)-2].Value)
assert.Equal(0.0, ticks[len(ticks)-1].Value) assert.Equal(0.0, ticks[len(ticks)-1].Value)
} }