-
Notifications
You must be signed in to change notification settings - Fork 2
/
tick.go
140 lines (116 loc) · 3.04 KB
/
tick.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package plot
import (
"fmt"
"math"
)
// Ticks represents an approach to calculating tick position and label.
type Ticks interface {
Ticks(axis *Axis) []Tick
}
// Tick represents a division on plot.
type Tick struct {
Minor bool
Label string
Value float64
}
// AutomaticTicks tries to automatically figure out which ticks to use.
type AutomaticTicks struct{}
// logarithmicTicks calculates ticks for logarithmic axis.
func (at AutomaticTicks) logarithmicTicks(axis *Axis, transform *Log1pTransform) []Tick {
//TODO: fix, we don't properly assign labels for logarithmic axis
ticks := make([]Tick, 0)
low, high := axis.Min, axis.Max
if low > high {
low, high = high, low
}
previous := math.NaN()
inRange := func(value float64) bool {
return low < value && value < high
}
if inRange(0) {
ticks = append(ticks, Tick{Value: 0, Label: "0"})
previous = 0
}
for power := 0; power < 10; power++ {
value := math.Pow(transform.base, float64(power))
if inRange(value) {
ticks = append(ticks, Tick{
Value: value,
Label: fmt.Sprintf("%.0f", value),
})
}
if inRange(-value) {
ticks = append(ticks, Tick{
Value: -value,
Label: fmt.Sprintf("%.0f", -value),
})
}
if !math.IsNaN(previous) && axis.MinorTicks > 0 {
minorSpacing := (value - previous) / float64(axis.MinorTicks)
minor := previous
for i := 0; i < axis.MinorTicks; i++ {
if inRange(minor) {
ticks = append(ticks, Tick{Minor: true, Value: minor})
}
if inRange(-minor) {
ticks = append(ticks, Tick{Minor: true, Value: -minor})
}
minor += minorSpacing
}
}
previous = value
}
return ticks
}
// linearTicks calculates ticks on linear axis.
func (AutomaticTicks) linearTicks(axis *Axis) []Tick {
majorSpacing := (axis.Max - axis.Min) / float64(axis.MajorTicks)
minorSpacing := majorSpacing / float64(axis.MinorTicks)
frac := -int(math.Floor(math.Log10(majorSpacing)))
if frac < 0 {
frac = 0
}
ticks := make([]Tick, 0, axis.MajorTicks*axis.MinorTicks)
major := axis.Min
hasZero := false
for i := 0; i < axis.MajorTicks; i++ {
if major == 0 {
hasZero = true
}
ticks = append(ticks, Tick{
Value: major,
Label: fmt.Sprintf("%.[2]*[1]f", major, frac),
})
minor := major
for k := 0; k < axis.MinorTicks; k++ {
if minor == 0 {
minor += minorSpacing
continue
}
ticks = append(ticks, Tick{
Minor: true,
Value: minor,
})
minor += minorSpacing
}
major += majorSpacing
}
if !hasZero && (axis.Min <= 0) == (0 <= axis.Max) {
ticks = append(ticks, Tick{
Value: 0,
Label: "0",
})
}
return ticks
}
// Ticks automatically calculates appropriate ticks for an axis.
func (ticks AutomaticTicks) Ticks(axis *Axis) []Tick {
// if transform, ok := axis.Transform.(*Log1pTransform); ok {
// return ticks.logarithmicTicks(axis, transform)
// }
return ticks.linearTicks(axis)
}
// ManualTicks allows to manually place and label ticks.
type ManualTicks []Tick
// Ticks calculates ticks for specified axis.
func (ticks ManualTicks) Ticks(axis *Axis) []Tick { return []Tick(ticks) }