Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add IntTicks #360

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions axis.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package plot

import (
"fmt"
"image/color"
"math"
"strconv"
Expand Down Expand Up @@ -504,6 +505,98 @@ func (t TimeTicks) Ticks(min, max float64) []Tick {
return ticks
}

// IntTicks is suitable for axes representing integer.
type IntTicks struct {
// AddMinTick adds tick to the min value when it is a true.
AddMinTick bool

// AddMaxTick adds tick to the max value when it is a true.
AddMaxTick bool
}

// Ticks returns Ticks in a specified range
func (it IntTicks) Ticks(min, max float64) (ticks []Tick) {
const SuggestedTicks = 3
if max < min {
panic("illegal range")
}
tens := math.Pow10(int(math.Floor(math.Log10(max - min))))
n := (max - min) / tens
for n < SuggestedTicks {
tens /= 10
n = (max - min) / tens
}

majorMult := int(n / SuggestedTicks)
switch majorMult {
case 7:
majorMult = 6
case 9:
majorMult = 8
}
majorDelta := float64(majorMult) * tens
val := math.Floor(min/majorDelta) * majorDelta

// Add tick to min value
if it.AddMinTick {
if val != min {
ticks = append(ticks, Tick{Value: min, Label: fmt.Sprintf("%.f", min)})
}
}

for val <= max {
if val >= min && val <= max {
ticks = append(ticks, Tick{Value: val, Label: fmt.Sprintf("%.f", val)})
}
if math.Nextafter(val, val+majorDelta) == val {
break
}
val += majorDelta
}

minorDelta := majorDelta / 2
switch majorMult {
case 3, 6:
minorDelta = majorDelta / 3
case 5:
minorDelta = majorDelta / 5
}

val = math.Floor(min/minorDelta) * minorDelta
for val <= max {
found := false
for _, t := range ticks {
if t.Value == val {
found = true
}
}
if val >= min && val <= max && !found {
ticks = append(ticks, Tick{Value: val})
}
if math.Nextafter(val, val+minorDelta) == val {
break
}
val += minorDelta
}
// Add tick to max value
if it.AddMaxTick {
found := false
for _, t := range ticks {
if t.Value == max {
if t.Label == "" {
t.Label = fmt.Sprintf("%.f", t.Value)
}
found = true
break
}
}
if found == false {
ticks = append(ticks, Tick{Value: max, Label: fmt.Sprintf("%.f", max)})
}
}
return
}

// A Tick is a single tick mark on an axis.
type Tick struct {
// Value is the data value marked by this Tick.
Expand Down
40 changes: 40 additions & 0 deletions axis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,46 @@ func TestAxisSmallTick(t *testing.T) {
}
}

func TestIntTick(t *testing.T) {
i := IntTicks{}
for _, test := range []struct {
addMinTick bool
addMaxTick bool
min, max float64
want []string
}{
{
addMinTick: false,
addMaxTick: false,
min: 1.0,
max: 99999.0,
want: []string{"30000", "60000", "90000"},
},
{
addMinTick: true,
addMaxTick: false,
min: 1.0,
max: 99999.0,
want: []string{"1", "30000", "60000", "90000"},
},
{
addMinTick: true,
addMaxTick: true,
min: 1.0,
max: 99999.0,
want: []string{"1", "30000", "60000", "90000", "99999"},
},
} {
i.AddMinTick = test.addMinTick
i.AddMaxTick = test.addMaxTick
ticks := i.Ticks(test.min, test.max)
got := labelsOf(ticks)
if !reflect.DeepEqual(got, test.want) {
t.Errorf("tick labels mismatch:\ngot: %q\nwant:%q", got, test.want)
}
}
}

func labelsOf(ticks []Tick) []string {
var labels []string
for _, t := range ticks {
Expand Down