descending
This commit is contained in:
parent
78cbfa62bc
commit
98d3996b47
6 changed files with 127 additions and 18 deletions
55
_examples/descending/main.go
Normal file
55
_examples/descending/main.go
Normal 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)
|
||||||
|
}
|
|
@ -7,9 +7,15 @@ import (
|
||||||
|
|
||||||
// ContinuousRange represents a boundary for a set of numbers.
|
// ContinuousRange represents a boundary for a set of numbers.
|
||||||
type ContinuousRange struct {
|
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)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,13 @@ type MarketHoursRange struct {
|
||||||
|
|
||||||
ValueFormatter ValueFormatter
|
ValueFormatter ValueFormatter
|
||||||
|
|
||||||
Domain int
|
Descending bool
|
||||||
|
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.
|
||||||
|
|
2
range.go
2
range.go
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
52
tick.go
52
tick.go
|
@ -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()
|
||||||
|
|
||||||
ticks = append(ticks, Tick{
|
if ra.IsDescending() {
|
||||||
Value: min,
|
ticks = append(ticks, Tick{
|
||||||
Label: vf(min),
|
Value: max,
|
||||||
})
|
Label: vf(max),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
ticks = append(ticks, Tick{
|
||||||
|
Value: 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),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
ticks = append(ticks, Tick{
|
if ra.IsDescending() {
|
||||||
Value: max,
|
ticks = append(ticks, Tick{
|
||||||
Label: vf(max),
|
Value: min,
|
||||||
})
|
Label: vf(min),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
ticks = append(ticks, Tick{
|
||||||
|
Value: max,
|
||||||
|
Label: vf(max),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return ticks
|
return ticks
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue